X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/c9310e10a4872ad1bb8ccb504f54fdfc3bfd50c9..212fe75b6fd26d01dfacf31650e7f71eb7f06a63:/vendor/assets/iD/iD.js?ds=inline diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index 337d13fae..0c6f719a4 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -1,118900 +1,85885 @@ -(function () { - var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - function createCommonjsModule(fn, basedir, module) { - return module = { - path: basedir, - exports: {}, - require: function (path, base) { - return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); - } - }, fn(module, module.exports), module.exports; - } - - function getCjsExportFromNamespace (n) { - return n && n['default'] || n; - } - - function commonjsRequire () { - throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); - } - - var isImplemented = function () { - var set, iterator, result; - if (typeof Set !== 'function') return false; - set = new Set(['raz', 'dwa', 'trzy']); - if (String(set) !== '[object Set]') return false; - if (set.size !== 3) return false; - if (typeof set.add !== 'function') return false; - if (typeof set.clear !== 'function') return false; - if (typeof set.delete !== 'function') return false; - if (typeof set.entries !== 'function') return false; - if (typeof set.forEach !== 'function') return false; - if (typeof set.has !== 'function') return false; - if (typeof set.keys !== 'function') return false; - if (typeof set.values !== 'function') return false; - - iterator = set.values(); - result = iterator.next(); - if (result.done !== false) return false; - if (result.value !== 'raz') return false; - - return true; - }; - - // eslint-disable-next-line no-empty-function - var noop = function () {}; - - var _undefined = noop(); // Support ES3 engines - - var isValue = function (val) { return val !== _undefined && val !== null; }; - - var validValue = function (value) { - if (!isValue(value)) throw new TypeError("Cannot use null or undefined"); - return value; - }; - - var clear = function () { - validValue(this).length = 0; - return this; - }; - - var isImplemented$1 = function () { - var numberIsNaN = Number.isNaN; - if (typeof numberIsNaN !== "function") return false; - return !numberIsNaN({}) && numberIsNaN(NaN) && !numberIsNaN(34); - }; - - var shim = function (value) { - // eslint-disable-next-line no-self-compare - return value !== value; - }; - - var isNan = isImplemented$1() ? Number.isNaN : shim; - - var isImplemented$2 = function () { - var sign = Math.sign; - if (typeof sign !== "function") return false; - return sign(10) === 1 && sign(-20) === -1; - }; - - var shim$1 = function (value) { - value = Number(value); - if (isNaN(value) || value === 0) return value; - return value > 0 ? 1 : -1; - }; - - var sign = isImplemented$2() ? Math.sign : shim$1; - - var abs = Math.abs - , floor = Math.floor; - - var toInteger = function (value) { - if (isNaN(value)) return 0; - value = Number(value); - if (value === 0 || !isFinite(value)) return value; - return sign(value) * floor(abs(value)); - }; - - var max = Math.max; - - var toPosInteger = function (value) { return max(0, toInteger(value)); }; - - var indexOf = Array.prototype.indexOf - , objHasOwnProperty = Object.prototype.hasOwnProperty - , abs$1 = Math.abs - , floor$1 = Math.floor; - - var eIndexOf = function (searchElement/*, fromIndex*/) { - var i, length, fromIndex, val; - if (!isNan(searchElement)) return indexOf.apply(this, arguments); - - length = toPosInteger(validValue(this).length); - fromIndex = arguments[1]; - if (isNaN(fromIndex)) fromIndex = 0; - else if (fromIndex >= 0) fromIndex = floor$1(fromIndex); - else fromIndex = toPosInteger(this.length) - floor$1(abs$1(fromIndex)); - - for (i = fromIndex; i < length; ++i) { - if (objHasOwnProperty.call(this, i)) { - val = this[i]; - if (isNan(val)) return i; // Jslint: ignore - } - } - return -1; - }; - - var create = Object.create, getPrototypeOf = Object.getPrototypeOf, plainObject = {}; - - var isImplemented$3 = function (/* CustomCreate*/) { - var setPrototypeOf = Object.setPrototypeOf, customCreate = arguments[0] || create; - if (typeof setPrototypeOf !== "function") return false; - return getPrototypeOf(setPrototypeOf(customCreate(null), plainObject)) === plainObject; - }; - - var map = { function: true, object: true }; - - var isObject = function (value) { return (isValue(value) && map[typeof value]) || false; }; - - var create$1 = Object.create, shim$2; - - if (!isImplemented$3()) { - shim$2 = shim$3; - } - - var create_1 = (function () { - var nullObject, polyProps, desc; - if (!shim$2) return create$1; - if (shim$2.level !== 1) return create$1; - - nullObject = {}; - polyProps = {}; - desc = { configurable: false, enumerable: false, writable: true, value: undefined }; - Object.getOwnPropertyNames(Object.prototype).forEach(function (name) { - if (name === "__proto__") { - polyProps[name] = { - configurable: true, - enumerable: false, - writable: true, - value: undefined - }; - return; - } - polyProps[name] = desc; - }); - Object.defineProperties(nullObject, polyProps); - - Object.defineProperty(shim$2, "nullPolyfill", { - configurable: false, - enumerable: false, - writable: false, - value: nullObject - }); - - return function (prototype, props) { - return create$1(prototype === null ? nullObject : prototype, props); - }; - })(); - - var objIsPrototypeOf = Object.prototype.isPrototypeOf - , defineProperty = Object.defineProperty - , nullDesc = { configurable: true, enumerable: false, writable: true, value: undefined } - , validate; - - validate = function (obj, prototype) { - validValue(obj); - if (prototype === null || isObject(prototype)) return obj; - throw new TypeError("Prototype must be null or an object"); - }; - - var shim$3 = (function (status) { - var fn, set; - if (!status) return null; - if (status.level === 2) { - if (status.set) { - set = status.set; - fn = function (obj, prototype) { - set.call(validate(obj, prototype), prototype); - return obj; - }; - } else { - fn = function (obj, prototype) { - validate(obj, prototype).__proto__ = prototype; - return obj; - }; - } - } else { - fn = function self(obj, prototype) { - var isNullBase; - validate(obj, prototype); - isNullBase = objIsPrototypeOf.call(self.nullPolyfill, obj); - if (isNullBase) delete self.nullPolyfill.__proto__; - if (prototype === null) prototype = self.nullPolyfill; - obj.__proto__ = prototype; - if (isNullBase) defineProperty(self.nullPolyfill, "__proto__", nullDesc); - return obj; - }; - } - return Object.defineProperty(fn, "level", { - configurable: false, - enumerable: false, - writable: false, - value: status.level - }); - })( - (function () { - var tmpObj1 = Object.create(null) - , tmpObj2 = {} - , set - , desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__"); - - if (desc) { - try { - set = desc.set; // Opera crashes at this point - set.call(tmpObj1, tmpObj2); - } catch (ignore) {} - if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { set: set, level: 2 }; - } - - tmpObj1.__proto__ = tmpObj2; - if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 2 }; - - tmpObj1 = {}; - tmpObj1.__proto__ = tmpObj2; - if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 1 }; - - return false; - })() - ); - - var setPrototypeOf = isImplemented$3() ? Object.setPrototypeOf : shim$3; - - var validCallable = function (fn) { - if (typeof fn !== "function") throw new TypeError(fn + " is not a function"); - return fn; - }; - - // ES3 safe - var _undefined$1 = void 0; - - var is = function (value) { return value !== _undefined$1 && value !== null; }; - - // prettier-ignore - var possibleTypes = { "object": true, "function": true, "undefined": true /* document.all */ }; - - var is$1 = function (value) { - if (!is(value)) return false; - return hasOwnProperty.call(possibleTypes, typeof value); - }; - - var is$2 = function (value) { - if (!is$1(value)) return false; - try { - if (!value.constructor) return false; - return value.constructor.prototype === value; - } catch (error) { - return false; - } - }; - - var is$3 = function (value) { - if (typeof value !== "function") return false; - - if (!hasOwnProperty.call(value, "length")) return false; - - try { - if (typeof value.length !== "number") return false; - if (typeof value.call !== "function") return false; - if (typeof value.apply !== "function") return false; - } catch (error) { - return false; - } - - return !is$2(value); - }; - - var classRe = /^\s*class[\s{/}]/, functionToString = Function.prototype.toString; - - var is$4 = function (value) { - if (!is$3(value)) return false; - if (classRe.test(functionToString.call(value))) return false; - return true; - }; - - var isImplemented$4 = function () { - var assign = Object.assign, obj; - if (typeof assign !== "function") return false; - obj = { foo: "raz" }; - assign(obj, { bar: "dwa" }, { trzy: "trzy" }); - return obj.foo + obj.bar + obj.trzy === "razdwatrzy"; - }; - - var isImplemented$5 = function () { - try { - Object.keys("primitive"); - return true; - } catch (e) { - return false; - } - }; - - var keys = Object.keys; - - var shim$4 = function (object) { return keys(isValue(object) ? Object(object) : object); }; - - var keys$1 = isImplemented$5() ? Object.keys : shim$4; - - var max$1 = Math.max; - - var shim$5 = function (dest, src/*, …srcn*/) { - var error, i, length = max$1(arguments.length, 2), assign; - dest = Object(validValue(dest)); - assign = function (key) { - try { - dest[key] = src[key]; - } catch (e) { - if (!error) error = e; - } - }; - for (i = 1; i < length; ++i) { - src = arguments[i]; - keys$1(src).forEach(assign); - } - if (error !== undefined) throw error; - return dest; - }; - - var assign = isImplemented$4() ? Object.assign : shim$5; - - var forEach = Array.prototype.forEach, create$2 = Object.create; - - var process$1 = function (src, obj) { - var key; - for (key in src) obj[key] = src[key]; - }; - - // eslint-disable-next-line no-unused-vars - var normalizeOptions = function (opts1/*, …options*/) { - var result = create$2(null); - forEach.call(arguments, function (options) { - if (!isValue(options)) return; - process$1(Object(options), result); - }); - return result; - }; - - var str = "razdwatrzy"; - - var isImplemented$6 = function () { - if (typeof str.contains !== "function") return false; - return str.contains("dwa") === true && str.contains("foo") === false; - }; - - var indexOf$1 = String.prototype.indexOf; - - var shim$6 = function (searchString/*, position*/) { - return indexOf$1.call(this, searchString, arguments[1]) > -1; - }; - - var contains = isImplemented$6() ? String.prototype.contains : shim$6; - - var d_1 = createCommonjsModule(function (module) { - - - - var d = (module.exports = function (dscr, value/*, options*/) { - var c, e, w, options, desc; - if (arguments.length < 2 || typeof dscr !== "string") { - options = value; - value = dscr; - dscr = null; - } else { - options = arguments[2]; - } - if (is(dscr)) { - c = contains.call(dscr, "c"); - e = contains.call(dscr, "e"); - w = contains.call(dscr, "w"); - } else { - c = w = true; - e = false; - } - - desc = { value: value, configurable: c, enumerable: e, writable: w }; - return !options ? desc : assign(normalizeOptions(options), desc); - }); - - d.gs = function (dscr, get, set/*, options*/) { - var c, e, options, desc; - if (typeof dscr !== "string") { - options = set; - set = get; - get = dscr; - dscr = null; - } else { - options = arguments[3]; - } - if (!is(get)) { - get = undefined; - } else if (!is$4(get)) { - options = get; - get = set = undefined; - } else if (!is(set)) { - set = undefined; - } else if (!is$4(set)) { - options = set; - set = undefined; - } - if (is(dscr)) { - c = contains.call(dscr, "c"); - e = contains.call(dscr, "e"); - } else { - c = true; - e = false; - } - - desc = { get: get, set: set, configurable: c, enumerable: e }; - return !options ? desc : assign(normalizeOptions(options), desc); - }; - }); - - var eventEmitter = createCommonjsModule(function (module, exports) { - - var apply = Function.prototype.apply, call = Function.prototype.call - , create = Object.create, defineProperty = Object.defineProperty - , defineProperties = Object.defineProperties - , hasOwnProperty = Object.prototype.hasOwnProperty - , descriptor = { configurable: true, enumerable: false, writable: true } - - , on, once, off, emit, methods, descriptors, base; - - on = function (type, listener) { - var data; - - validCallable(listener); - - if (!hasOwnProperty.call(this, '__ee__')) { - data = descriptor.value = create(null); - defineProperty(this, '__ee__', descriptor); - descriptor.value = null; - } else { - data = this.__ee__; - } - if (!data[type]) data[type] = listener; - else if (typeof data[type] === 'object') data[type].push(listener); - else data[type] = [data[type], listener]; - - return this; - }; - - once = function (type, listener) { - var once, self; - - validCallable(listener); - self = this; - on.call(this, type, once = function () { - off.call(self, type, once); - apply.call(listener, this, arguments); - }); - - once.__eeOnceListener__ = listener; - return this; - }; - - off = function (type, listener) { - var data, listeners, candidate, i; - - validCallable(listener); - - if (!hasOwnProperty.call(this, '__ee__')) return this; - data = this.__ee__; - if (!data[type]) return this; - listeners = data[type]; - - if (typeof listeners === 'object') { - for (i = 0; (candidate = listeners[i]); ++i) { - if ((candidate === listener) || - (candidate.__eeOnceListener__ === listener)) { - if (listeners.length === 2) data[type] = listeners[i ? 0 : 1]; - else listeners.splice(i, 1); - } - } - } else { - if ((listeners === listener) || - (listeners.__eeOnceListener__ === listener)) { - delete data[type]; - } - } - - return this; - }; - - emit = function (type) { - var i, l, listener, listeners, args; - - if (!hasOwnProperty.call(this, '__ee__')) return; - listeners = this.__ee__[type]; - if (!listeners) return; - - if (typeof listeners === 'object') { - l = arguments.length; - args = new Array(l - 1); - for (i = 1; i < l; ++i) args[i - 1] = arguments[i]; - - listeners = listeners.slice(); - for (i = 0; (listener = listeners[i]); ++i) { - apply.call(listener, this, args); - } - } else { - switch (arguments.length) { - case 1: - call.call(listeners, this); - break; - case 2: - call.call(listeners, this, arguments[1]); - break; - case 3: - call.call(listeners, this, arguments[1], arguments[2]); - break; - default: - l = arguments.length; - args = new Array(l - 1); - for (i = 1; i < l; ++i) { - args[i - 1] = arguments[i]; - } - apply.call(listeners, this, args); - } - } - }; - - methods = { - on: on, - once: once, - off: off, - emit: emit - }; - - descriptors = { - on: d_1(on), - once: d_1(once), - off: d_1(off), - emit: d_1(emit) - }; - - base = defineProperties({}, descriptors); - - module.exports = exports = function (o) { - return (o == null) ? create(base) : defineProperties(Object(o), descriptors); - }; - exports.methods = methods; - }); - - var validTypes = { object: true, symbol: true }; - - var isImplemented$7 = function () { - var symbol; - if (typeof Symbol !== 'function') return false; - symbol = Symbol('test symbol'); - try { String(symbol); } catch (e) { return false; } - - // Return 'true' also for polyfills - if (!validTypes[typeof Symbol.iterator]) return false; - if (!validTypes[typeof Symbol.toPrimitive]) return false; - if (!validTypes[typeof Symbol.toStringTag]) return false; - - return true; - }; - - var isSymbol = function (x) { - if (!x) return false; - if (typeof x === 'symbol') return true; - if (!x.constructor) return false; - if (x.constructor.name !== 'Symbol') return false; - return (x[x.constructor.toStringTag] === 'Symbol'); - }; - - var validateSymbol = function (value) { - if (!isSymbol(value)) throw new TypeError(value + " is not a symbol"); - return value; - }; - - var create$3 = Object.create, defineProperties = Object.defineProperties - , defineProperty$1 = Object.defineProperty, objPrototype = Object.prototype - , NativeSymbol, SymbolPolyfill, HiddenSymbol, globalSymbols = create$3(null) - , isNativeSafe; - - if (typeof Symbol === 'function') { - NativeSymbol = Symbol; - try { - String(NativeSymbol()); - isNativeSafe = true; - } catch (ignore) {} - } - - var generateName = (function () { - var created = create$3(null); - return function (desc) { - var postfix = 0, name, ie11BugWorkaround; - while (created[desc + (postfix || '')]) ++postfix; - desc += (postfix || ''); - created[desc] = true; - name = '@@' + desc; - defineProperty$1(objPrototype, name, d_1.gs(null, function (value) { - // For IE11 issue see: - // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/ - // ie11-broken-getters-on-dom-objects - // https://github.com/medikoo/es6-symbol/issues/12 - if (ie11BugWorkaround) return; - ie11BugWorkaround = true; - defineProperty$1(this, name, d_1(value)); - ie11BugWorkaround = false; - })); - return name; - }; - }()); - - // Internal constructor (not one exposed) for creating Symbol instances. - // This one is used to ensure that `someSymbol instanceof Symbol` always return false - HiddenSymbol = function Symbol(description) { - if (this instanceof HiddenSymbol) throw new TypeError('Symbol is not a constructor'); - return SymbolPolyfill(description); - }; - - // Exposed `Symbol` constructor - // (returns instances of HiddenSymbol) - var polyfill = SymbolPolyfill = function Symbol(description) { - var symbol; - if (this instanceof Symbol) throw new TypeError('Symbol is not a constructor'); - if (isNativeSafe) return NativeSymbol(description); - symbol = create$3(HiddenSymbol.prototype); - description = (description === undefined ? '' : String(description)); - return defineProperties(symbol, { - __description__: d_1('', description), - __name__: d_1('', generateName(description)) - }); - }; - defineProperties(SymbolPolyfill, { - for: d_1(function (key) { - if (globalSymbols[key]) return globalSymbols[key]; - return (globalSymbols[key] = SymbolPolyfill(String(key))); - }), - keyFor: d_1(function (s) { - var key; - validateSymbol(s); - for (key in globalSymbols) if (globalSymbols[key] === s) return key; - }), - - // To ensure proper interoperability with other native functions (e.g. Array.from) - // fallback to eventual native implementation of given symbol - hasInstance: d_1('', (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill('hasInstance')), - isConcatSpreadable: d_1('', (NativeSymbol && NativeSymbol.isConcatSpreadable) || - SymbolPolyfill('isConcatSpreadable')), - iterator: d_1('', (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill('iterator')), - match: d_1('', (NativeSymbol && NativeSymbol.match) || SymbolPolyfill('match')), - replace: d_1('', (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill('replace')), - search: d_1('', (NativeSymbol && NativeSymbol.search) || SymbolPolyfill('search')), - species: d_1('', (NativeSymbol && NativeSymbol.species) || SymbolPolyfill('species')), - split: d_1('', (NativeSymbol && NativeSymbol.split) || SymbolPolyfill('split')), - toPrimitive: d_1('', (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill('toPrimitive')), - toStringTag: d_1('', (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill('toStringTag')), - unscopables: d_1('', (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill('unscopables')) - }); - - // Internal tweaks for real symbol producer - defineProperties(HiddenSymbol.prototype, { - constructor: d_1(SymbolPolyfill), - toString: d_1('', function () { return this.__name__; }) - }); - - // Proper implementation of methods exposed on Symbol.prototype - // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype - defineProperties(SymbolPolyfill.prototype, { - toString: d_1(function () { return 'Symbol (' + validateSymbol(this).__description__ + ')'; }), - valueOf: d_1(function () { return validateSymbol(this); }) - }); - defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toPrimitive, d_1('', function () { - var symbol = validateSymbol(this); - if (typeof symbol === 'symbol') return symbol; - return symbol.toString(); - })); - defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toStringTag, d_1('c', 'Symbol')); - - // Proper implementaton of toPrimitive and toStringTag for returned symbol instances - defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toStringTag, - d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toStringTag])); - - // Note: It's important to define `toPrimitive` as last one, as some implementations - // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols) - // And that may invoke error in definition flow: - // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149 - defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toPrimitive, - d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toPrimitive])); - - var es6Symbol = isImplemented$7() ? Symbol : polyfill; - - var objToString = Object.prototype.toString - , id = objToString.call((function () { return arguments; })()); - - var isArguments = function (value) { return objToString.call(value) === id; }; - - var objToString$1 = Object.prototype.toString, id$1 = objToString$1.call(""); - - var isString = function (value) { - return ( - typeof value === "string" || - (value && - typeof value === "object" && - (value instanceof String || objToString$1.call(value) === id$1)) || - false - ); - }; - - var isImplemented$8 = function () { - if (typeof globalThis !== "object") return false; - if (!globalThis) return false; - return globalThis.Array === Array; - }; - - var naiveFallback = function () { - if (typeof self === "object" && self) return self; - if (typeof window === "object" && window) return window; - throw new Error("Unable to resolve global `this`"); - }; - - var implementation = (function () { - if (this) return this; - - // Unexpected strict mode (may happen if e.g. bundled into ESM module) - - // Thanks @mathiasbynens -> https://mathiasbynens.be/notes/globalthis - // In all ES5+ engines global object inherits from Object.prototype - // (if you approached one that doesn't please report) - try { - Object.defineProperty(Object.prototype, "__global__", { - get: function () { return this; }, - configurable: true - }); - } catch (error) { - // Unfortunate case of Object.prototype being sealed (via preventExtensions, seal or freeze) - return naiveFallback(); - } - try { - // Safari case (window.__global__ is resolved with global context, but __global__ does not) - if (!__global__) return naiveFallback(); - return __global__; - } finally { - delete Object.prototype.__global__; - } - })(); - - var globalThis_1 = isImplemented$8() ? globalThis : implementation; - - var validTypes$1 = { object: true, symbol: true }; - - var isImplemented$9 = function () { - var Symbol = globalThis_1.Symbol; - var symbol; - if (typeof Symbol !== "function") return false; - symbol = Symbol("test symbol"); - try { String(symbol); } - catch (e) { return false; } - - // Return 'true' also for polyfills - if (!validTypes$1[typeof Symbol.iterator]) return false; - if (!validTypes$1[typeof Symbol.toPrimitive]) return false; - if (!validTypes$1[typeof Symbol.toStringTag]) return false; - - return true; - }; - - var isSymbol$1 = function (value) { - if (!value) return false; - if (typeof value === "symbol") return true; - if (!value.constructor) return false; - if (value.constructor.name !== "Symbol") return false; - return value[value.constructor.toStringTag] === "Symbol"; - }; - - var validateSymbol$1 = function (value) { - if (!isSymbol$1(value)) throw new TypeError(value + " is not a symbol"); - return value; - }; - - var create$4 = Object.create, defineProperty$2 = Object.defineProperty, objPrototype$1 = Object.prototype; - - var created = create$4(null); - var generateName$1 = function (desc) { - var postfix = 0, name, ie11BugWorkaround; - while (created[desc + (postfix || "")]) ++postfix; - desc += postfix || ""; - created[desc] = true; - name = "@@" + desc; - defineProperty$2( - objPrototype$1, - name, - d_1.gs(null, function (value) { - // For IE11 issue see: - // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/ - // ie11-broken-getters-on-dom-objects - // https://github.com/medikoo/es6-symbol/issues/12 - if (ie11BugWorkaround) return; - ie11BugWorkaround = true; - defineProperty$2(this, name, d_1(value)); - ie11BugWorkaround = false; - }) - ); - return name; - }; - - var NativeSymbol$1 = globalThis_1.Symbol; - - var standardSymbols = function (SymbolPolyfill) { - return Object.defineProperties(SymbolPolyfill, { - // To ensure proper interoperability with other native functions (e.g. Array.from) - // fallback to eventual native implementation of given symbol - hasInstance: d_1( - "", (NativeSymbol$1 && NativeSymbol$1.hasInstance) || SymbolPolyfill("hasInstance") - ), - isConcatSpreadable: d_1( - "", - (NativeSymbol$1 && NativeSymbol$1.isConcatSpreadable) || - SymbolPolyfill("isConcatSpreadable") - ), - iterator: d_1("", (NativeSymbol$1 && NativeSymbol$1.iterator) || SymbolPolyfill("iterator")), - match: d_1("", (NativeSymbol$1 && NativeSymbol$1.match) || SymbolPolyfill("match")), - replace: d_1("", (NativeSymbol$1 && NativeSymbol$1.replace) || SymbolPolyfill("replace")), - search: d_1("", (NativeSymbol$1 && NativeSymbol$1.search) || SymbolPolyfill("search")), - species: d_1("", (NativeSymbol$1 && NativeSymbol$1.species) || SymbolPolyfill("species")), - split: d_1("", (NativeSymbol$1 && NativeSymbol$1.split) || SymbolPolyfill("split")), - toPrimitive: d_1( - "", (NativeSymbol$1 && NativeSymbol$1.toPrimitive) || SymbolPolyfill("toPrimitive") - ), - toStringTag: d_1( - "", (NativeSymbol$1 && NativeSymbol$1.toStringTag) || SymbolPolyfill("toStringTag") - ), - unscopables: d_1( - "", (NativeSymbol$1 && NativeSymbol$1.unscopables) || SymbolPolyfill("unscopables") - ) - }); - }; - - var registry = Object.create(null); - - var symbolRegistry = function (SymbolPolyfill) { - return Object.defineProperties(SymbolPolyfill, { - for: d_1(function (key) { - if (registry[key]) return registry[key]; - return (registry[key] = SymbolPolyfill(String(key))); - }), - keyFor: d_1(function (symbol) { - var key; - validateSymbol$1(symbol); - for (key in registry) { - if (registry[key] === symbol) return key; - } - return undefined; - }) - }); - }; - - var NativeSymbol$2 = globalThis_1.Symbol; - - var create$5 = Object.create - , defineProperties$1 = Object.defineProperties - , defineProperty$3 = Object.defineProperty; - - var SymbolPolyfill$1, HiddenSymbol$1, isNativeSafe$1; - - if (typeof NativeSymbol$2 === "function") { - try { - String(NativeSymbol$2()); - isNativeSafe$1 = true; - } catch (ignore) {} - } else { - NativeSymbol$2 = null; - } - - // Internal constructor (not one exposed) for creating Symbol instances. - // This one is used to ensure that `someSymbol instanceof Symbol` always return false - HiddenSymbol$1 = function Symbol(description) { - if (this instanceof HiddenSymbol$1) throw new TypeError("Symbol is not a constructor"); - return SymbolPolyfill$1(description); - }; - - // Exposed `Symbol` constructor - // (returns instances of HiddenSymbol) - var polyfill$1 = SymbolPolyfill$1 = function Symbol(description) { - var symbol; - if (this instanceof Symbol) throw new TypeError("Symbol is not a constructor"); - if (isNativeSafe$1) return NativeSymbol$2(description); - symbol = create$5(HiddenSymbol$1.prototype); - description = description === undefined ? "" : String(description); - return defineProperties$1(symbol, { - __description__: d_1("", description), - __name__: d_1("", generateName$1(description)) - }); - }; - - standardSymbols(SymbolPolyfill$1); - symbolRegistry(SymbolPolyfill$1); - - // Internal tweaks for real symbol producer - defineProperties$1(HiddenSymbol$1.prototype, { - constructor: d_1(SymbolPolyfill$1), - toString: d_1("", function () { return this.__name__; }) - }); - - // Proper implementation of methods exposed on Symbol.prototype - // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype - defineProperties$1(SymbolPolyfill$1.prototype, { - toString: d_1(function () { return "Symbol (" + validateSymbol$1(this).__description__ + ")"; }), - valueOf: d_1(function () { return validateSymbol$1(this); }) - }); - defineProperty$3( - SymbolPolyfill$1.prototype, - SymbolPolyfill$1.toPrimitive, - d_1("", function () { - var symbol = validateSymbol$1(this); - if (typeof symbol === "symbol") return symbol; - return symbol.toString(); - }) - ); - defineProperty$3(SymbolPolyfill$1.prototype, SymbolPolyfill$1.toStringTag, d_1("c", "Symbol")); - - // Proper implementaton of toPrimitive and toStringTag for returned symbol instances - defineProperty$3( - HiddenSymbol$1.prototype, SymbolPolyfill$1.toStringTag, - d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toStringTag]) - ); - - // Note: It's important to define `toPrimitive` as last one, as some implementations - // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols) - // And that may invoke error in definition flow: - // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149 - defineProperty$3( - HiddenSymbol$1.prototype, SymbolPolyfill$1.toPrimitive, - d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toPrimitive]) - ); - - var es6Symbol$1 = isImplemented$9() - ? globalThis_1.Symbol - : polyfill$1; - - var iteratorSymbol = es6Symbol$1.iterator - , isArray = Array.isArray; - - var isIterable = function (value) { - if (!isValue(value)) return false; - if (isArray(value)) return true; - if (isString(value)) return true; - if (isArguments(value)) return true; - return typeof value[iteratorSymbol] === "function"; - }; - - var validIterable = function (value) { - if (!isIterable(value)) throw new TypeError(value + " is not iterable"); - return value; - }; - - var objectToString = Object.prototype.toString; - - var coerce = function (value) { - if (!is(value)) return null; - if (is$1(value)) { - // Reject Object.prototype.toString coercion - var valueToString = value.toString; - if (typeof valueToString !== "function") return null; - if (valueToString === objectToString) return null; - // Note: It can be object coming from other realm, still as there's no ES3 and CSP compliant - // way to resolve its realm's Object.prototype.toString it's left as not addressed edge case - } - try { - return "" + value; // Ensure implicit coercion - } catch (error) { - return null; - } - }; - - var safeToString = function (value) { - try { - return value.toString(); - } catch (error) { - try { return String(value); } - catch (error2) { return null; } - } - }; - - var reNewLine = /[\n\r\u2028\u2029]/g; - - var toShortString = function (value) { - var string = safeToString(value); - if (string === null) return ""; - // Trim if too long - if (string.length > 100) string = string.slice(0, 99) + "…"; - // Replace eventual new lines - string = string.replace(reNewLine, function (char) { - switch (char) { - case "\n": - return "\\n"; - case "\r": - return "\\r"; - case "\u2028": - return "\\u2028"; - case "\u2029": - return "\\u2029"; - /* istanbul ignore next */ - default: - throw new Error("Unexpected character"); - } - }); - return string; - }; - - var resolveMessage = function (message, value) { - return message.replace("%v", toShortString(value)); - }; - - var resolveException = function (value, defaultMessage, inputOptions) { - if (!is$1(inputOptions)) throw new TypeError(resolveMessage(defaultMessage, value)); - if (!is(value)) { - if ("default" in inputOptions) return inputOptions["default"]; - if (inputOptions.isOptional) return null; - } - var errorMessage = coerce(inputOptions.errorMessage); - if (!is(errorMessage)) errorMessage = defaultMessage; - throw new TypeError(resolveMessage(errorMessage, value)); - }; - - var ensure = function (value/*, options*/) { - if (is(value)) return value; - return resolveException(value, "Cannot use %v", arguments[1]); - }; - - var ensure$1 = function (value/*, options*/) { - if (is$4(value)) return value; - return resolveException(value, "%v is not a plain function", arguments[1]); - }; - - var isImplemented$a = function () { - var from = Array.from, arr, result; - if (typeof from !== "function") return false; - arr = ["raz", "dwa"]; - result = from(arr); - return Boolean(result && result !== arr && result[1] === "dwa"); - }; - - var objToString$2 = Object.prototype.toString - , isFunctionStringTag = RegExp.prototype.test.bind(/^[object [A-Za-z0-9]*Function]$/); - - var isFunction = function (value) { - return typeof value === "function" && isFunctionStringTag(objToString$2.call(value)); - }; - - var iteratorSymbol$1 = es6Symbol$1.iterator - , isArray$1 = Array.isArray - , call = Function.prototype.call - , desc = { configurable: true, enumerable: true, writable: true, value: null } - , defineProperty$4 = Object.defineProperty; - - // eslint-disable-next-line complexity, max-lines-per-function - var shim$7 = function (arrayLike/*, mapFn, thisArg*/) { - var mapFn = arguments[1] - , thisArg = arguments[2] - , Context - , i - , j - , arr - , length - , code - , iterator - , result - , getIterator - , value; - - arrayLike = Object(validValue(arrayLike)); - - if (isValue(mapFn)) validCallable(mapFn); - if (!this || this === Array || !isFunction(this)) { - // Result: Plain array - if (!mapFn) { - if (isArguments(arrayLike)) { - // Source: Arguments - length = arrayLike.length; - if (length !== 1) return Array.apply(null, arrayLike); - arr = new Array(1); - arr[0] = arrayLike[0]; - return arr; - } - if (isArray$1(arrayLike)) { - // Source: Array - arr = new Array((length = arrayLike.length)); - for (i = 0; i < length; ++i) arr[i] = arrayLike[i]; - return arr; - } - } - arr = []; - } else { - // Result: Non plain array - Context = this; - } - - if (!isArray$1(arrayLike)) { - if ((getIterator = arrayLike[iteratorSymbol$1]) !== undefined) { - // Source: Iterator - iterator = validCallable(getIterator).call(arrayLike); - if (Context) arr = new Context(); - result = iterator.next(); - i = 0; - while (!result.done) { - value = mapFn ? call.call(mapFn, thisArg, result.value, i) : result.value; - if (Context) { - desc.value = value; - defineProperty$4(arr, i, desc); - } else { - arr[i] = value; - } - result = iterator.next(); - ++i; - } - length = i; - } else if (isString(arrayLike)) { - // Source: String - length = arrayLike.length; - if (Context) arr = new Context(); - for (i = 0, j = 0; i < length; ++i) { - value = arrayLike[i]; - if (i + 1 < length) { - code = value.charCodeAt(0); - // eslint-disable-next-line max-depth - if (code >= 0xd800 && code <= 0xdbff) value += arrayLike[++i]; - } - value = mapFn ? call.call(mapFn, thisArg, value, j) : value; - if (Context) { - desc.value = value; - defineProperty$4(arr, j, desc); - } else { - arr[j] = value; - } - ++j; - } - length = j; - } - } - if (length === undefined) { - // Source: array or array-like - length = toPosInteger(arrayLike.length); - if (Context) arr = new Context(length); - for (i = 0; i < length; ++i) { - value = mapFn ? call.call(mapFn, thisArg, arrayLike[i], i) : arrayLike[i]; - if (Context) { - desc.value = value; - defineProperty$4(arr, i, desc); - } else { - arr[i] = value; - } - } - } - if (Context) { - desc.value = null; - arr.length = length; - } - return arr; - }; - - var from_1 = isImplemented$a() ? Array.from : shim$7; - - var copy = function (obj/*, propertyNames, options*/) { - var copy = Object(validValue(obj)), propertyNames = arguments[1], options = Object(arguments[2]); - if (copy !== obj && !propertyNames) return copy; - var result = {}; - if (propertyNames) { - from_1(propertyNames, function (propertyName) { - if (options.ensure || propertyName in obj) result[propertyName] = obj[propertyName]; - }); - } else { - assign(result, obj); - } - return result; - }; - - var bind = Function.prototype.bind - , call$1 = Function.prototype.call - , keys$2 = Object.keys - , objPropertyIsEnumerable = Object.prototype.propertyIsEnumerable; - - var _iterate = function (method, defVal) { - return function (obj, cb/*, thisArg, compareFn*/) { - var list, thisArg = arguments[2], compareFn = arguments[3]; - obj = Object(validValue(obj)); - validCallable(cb); - - list = keys$2(obj); - if (compareFn) { - list.sort(typeof compareFn === "function" ? bind.call(compareFn, obj) : undefined); - } - if (typeof method !== "function") method = list[method]; - return call$1.call(method, list, function (key, index) { - if (!objPropertyIsEnumerable.call(obj, key)) return defVal; - return call$1.call(cb, thisArg, obj[key], key, obj, index); - }); - }; - }; - - var forEach$1 = _iterate("forEach"); - - var call$2 = Function.prototype.call; - - var map$1 = function (obj, cb/*, thisArg*/) { - var result = {}, thisArg = arguments[2]; - validCallable(cb); - forEach$1(obj, function (value, key, targetObj, index) { - result[key] = call$2.call(cb, thisArg, value, key, targetObj, index); - }); - return result; - }; - - var bind$1 = Function.prototype.bind - , defineProperty$5 = Object.defineProperty - , hasOwnProperty$1 = Object.prototype.hasOwnProperty - , define; - - define = function (name, desc, options) { - var value = ensure(desc) && ensure$1(desc.value), dgs; - dgs = copy(desc); - delete dgs.writable; - delete dgs.value; - dgs.get = function () { - if (!options.overwriteDefinition && hasOwnProperty$1.call(this, name)) return value; - desc.value = bind$1.call(value, options.resolveContext ? options.resolveContext(this) : this); - defineProperty$5(this, name, desc); - return this[name]; - }; - return dgs; - }; - - var autoBind = function (props/*, options*/) { - var options = normalizeOptions(arguments[1]); - if (is(options.resolveContext)) ensure$1(options.resolveContext); - return map$1(props, function (desc, name) { return define(name, desc, options); }); - }; - - var defineProperty$6 = Object.defineProperty, defineProperties$2 = Object.defineProperties, Iterator; - - var es6Iterator = Iterator = function (list, context) { - if (!(this instanceof Iterator)) throw new TypeError("Constructor requires 'new'"); - defineProperties$2(this, { - __list__: d_1("w", validValue(list)), - __context__: d_1("w", context), - __nextIndex__: d_1("w", 0) - }); - if (!context) return; - validCallable(context.on); - context.on("_add", this._onAdd); - context.on("_delete", this._onDelete); - context.on("_clear", this._onClear); - }; - - // Internal %IteratorPrototype% doesn't expose its constructor - delete Iterator.prototype.constructor; - - defineProperties$2( - Iterator.prototype, - assign( - { - _next: d_1(function () { - var i; - if (!this.__list__) return undefined; - if (this.__redo__) { - i = this.__redo__.shift(); - if (i !== undefined) return i; - } - if (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++; - this._unBind(); - return undefined; - }), - next: d_1(function () { - return this._createResult(this._next()); - }), - _createResult: d_1(function (i) { - if (i === undefined) return { done: true, value: undefined }; - return { done: false, value: this._resolve(i) }; - }), - _resolve: d_1(function (i) { - return this.__list__[i]; - }), - _unBind: d_1(function () { - this.__list__ = null; - delete this.__redo__; - if (!this.__context__) return; - this.__context__.off("_add", this._onAdd); - this.__context__.off("_delete", this._onDelete); - this.__context__.off("_clear", this._onClear); - this.__context__ = null; - }), - toString: d_1(function () { - return "[object " + (this[es6Symbol$1.toStringTag] || "Object") + "]"; - }) - }, - autoBind({ - _onAdd: d_1(function (index) { - if (index >= this.__nextIndex__) return; - ++this.__nextIndex__; - if (!this.__redo__) { - defineProperty$6(this, "__redo__", d_1("c", [index])); - return; - } - this.__redo__.forEach(function (redo, i) { - if (redo >= index) this.__redo__[i] = ++redo; - }, this); - this.__redo__.push(index); - }), - _onDelete: d_1(function (index) { - var i; - if (index >= this.__nextIndex__) return; - --this.__nextIndex__; - if (!this.__redo__) return; - i = this.__redo__.indexOf(index); - if (i !== -1) this.__redo__.splice(i, 1); - this.__redo__.forEach(function (redo, j) { - if (redo > index) this.__redo__[j] = --redo; - }, this); - }), - _onClear: d_1(function () { - if (this.__redo__) clear.call(this.__redo__); - this.__nextIndex__ = 0; - }) - }) - ) - ); - - defineProperty$6( - Iterator.prototype, - es6Symbol$1.iterator, - d_1(function () { - return this; - }) - ); - - var array = createCommonjsModule(function (module) { - - - - var defineProperty = Object.defineProperty, ArrayIterator; - - ArrayIterator = module.exports = function (arr, kind) { - if (!(this instanceof ArrayIterator)) throw new TypeError("Constructor requires 'new'"); - es6Iterator.call(this, arr); - if (!kind) kind = "value"; - else if (contains.call(kind, "key+value")) kind = "key+value"; - else if (contains.call(kind, "key")) kind = "key"; - else kind = "value"; - defineProperty(this, "__kind__", d_1("", kind)); - }; - if (setPrototypeOf) setPrototypeOf(ArrayIterator, es6Iterator); - - // Internal %ArrayIteratorPrototype% doesn't expose its constructor - delete ArrayIterator.prototype.constructor; - - ArrayIterator.prototype = Object.create(es6Iterator.prototype, { - _resolve: d_1(function (i) { - if (this.__kind__ === "value") return this.__list__[i]; - if (this.__kind__ === "key+value") return [i, this.__list__[i]]; - return i; - }) - }); - defineProperty(ArrayIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "Array Iterator")); - }); - - var string = createCommonjsModule(function (module) { - - - - var defineProperty = Object.defineProperty, StringIterator; - - StringIterator = module.exports = function (str) { - if (!(this instanceof StringIterator)) throw new TypeError("Constructor requires 'new'"); - str = String(str); - es6Iterator.call(this, str); - defineProperty(this, "__length__", d_1("", str.length)); - }; - if (setPrototypeOf) setPrototypeOf(StringIterator, es6Iterator); - - // Internal %ArrayIteratorPrototype% doesn't expose its constructor - delete StringIterator.prototype.constructor; - - StringIterator.prototype = Object.create(es6Iterator.prototype, { - _next: d_1(function () { - if (!this.__list__) return undefined; - if (this.__nextIndex__ < this.__length__) return this.__nextIndex__++; - this._unBind(); - return undefined; - }), - _resolve: d_1(function (i) { - var char = this.__list__[i], code; - if (this.__nextIndex__ === this.__length__) return char; - code = char.charCodeAt(0); - if (code >= 0xd800 && code <= 0xdbff) return char + this.__list__[this.__nextIndex__++]; - return char; - }) - }); - defineProperty(StringIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "String Iterator")); - }); - - var iteratorSymbol$2 = es6Symbol$1.iterator; - - var get = function (obj) { - if (typeof validIterable(obj)[iteratorSymbol$2] === "function") return obj[iteratorSymbol$2](); - if (isArguments(obj)) return new array(obj); - if (isString(obj)) return new string(obj); - return new array(obj); - }; - - var isArray$2 = Array.isArray, call$3 = Function.prototype.call, some = Array.prototype.some; - - var forOf = function (iterable, cb /*, thisArg*/) { - var mode, thisArg = arguments[2], result, doBreak, broken, i, length, char, code; - if (isArray$2(iterable) || isArguments(iterable)) mode = "array"; - else if (isString(iterable)) mode = "string"; - else iterable = get(iterable); - - validCallable(cb); - doBreak = function () { - broken = true; - }; - if (mode === "array") { - some.call(iterable, function (value) { - call$3.call(cb, thisArg, value, doBreak); - return broken; - }); - return; - } - if (mode === "string") { - length = iterable.length; - for (i = 0; i < length; ++i) { - char = iterable[i]; - if (i + 1 < length) { - code = char.charCodeAt(0); - if (code >= 0xd800 && code <= 0xdbff) char += iterable[++i]; - } - call$3.call(cb, thisArg, char, doBreak); - if (broken) break; - } - return; - } - result = iterable.next(); - - while (!result.done) { - call$3.call(cb, thisArg, result.value, doBreak); - if (broken) return; - result = iterable.next(); - } - }; - - var iterator = createCommonjsModule(function (module) { - - var toStringTagSymbol = es6Symbol.toStringTag - - , defineProperty = Object.defineProperty - , SetIterator; - - SetIterator = module.exports = function (set, kind) { - if (!(this instanceof SetIterator)) return new SetIterator(set, kind); - es6Iterator.call(this, set.__setData__, set); - if (!kind) kind = 'value'; - else if (contains.call(kind, 'key+value')) kind = 'key+value'; - else kind = 'value'; - defineProperty(this, '__kind__', d_1('', kind)); - }; - if (setPrototypeOf) setPrototypeOf(SetIterator, es6Iterator); - - SetIterator.prototype = Object.create(es6Iterator.prototype, { - constructor: d_1(SetIterator), - _resolve: d_1(function (i) { - if (this.__kind__ === 'value') return this.__list__[i]; - return [this.__list__[i], this.__list__[i]]; - }), - toString: d_1(function () { return '[object Set Iterator]'; }) - }); - defineProperty(SetIterator.prototype, toStringTagSymbol, d_1('c', 'Set Iterator')); - }); - - // Exports true if environment provides native `Set` implementation, - - var isNativeImplemented = (function () { - if (typeof Set === 'undefined') return false; - return (Object.prototype.toString.call(Set.prototype) === '[object Set]'); - }()); - - var iterator$1 = validIterable - - , call$4 = Function.prototype.call - , defineProperty$7 = Object.defineProperty, getPrototypeOf$1 = Object.getPrototypeOf - , SetPoly, getValues, NativeSet; - - if (isNativeImplemented) NativeSet = Set; - - var polyfill$2 = SetPoly = function Set(/*iterable*/) { - var iterable = arguments[0], self; - if (!(this instanceof SetPoly)) throw new TypeError('Constructor requires \'new\''); - if (isNativeImplemented && setPrototypeOf) self = setPrototypeOf(new NativeSet(), getPrototypeOf$1(this)); - else self = this; - if (iterable != null) iterator$1(iterable); - defineProperty$7(self, '__setData__', d_1('c', [])); - if (!iterable) return self; - forOf(iterable, function (value) { - if (eIndexOf.call(this, value) !== -1) return; - this.push(value); - }, self.__setData__); - return self; - }; - - if (isNativeImplemented) { - if (setPrototypeOf) setPrototypeOf(SetPoly, NativeSet); - SetPoly.prototype = Object.create(NativeSet.prototype, { constructor: d_1(SetPoly) }); - } - - eventEmitter(Object.defineProperties(SetPoly.prototype, { - add: d_1(function (value) { - if (this.has(value)) return this; - this.emit('_add', this.__setData__.push(value) - 1, value); - return this; - }), - clear: d_1(function () { - if (!this.__setData__.length) return; - clear.call(this.__setData__); - this.emit('_clear'); - }), - delete: d_1(function (value) { - var index = eIndexOf.call(this.__setData__, value); - if (index === -1) return false; - this.__setData__.splice(index, 1); - this.emit('_delete', index, value); - return true; - }), - entries: d_1(function () { return new iterator(this, 'key+value'); }), - forEach: d_1(function (cb/*, thisArg*/) { - var thisArg = arguments[1], iterator, result, value; - validCallable(cb); - iterator = this.values(); - result = iterator._next(); - while (result !== undefined) { - value = iterator._resolve(result); - call$4.call(cb, thisArg, value, value, this); - result = iterator._next(); - } - }), - has: d_1(function (value) { - return (eIndexOf.call(this.__setData__, value) !== -1); - }), - keys: d_1(getValues = function () { return this.values(); }), - size: d_1.gs(function () { return this.__setData__.length; }), - values: d_1(function () { return new iterator(this); }), - toString: d_1(function () { return '[object Set]'; }) - })); - defineProperty$7(SetPoly.prototype, es6Symbol.iterator, d_1(getValues)); - defineProperty$7(SetPoly.prototype, es6Symbol.toStringTag, d_1('c', 'Set')); - - var es6Set = isImplemented() ? Set : polyfill$2; - - var isImplemented$b = function () { - var map, iterator, result; - if (typeof Map !== 'function') return false; - try { - // WebKit doesn't support arguments and crashes - map = new Map([['raz', 'one'], ['dwa', 'two'], ['trzy', 'three']]); - } catch (e) { - return false; - } - if (String(map) !== '[object Map]') return false; - if (map.size !== 3) return false; - if (typeof map.clear !== 'function') return false; - if (typeof map.delete !== 'function') return false; - if (typeof map.entries !== 'function') return false; - if (typeof map.forEach !== 'function') return false; - if (typeof map.get !== 'function') return false; - if (typeof map.has !== 'function') return false; - if (typeof map.keys !== 'function') return false; - if (typeof map.set !== 'function') return false; - if (typeof map.values !== 'function') return false; - - iterator = map.entries(); - result = iterator.next(); - if (result.done !== false) return false; - if (!result.value) return false; - if (result.value[0] !== 'raz') return false; - if (result.value[1] !== 'one') return false; - - return true; - }; - - var forEach$2 = Array.prototype.forEach, create$6 = Object.create; - - // eslint-disable-next-line no-unused-vars - var primitiveSet = function (arg/*, …args*/) { - var set = create$6(null); - forEach$2.call(arguments, function (name) { set[name] = true; }); - return set; - }; - - var iteratorKinds = primitiveSet('key', - 'value', 'key+value'); - - var iterator$2 = createCommonjsModule(function (module) { - - var toStringTagSymbol = es6Symbol$1.toStringTag - - , defineProperties = Object.defineProperties - , unBind = es6Iterator.prototype._unBind - , MapIterator; - - MapIterator = module.exports = function (map, kind) { - if (!(this instanceof MapIterator)) return new MapIterator(map, kind); - es6Iterator.call(this, map.__mapKeysData__, map); - if (!kind || !iteratorKinds[kind]) kind = 'key+value'; - defineProperties(this, { - __kind__: d_1('', kind), - __values__: d_1('w', map.__mapValuesData__) - }); - }; - if (setPrototypeOf) setPrototypeOf(MapIterator, es6Iterator); - - MapIterator.prototype = Object.create(es6Iterator.prototype, { - constructor: d_1(MapIterator), - _resolve: d_1(function (i) { - if (this.__kind__ === 'value') return this.__values__[i]; - if (this.__kind__ === 'key') return this.__list__[i]; - return [this.__list__[i], this.__values__[i]]; - }), - _unBind: d_1(function () { - this.__values__ = null; - unBind.call(this); - }), - toString: d_1(function () { return '[object Map Iterator]'; }) - }); - Object.defineProperty(MapIterator.prototype, toStringTagSymbol, - d_1('c', 'Map Iterator')); - }); - - // Exports true if environment provides native `Map` implementation, - - var isNativeImplemented$1 = (function () { - if (typeof Map === 'undefined') return false; - return (Object.prototype.toString.call(new Map()) === '[object Map]'); - }()); - - var iterator$3 = validIterable - - , call$5 = Function.prototype.call - , defineProperties$3 = Object.defineProperties, getPrototypeOf$2 = Object.getPrototypeOf - , MapPoly; - - var polyfill$3 = MapPoly = function (/*iterable*/) { - var iterable = arguments[0], keys, values, self; - if (!(this instanceof MapPoly)) throw new TypeError('Constructor requires \'new\''); - if (isNativeImplemented$1 && setPrototypeOf && (Map !== MapPoly)) { - self = setPrototypeOf(new Map(), getPrototypeOf$2(this)); - } else { - self = this; - } - if (iterable != null) iterator$3(iterable); - defineProperties$3(self, { - __mapKeysData__: d_1('c', keys = []), - __mapValuesData__: d_1('c', values = []) - }); - if (!iterable) return self; - forOf(iterable, function (value) { - var key = validValue(value)[0]; - value = value[1]; - if (eIndexOf.call(keys, key) !== -1) return; - keys.push(key); - values.push(value); - }, self); - return self; - }; - - if (isNativeImplemented$1) { - if (setPrototypeOf) setPrototypeOf(MapPoly, Map); - MapPoly.prototype = Object.create(Map.prototype, { - constructor: d_1(MapPoly) - }); - } - - eventEmitter(defineProperties$3(MapPoly.prototype, { - clear: d_1(function () { - if (!this.__mapKeysData__.length) return; - clear.call(this.__mapKeysData__); - clear.call(this.__mapValuesData__); - this.emit('_clear'); - }), - delete: d_1(function (key) { - var index = eIndexOf.call(this.__mapKeysData__, key); - if (index === -1) return false; - this.__mapKeysData__.splice(index, 1); - this.__mapValuesData__.splice(index, 1); - this.emit('_delete', index, key); - return true; - }), - entries: d_1(function () { return new iterator$2(this, 'key+value'); }), - forEach: d_1(function (cb/*, thisArg*/) { - var thisArg = arguments[1], iterator, result; - validCallable(cb); - iterator = this.entries(); - result = iterator._next(); - while (result !== undefined) { - call$5.call(cb, thisArg, this.__mapValuesData__[result], - this.__mapKeysData__[result], this); - result = iterator._next(); - } - }), - get: d_1(function (key) { - var index = eIndexOf.call(this.__mapKeysData__, key); - if (index === -1) return; - return this.__mapValuesData__[index]; - }), - has: d_1(function (key) { - return (eIndexOf.call(this.__mapKeysData__, key) !== -1); - }), - keys: d_1(function () { return new iterator$2(this, 'key'); }), - set: d_1(function (key, value) { - var index = eIndexOf.call(this.__mapKeysData__, key), emit; - if (index === -1) { - index = this.__mapKeysData__.push(key) - 1; - emit = true; - } - this.__mapValuesData__[index] = value; - if (emit) this.emit('_add', index, key); - return this; - }), - size: d_1.gs(function () { return this.__mapKeysData__.length; }), - values: d_1(function () { return new iterator$2(this, 'value'); }), - toString: d_1(function () { return '[object Map]'; }) - })); - Object.defineProperty(MapPoly.prototype, es6Symbol$1.iterator, d_1(function () { - return this.entries(); - })); - Object.defineProperty(MapPoly.prototype, es6Symbol$1.toStringTag, d_1('c', 'Map')); - - var es6Map = isImplemented$b() ? Map : polyfill$3; - - var toStr = Object.prototype.toString; - - var isArguments$1 = function isArguments(value) { - var str = toStr.call(value); - var isArgs = str === '[object Arguments]'; - if (!isArgs) { - isArgs = str !== '[object Array]' && - value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - toStr.call(value.callee) === '[object Function]'; - } - return isArgs; - }; - - var keysShim; - if (!Object.keys) { - // modified from https://github.com/es-shims/es5-shim - var has = Object.prototype.hasOwnProperty; - var toStr$1 = Object.prototype.toString; - var isArgs = isArguments$1; // eslint-disable-line global-require - var isEnumerable = Object.prototype.propertyIsEnumerable; - var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); - var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); - var dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ]; - var equalsConstructorPrototype = function (o) { - var ctor = o.constructor; - return ctor && ctor.prototype === o; - }; - var excludedKeys = { - $applicationCache: true, - $console: true, - $external: true, - $frame: true, - $frameElement: true, - $frames: true, - $innerHeight: true, - $innerWidth: true, - $onmozfullscreenchange: true, - $onmozfullscreenerror: true, - $outerHeight: true, - $outerWidth: true, - $pageXOffset: true, - $pageYOffset: true, - $parent: true, - $scrollLeft: true, - $scrollTop: true, - $scrollX: true, - $scrollY: true, - $self: true, - $webkitIndexedDB: true, - $webkitStorageInfo: true, - $window: true - }; - var hasAutomationEqualityBug = (function () { - /* global window */ - if (typeof window === 'undefined') { return false; } - for (var k in window) { - try { - if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { - try { - equalsConstructorPrototype(window[k]); - } catch (e) { - return true; - } - } - } catch (e) { - return true; - } - } - return false; - }()); - var equalsConstructorPrototypeIfNotBuggy = function (o) { - /* global window */ - if (typeof window === 'undefined' || !hasAutomationEqualityBug) { - return equalsConstructorPrototype(o); - } - try { - return equalsConstructorPrototype(o); - } catch (e) { - return false; - } - }; - - keysShim = function keys(object) { - var isObject = object !== null && typeof object === 'object'; - var isFunction = toStr$1.call(object) === '[object Function]'; - var isArguments = isArgs(object); - var isString = isObject && toStr$1.call(object) === '[object String]'; - var theKeys = []; - - if (!isObject && !isFunction && !isArguments) { - throw new TypeError('Object.keys called on a non-object'); - } - - var skipProto = hasProtoEnumBug && isFunction; - if (isString && object.length > 0 && !has.call(object, 0)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)); - } - } - - if (isArguments && object.length > 0) { - for (var j = 0; j < object.length; ++j) { - theKeys.push(String(j)); - } - } else { - for (var name in object) { - if (!(skipProto && name === 'prototype') && has.call(object, name)) { - theKeys.push(String(name)); - } - } - } - - if (hasDontEnumBug) { - var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); - - for (var k = 0; k < dontEnums.length; ++k) { - if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { - theKeys.push(dontEnums[k]); - } - } - } - return theKeys; - }; - } - var implementation$1 = keysShim; - - var slice = Array.prototype.slice; - - - var origKeys = Object.keys; - var keysShim$1 = origKeys ? function keys(o) { return origKeys(o); } : implementation$1; - - var originalKeys = Object.keys; - - keysShim$1.shim = function shimObjectKeys() { - if (Object.keys) { - var keysWorksWithArguments = (function () { - // Safari 5.0 bug - var args = Object.keys(arguments); - return args && args.length === arguments.length; - }(1, 2)); - if (!keysWorksWithArguments) { - Object.keys = function keys(object) { // eslint-disable-line func-name-matching - if (isArguments$1(object)) { - return originalKeys(slice.call(object)); - } - return originalKeys(object); - }; - } - } else { - Object.keys = keysShim$1; - } - return Object.keys || keysShim$1; - }; - - var objectKeys = keysShim$1; - - var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol'; - - var toStr$2 = Object.prototype.toString; - var concat = Array.prototype.concat; - var origDefineProperty = Object.defineProperty; - - var isFunction$1 = function (fn) { - return typeof fn === 'function' && toStr$2.call(fn) === '[object Function]'; - }; - - var arePropertyDescriptorsSupported = function () { - var obj = {}; - try { - origDefineProperty(obj, 'x', { enumerable: false, value: obj }); - // eslint-disable-next-line no-unused-vars, no-restricted-syntax - for (var _ in obj) { // jscs:ignore disallowUnusedVariables - return false; - } - return obj.x === obj; - } catch (e) { /* this is IE 8. */ - return false; - } - }; - var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported(); - - var defineProperty$8 = function (object, name, value, predicate) { - if (name in object && (!isFunction$1(predicate) || !predicate())) { - return; - } - if (supportsDescriptors) { - origDefineProperty(object, name, { - configurable: true, - enumerable: false, - value: value, - writable: true - }); - } else { - object[name] = value; - } - }; - - var defineProperties$4 = function (object, map) { - var predicates = arguments.length > 2 ? arguments[2] : {}; - var props = objectKeys(map); - if (hasSymbols) { - props = concat.call(props, Object.getOwnPropertySymbols(map)); - } - for (var i = 0; i < props.length; i += 1) { - defineProperty$8(object, props[i], map[props[i]], predicates[props[i]]); - } - }; - - defineProperties$4.supportsDescriptors = !!supportsDescriptors; - - var defineProperties_1 = defineProperties$4; - - /* eslint complexity: [2, 18], max-statements: [2, 33] */ - var shams = function hasSymbols() { - if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } - if (typeof Symbol.iterator === 'symbol') { return true; } - - var obj = {}; - var sym = Symbol('test'); - var symObj = Object(sym); - if (typeof sym === 'string') { return false; } - - if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } - if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } - - // temp disabled per https://github.com/ljharb/object.assign/issues/17 - // if (sym instanceof Symbol) { return false; } - // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 - // if (!(symObj instanceof Symbol)) { return false; } - - // if (typeof Symbol.prototype.toString !== 'function') { return false; } - // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } - - var symVal = 42; - obj[sym] = symVal; - for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax - if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } - - if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } - - var syms = Object.getOwnPropertySymbols(obj); - if (syms.length !== 1 || syms[0] !== sym) { return false; } - - if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } - - if (typeof Object.getOwnPropertyDescriptor === 'function') { - var descriptor = Object.getOwnPropertyDescriptor(obj, sym); - if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } - } - - return true; - }; - - var origSymbol = commonjsGlobal.Symbol; - - - var hasSymbols$1 = function hasNativeSymbols() { - if (typeof origSymbol !== 'function') { return false; } - if (typeof Symbol !== 'function') { return false; } - if (typeof origSymbol('foo') !== 'symbol') { return false; } - if (typeof Symbol('bar') !== 'symbol') { return false; } - - return shams(); - }; - - /* eslint no-invalid-this: 1 */ - - var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; - var slice$1 = Array.prototype.slice; - var toStr$3 = Object.prototype.toString; - var funcType = '[object Function]'; - - var implementation$2 = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr$3.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); - } - var args = slice$1.call(arguments, 1); - - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice$1.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; - } else { - return target.apply( - that, - args.concat(slice$1.call(arguments)) - ); - } - }; - - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); - } - - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); - - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } - - return bound; - }; - - var functionBind = Function.prototype.bind || implementation$2; - - /* globals - Atomics, - SharedArrayBuffer, - */ - - var undefined$1; - - var $TypeError = TypeError; - - var $gOPD = Object.getOwnPropertyDescriptor; - if ($gOPD) { - try { - $gOPD({}, ''); - } catch (e) { - $gOPD = null; // this is IE 8, which has a broken gOPD - } - } - - var throwTypeError = function () { throw new $TypeError(); }; - var ThrowTypeError = $gOPD - ? (function () { - try { - // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties - arguments.callee; // IE 8 does not throw here - return throwTypeError; - } catch (calleeThrows) { - try { - // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '') - return $gOPD(arguments, 'callee').get; - } catch (gOPDthrows) { - return throwTypeError; - } - } - }()) - : throwTypeError; - - var hasSymbols$2 = hasSymbols$1(); - - var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto - var generatorFunction = undefined$1; - var asyncFunction = undefined$1; - var asyncGenFunction = undefined$1; - - var TypedArray = typeof Uint8Array === 'undefined' ? undefined$1 : getProto(Uint8Array); - - var INTRINSICS = { - '%Array%': Array, - '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer, - '%ArrayBufferPrototype%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer.prototype, - '%ArrayIteratorPrototype%': hasSymbols$2 ? getProto([][Symbol.iterator]()) : undefined$1, - '%ArrayPrototype%': Array.prototype, - '%ArrayProto_entries%': Array.prototype.entries, - '%ArrayProto_forEach%': Array.prototype.forEach, - '%ArrayProto_keys%': Array.prototype.keys, - '%ArrayProto_values%': Array.prototype.values, - '%AsyncFromSyncIteratorPrototype%': undefined$1, - '%AsyncFunction%': asyncFunction, - '%AsyncFunctionPrototype%': undefined$1, - '%AsyncGenerator%': undefined$1, - '%AsyncGeneratorFunction%': asyncGenFunction, - '%AsyncGeneratorPrototype%': undefined$1, - '%AsyncIteratorPrototype%': undefined$1, - '%Atomics%': typeof Atomics === 'undefined' ? undefined$1 : Atomics, - '%Boolean%': Boolean, - '%BooleanPrototype%': Boolean.prototype, - '%DataView%': typeof DataView === 'undefined' ? undefined$1 : DataView, - '%DataViewPrototype%': typeof DataView === 'undefined' ? undefined$1 : DataView.prototype, - '%Date%': Date, - '%DatePrototype%': Date.prototype, - '%decodeURI%': decodeURI, - '%decodeURIComponent%': decodeURIComponent, - '%encodeURI%': encodeURI, - '%encodeURIComponent%': encodeURIComponent, - '%Error%': Error, - '%ErrorPrototype%': Error.prototype, - '%eval%': eval, // eslint-disable-line no-eval - '%EvalError%': EvalError, - '%EvalErrorPrototype%': EvalError.prototype, - '%Float32Array%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array, - '%Float32ArrayPrototype%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array.prototype, - '%Float64Array%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array, - '%Float64ArrayPrototype%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array.prototype, - '%Function%': Function, - '%FunctionPrototype%': Function.prototype, - '%Generator%': undefined$1, - '%GeneratorFunction%': generatorFunction, - '%GeneratorPrototype%': undefined$1, - '%Int8Array%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array, - '%Int8ArrayPrototype%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array.prototype, - '%Int16Array%': typeof Int16Array === 'undefined' ? undefined$1 : Int16Array, - '%Int16ArrayPrototype%': typeof Int16Array === 'undefined' ? undefined$1 : Int8Array.prototype, - '%Int32Array%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array, - '%Int32ArrayPrototype%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array.prototype, - '%isFinite%': isFinite, - '%isNaN%': isNaN, - '%IteratorPrototype%': hasSymbols$2 ? getProto(getProto([][Symbol.iterator]())) : undefined$1, - '%JSON%': typeof JSON === 'object' ? JSON : undefined$1, - '%JSONParse%': typeof JSON === 'object' ? JSON.parse : undefined$1, - '%Map%': typeof Map === 'undefined' ? undefined$1 : Map, - '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Map()[Symbol.iterator]()), - '%MapPrototype%': typeof Map === 'undefined' ? undefined$1 : Map.prototype, - '%Math%': Math, - '%Number%': Number, - '%NumberPrototype%': Number.prototype, - '%Object%': Object, - '%ObjectPrototype%': Object.prototype, - '%ObjProto_toString%': Object.prototype.toString, - '%ObjProto_valueOf%': Object.prototype.valueOf, - '%parseFloat%': parseFloat, - '%parseInt%': parseInt, - '%Promise%': typeof Promise === 'undefined' ? undefined$1 : Promise, - '%PromisePrototype%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype, - '%PromiseProto_then%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype.then, - '%Promise_all%': typeof Promise === 'undefined' ? undefined$1 : Promise.all, - '%Promise_reject%': typeof Promise === 'undefined' ? undefined$1 : Promise.reject, - '%Promise_resolve%': typeof Promise === 'undefined' ? undefined$1 : Promise.resolve, - '%Proxy%': typeof Proxy === 'undefined' ? undefined$1 : Proxy, - '%RangeError%': RangeError, - '%RangeErrorPrototype%': RangeError.prototype, - '%ReferenceError%': ReferenceError, - '%ReferenceErrorPrototype%': ReferenceError.prototype, - '%Reflect%': typeof Reflect === 'undefined' ? undefined$1 : Reflect, - '%RegExp%': RegExp, - '%RegExpPrototype%': RegExp.prototype, - '%Set%': typeof Set === 'undefined' ? undefined$1 : Set, - '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Set()[Symbol.iterator]()), - '%SetPrototype%': typeof Set === 'undefined' ? undefined$1 : Set.prototype, - '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer, - '%SharedArrayBufferPrototype%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer.prototype, - '%String%': String, - '%StringIteratorPrototype%': hasSymbols$2 ? getProto(''[Symbol.iterator]()) : undefined$1, - '%StringPrototype%': String.prototype, - '%Symbol%': hasSymbols$2 ? Symbol : undefined$1, - '%SymbolPrototype%': hasSymbols$2 ? Symbol.prototype : undefined$1, - '%SyntaxError%': SyntaxError, - '%SyntaxErrorPrototype%': SyntaxError.prototype, - '%ThrowTypeError%': ThrowTypeError, - '%TypedArray%': TypedArray, - '%TypedArrayPrototype%': TypedArray ? TypedArray.prototype : undefined$1, - '%TypeError%': $TypeError, - '%TypeErrorPrototype%': $TypeError.prototype, - '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array, - '%Uint8ArrayPrototype%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array.prototype, - '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray, - '%Uint8ClampedArrayPrototype%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray.prototype, - '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array, - '%Uint16ArrayPrototype%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array.prototype, - '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array, - '%Uint32ArrayPrototype%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array.prototype, - '%URIError%': URIError, - '%URIErrorPrototype%': URIError.prototype, - '%WeakMap%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap, - '%WeakMapPrototype%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap.prototype, - '%WeakSet%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet, - '%WeakSetPrototype%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet.prototype - }; - - - var $replace = functionBind.call(Function.call, String.prototype.replace); - - /* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */ - var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g; - var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */ - var stringToPath = function stringToPath(string) { - var result = []; - $replace(string, rePropName, function (match, number, quote, subString) { - result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : (number || match); - }); - return result; - }; - /* end adaptation */ - - var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) { - if (!(name in INTRINSICS)) { - throw new SyntaxError('intrinsic ' + name + ' does not exist!'); - } - - // istanbul ignore if // hopefully this is impossible to test :-) - if (typeof INTRINSICS[name] === 'undefined' && !allowMissing) { - throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); - } - - return INTRINSICS[name]; - }; - - var GetIntrinsic = function GetIntrinsic(name, allowMissing) { - if (typeof name !== 'string' || name.length === 0) { - throw new TypeError('intrinsic name must be a non-empty string'); - } - if (arguments.length > 1 && typeof allowMissing !== 'boolean') { - throw new TypeError('"allowMissing" argument must be a boolean'); - } - - var parts = stringToPath(name); - - var value = getBaseIntrinsic('%' + (parts.length > 0 ? parts[0] : '') + '%', allowMissing); - for (var i = 1; i < parts.length; i += 1) { - if (value != null) { - if ($gOPD && (i + 1) >= parts.length) { - var desc = $gOPD(value, parts[i]); - if (!allowMissing && !(parts[i] in value)) { - throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.'); - } - value = desc ? (desc.get || desc.value) : value[parts[i]]; - } else { - value = value[parts[i]]; - } - } - } - return value; - }; - - var $TypeError$1 = GetIntrinsic('%TypeError%'); - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.10 - - var CheckObjectCoercible = function CheckObjectCoercible(value, optMessage) { - if (value == null) { - throw new $TypeError$1(optMessage || ('Cannot call method on ' + value)); - } - return value; - }; - - var RequireObjectCoercible = CheckObjectCoercible; - - var $Object = GetIntrinsic('%Object%'); - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-toobject - - var ToObject = function ToObject(value) { - RequireObjectCoercible(value); - return $Object(value); - }; - - var $Math = GetIntrinsic('%Math%'); - var $Number = GetIntrinsic('%Number%'); - - var maxSafeInteger = $Number.MAX_SAFE_INTEGER || $Math.pow(2, 53) - 1; - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.3 - - var ToNumber = function ToNumber(value) { - return +value; // eslint-disable-line no-implicit-coercion - }; - - var _isNaN = Number.isNaN || function isNaN(a) { - return a !== a; - }; - - var $isNaN = Number.isNaN || function (a) { return a !== a; }; - - var _isFinite = Number.isFinite || function (x) { return typeof x === 'number' && !$isNaN(x) && x !== Infinity && x !== -Infinity; }; - - var sign$1 = function sign(number) { - return number >= 0 ? 1 : -1; - }; - - var $Math$1 = GetIntrinsic('%Math%'); - - - - - - - var $floor = $Math$1.floor; - var $abs = $Math$1.abs; - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.4 - - var ToInteger = function ToInteger(value) { - var number = ToNumber(value); - if (_isNaN(number)) { return 0; } - if (number === 0 || !_isFinite(number)) { return number; } - return sign$1(number) * $floor($abs(number)); - }; - - var $apply = GetIntrinsic('%Function.prototype.apply%'); - var $call = GetIntrinsic('%Function.prototype.call%'); - var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || functionBind.call($call, $apply); - - var callBind = function callBind() { - return $reflectApply(functionBind, $call, arguments); - }; - - var apply = function applyBind() { - return $reflectApply(functionBind, $apply, arguments); - }; - callBind.apply = apply; - - var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); - - var callBound = function callBoundIntrinsic(name, allowMissing) { - var intrinsic = GetIntrinsic(name, !!allowMissing); - if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.')) { - return callBind(intrinsic); - } - return intrinsic; - }; - - var $test = GetIntrinsic('RegExp.prototype.test'); - - - - var regexTester = function regexTester(regex) { - return callBind($test, regex); - }; - - var isPrimitive = function isPrimitive(value) { - return value === null || (typeof value !== 'function' && typeof value !== 'object'); - }; - - var isPrimitive$1 = function isPrimitive(value) { - return value === null || (typeof value !== 'function' && typeof value !== 'object'); - }; - - var fnToStr = Function.prototype.toString; - var reflectApply = typeof Reflect === 'object' && Reflect !== null && Reflect.apply; - var badArrayLike; - var isCallableMarker; - if (typeof reflectApply === 'function' && typeof Object.defineProperty === 'function') { - try { - badArrayLike = Object.defineProperty({}, 'length', { - get: function () { - throw isCallableMarker; - } - }); - isCallableMarker = {}; - } catch (_) { - reflectApply = null; - } - } else { - reflectApply = null; - } - - var constructorRegex = /^\s*class\b/; - var isES6ClassFn = function isES6ClassFunction(value) { - try { - var fnStr = fnToStr.call(value); - return constructorRegex.test(fnStr); - } catch (e) { - return false; // not a function - } - }; - - var tryFunctionObject = function tryFunctionToStr(value) { - try { - if (isES6ClassFn(value)) { return false; } - fnToStr.call(value); - return true; - } catch (e) { - return false; - } - }; - var toStr$4 = Object.prototype.toString; - var fnClass = '[object Function]'; - var genClass = '[object GeneratorFunction]'; - var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'; - - var isCallable = reflectApply - ? function isCallable(value) { - if (!value) { return false; } - if (typeof value !== 'function' && typeof value !== 'object') { return false; } - if (typeof value === 'function' && !value.prototype) { return true; } - try { - reflectApply(value, null, badArrayLike); - } catch (e) { - if (e !== isCallableMarker) { return false; } - } - return !isES6ClassFn(value); - } - : function isCallable(value) { - if (!value) { return false; } - if (typeof value !== 'function' && typeof value !== 'object') { return false; } - if (typeof value === 'function' && !value.prototype) { return true; } - if (hasToStringTag) { return tryFunctionObject(value); } - if (isES6ClassFn(value)) { return false; } - var strClass = toStr$4.call(value); - return strClass === fnClass || strClass === genClass; - }; - - var getDay = Date.prototype.getDay; - var tryDateObject = function tryDateGetDayCall(value) { - try { - getDay.call(value); - return true; - } catch (e) { - return false; - } - }; - - var toStr$5 = Object.prototype.toString; - var dateClass = '[object Date]'; - var hasToStringTag$1 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'; - - var isDateObject = function isDateObject(value) { - if (typeof value !== 'object' || value === null) { - return false; - } - return hasToStringTag$1 ? tryDateObject(value) : toStr$5.call(value) === dateClass; - }; - - var isSymbol$2 = createCommonjsModule(function (module) { - - var toStr = Object.prototype.toString; - var hasSymbols = hasSymbols$1(); - - if (hasSymbols) { - var symToStr = Symbol.prototype.toString; - var symStringRegex = /^Symbol\(.*\)$/; - var isSymbolObject = function isRealSymbolObject(value) { - if (typeof value.valueOf() !== 'symbol') { - return false; - } - return symStringRegex.test(symToStr.call(value)); - }; - - module.exports = function isSymbol(value) { - if (typeof value === 'symbol') { - return true; - } - if (toStr.call(value) !== '[object Symbol]') { - return false; - } - try { - return isSymbolObject(value); - } catch (e) { - return false; - } - }; - } else { - - module.exports = function isSymbol(value) { - // this environment does not support Symbols. - return false ; - }; - } - }); - - var hasSymbols$3 = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; - - - - - - - var ordinaryToPrimitive = function OrdinaryToPrimitive(O, hint) { - if (typeof O === 'undefined' || O === null) { - throw new TypeError('Cannot call method on ' + O); - } - if (typeof hint !== 'string' || (hint !== 'number' && hint !== 'string')) { - throw new TypeError('hint must be "string" or "number"'); - } - var methodNames = hint === 'string' ? ['toString', 'valueOf'] : ['valueOf', 'toString']; - var method, result, i; - for (i = 0; i < methodNames.length; ++i) { - method = O[methodNames[i]]; - if (isCallable(method)) { - result = method.call(O); - if (isPrimitive$1(result)) { - return result; - } - } - } - throw new TypeError('No default value'); - }; - - var GetMethod = function GetMethod(O, P) { - var func = O[P]; - if (func !== null && typeof func !== 'undefined') { - if (!isCallable(func)) { - throw new TypeError(func + ' returned for property ' + P + ' of object ' + O + ' is not a function'); - } - return func; - } - return void 0; - }; - - // http://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive - var es2015 = function ToPrimitive(input) { - if (isPrimitive$1(input)) { - return input; - } - var hint = 'default'; - if (arguments.length > 1) { - if (arguments[1] === String) { - hint = 'string'; - } else if (arguments[1] === Number) { - hint = 'number'; - } - } - - var exoticToPrim; - if (hasSymbols$3) { - if (Symbol.toPrimitive) { - exoticToPrim = GetMethod(input, Symbol.toPrimitive); - } else if (isSymbol$2(input)) { - exoticToPrim = Symbol.prototype.valueOf; - } - } - if (typeof exoticToPrim !== 'undefined') { - var result = exoticToPrim.call(input, hint); - if (isPrimitive$1(result)) { - return result; - } - throw new TypeError('unable to convert exotic object to primitive'); - } - if (hint === 'default' && (isDateObject(input) || isSymbol$2(input))) { - hint = 'string'; - } - return ordinaryToPrimitive(input, hint === 'default' ? 'number' : hint); - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive - - var ToPrimitive = function ToPrimitive(input) { - if (arguments.length > 1) { - return es2015(input, arguments[1]); - } - return es2015(input); - }; - - var $TypeError$2 = GetIntrinsic('%TypeError%'); - var $Number$1 = GetIntrinsic('%Number%'); - var $RegExp = GetIntrinsic('%RegExp%'); - var $parseInteger = GetIntrinsic('%parseInt%'); - - - - - - var $strSlice = callBound('String.prototype.slice'); - var isBinary = regexTester(/^0b[01]+$/i); - var isOctal = regexTester(/^0o[0-7]+$/i); - var isInvalidHexLiteral = regexTester(/^[-+]0x[0-9a-f]+$/i); - var nonWS = ['\u0085', '\u200b', '\ufffe'].join(''); - var nonWSregex = new $RegExp('[' + nonWS + ']', 'g'); - var hasNonWS = regexTester(nonWSregex); - - // whitespace from: https://es5.github.io/#x15.5.4.20 - // implementation from https://github.com/es-shims/es5-shim/blob/v3.4.0/es5-shim.js#L1304-L1324 - var ws = [ - '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003', - '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028', - '\u2029\uFEFF' - ].join(''); - var trimRegex = new RegExp('(^[' + ws + ']+)|([' + ws + ']+$)', 'g'); - var $replace$1 = callBound('String.prototype.replace'); - var $trim = function (value) { - return $replace$1(value, trimRegex, ''); - }; - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber - - var ToNumber$1 = function ToNumber(argument) { - var value = isPrimitive(argument) ? argument : ToPrimitive(argument, $Number$1); - if (typeof value === 'symbol') { - throw new $TypeError$2('Cannot convert a Symbol value to a number'); - } - if (typeof value === 'string') { - if (isBinary(value)) { - return ToNumber($parseInteger($strSlice(value, 2), 2)); - } else if (isOctal(value)) { - return ToNumber($parseInteger($strSlice(value, 2), 8)); - } else if (hasNonWS(value) || isInvalidHexLiteral(value)) { - return NaN; - } else { - var trimmed = $trim(value); - if (trimmed !== value) { - return ToNumber(trimmed); - } - } - } - return $Number$1(value); - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-tointeger - - var ToInteger$1 = function ToInteger$1(value) { - var number = ToNumber$1(value); - return ToInteger(number); - }; - - var ToLength = function ToLength(argument) { - var len = ToInteger$1(argument); - if (len <= 0) { return 0; } // includes converting -0 to +0 - if (len > maxSafeInteger) { return maxSafeInteger; } - return len; - }; - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.11 - - var IsCallable = isCallable; - - var implementation$3 = function find(predicate) { - var list = ToObject(this); - var length = ToLength(list.length); - if (!IsCallable(predicate)) { - throw new TypeError('Array#find: predicate must be a function'); - } - if (length === 0) { - return void 0; - } - var thisArg; - if (arguments.length > 0) { - thisArg = arguments[1]; - } - - for (var i = 0, value; i < length; i++) { - value = list[i]; - // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) { - if (predicate.apply(thisArg, [value, i, list])) { - return value; - } - } - return void 0; - }; - - var polyfill$4 = function getPolyfill() { - // Detect if an implementation exists - // Detect early implementations which skipped holes in sparse arrays - // eslint-disable-next-line no-sparse-arrays - var implemented = Array.prototype.find && [, 1].find(function () { - return true; - }) !== 1; - - // eslint-disable-next-line global-require - return implemented ? Array.prototype.find : implementation$3; - }; - - var shim$8 = function shimArrayPrototypeFind() { - var polyfill = polyfill$4(); - - defineProperties_1(Array.prototype, { find: polyfill }, { - find: function () { - return Array.prototype.find !== polyfill; - } - }); - - return polyfill; - }; - - var slice$2 = Array.prototype.slice; - - var polyfill$5 = polyfill$4(); - - var boundFindShim = function find(array, predicate) { // eslint-disable-line no-unused-vars - RequireObjectCoercible(array); - var args = slice$2.call(arguments, 1); - return polyfill$5.apply(array, args); - }; - - defineProperties_1(boundFindShim, { - getPolyfill: polyfill$4, - implementation: implementation$3, - shim: shim$8 - }); - - var array_prototype_find = boundFindShim; - - var implementation$4 = function findIndex(predicate) { - var list = ToObject(this); - var length = ToLength(list.length); - if (!IsCallable(predicate)) { - throw new TypeError('Array#findIndex: predicate must be a function'); - } - - if (length === 0) { - return -1; - } - - var thisArg; - if (arguments.length > 0) { - thisArg = arguments[1]; - } - - for (var i = 0, value; i < length; i++) { - value = list[i]; - // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) return i; - if (predicate.apply(thisArg, [value, i, list])) { - return i; - } - } - - return -1; - }; - - var polyfill$6 = function getPolyfill() { - // Detect if an implementation exists - // Detect early implementations which skipped holes in sparse arrays - // eslint-disable-next-line no-sparse-arrays - var implemented = Array.prototype.findIndex && ([, 1].findIndex(function (item, idx) { - return idx === 0; - }) === 0); - - return implemented ? Array.prototype.findIndex : implementation$4; - }; - - var shim$9 = function shimFindIndex() { - var polyfill = polyfill$6(); - - defineProperties_1(Array.prototype, { findIndex: polyfill }, { - findIndex: function () { - return Array.prototype.findIndex !== polyfill; - } - }); - - return polyfill; - }; - - var slice$3 = Array.prototype.slice; - - var polyfill$7 = polyfill$6(); - - var boundShim = function findIndex(array, predicate) { // eslint-disable-line no-unused-vars - RequireObjectCoercible(array); - var args = slice$3.call(arguments, 1); - return polyfill$7.apply(array, args); - }; - - defineProperties_1(boundShim, { - getPolyfill: polyfill$6, - implementation: implementation$4, - shim: shim$9 - }); - - var array_prototype_findindex = boundShim; - - var $apply$1 = GetIntrinsic('%Reflect.apply%', true) || callBound('%Function.prototype.apply%'); - - // https://www.ecma-international.org/ecma-262/6.0/#sec-call - - var Call = function Call(F, V) { - var args = arguments.length > 2 ? arguments[2] : []; - return $apply$1(F, V, args); - }; - - var $defineProperty = GetIntrinsic('%Object.defineProperty%', true); - - if ($defineProperty) { - try { - $defineProperty({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty = null; - } - } - - - - var $isEnumerable = callBound('Object.prototype.propertyIsEnumerable'); - - // eslint-disable-next-line max-params - var DefineOwnProperty = function DefineOwnProperty(IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, desc) { - if (!$defineProperty) { - if (!IsDataDescriptor(desc)) { - // ES3 does not support getters/setters - return false; - } - if (!desc['[[Configurable]]'] || !desc['[[Writable]]']) { - return false; - } - - // fallback for ES3 - if (P in O && $isEnumerable(O, P) !== !!desc['[[Enumerable]]']) { - // a non-enumerable existing property - return false; - } - - // property does not exist at all, or exists but is enumerable - var V = desc['[[Value]]']; - // eslint-disable-next-line no-param-reassign - O[P] = V; // will use [[Define]] - return SameValue(O[P], V); - } - $defineProperty(O, P, FromPropertyDescriptor(desc)); - return true; - }; - - var src = functionBind.call(Function.call, Object.prototype.hasOwnProperty); - - var $TypeError$3 = GetIntrinsic('%TypeError%'); - var $SyntaxError = GetIntrinsic('%SyntaxError%'); - - - - var predicates = { - // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type - 'Property Descriptor': function isPropertyDescriptor(Type, Desc) { - if (Type(Desc) !== 'Object') { - return false; - } - var allowed = { - '[[Configurable]]': true, - '[[Enumerable]]': true, - '[[Get]]': true, - '[[Set]]': true, - '[[Value]]': true, - '[[Writable]]': true - }; - - for (var key in Desc) { // eslint-disable-line - if (src(Desc, key) && !allowed[key]) { - return false; - } - } - - var isData = src(Desc, '[[Value]]'); - var IsAccessor = src(Desc, '[[Get]]') || src(Desc, '[[Set]]'); - if (isData && IsAccessor) { - throw new $TypeError$3('Property Descriptors may not be both accessor and data descriptors'); - } - return true; - } - }; - - var assertRecord = function assertRecord(Type, recordType, argumentName, value) { - var predicate = predicates[recordType]; - if (typeof predicate !== 'function') { - throw new $SyntaxError('unknown record type: ' + recordType); - } - if (!predicate(Type, value)) { - throw new $TypeError$3(argumentName + ' must be a ' + recordType); - } - }; - - // https://www.ecma-international.org/ecma-262/5.1/#sec-8 - - var Type = function Type(x) { - if (x === null) { - return 'Null'; - } - if (typeof x === 'undefined') { - return 'Undefined'; - } - if (typeof x === 'function' || typeof x === 'object') { - return 'Object'; - } - if (typeof x === 'number') { - return 'Number'; - } - if (typeof x === 'boolean') { - return 'Boolean'; - } - if (typeof x === 'string') { - return 'String'; - } - }; - - // https://ecma-international.org/ecma-262/6.0/#sec-ecmascript-data-types-and-values - - var Type$1 = function Type$1(x) { - if (typeof x === 'symbol') { - return 'Symbol'; - } - return Type(x); - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-frompropertydescriptor - - var FromPropertyDescriptor = function FromPropertyDescriptor(Desc) { - if (typeof Desc === 'undefined') { - return Desc; - } - - assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc); - - var obj = {}; - if ('[[Value]]' in Desc) { - obj.value = Desc['[[Value]]']; - } - if ('[[Writable]]' in Desc) { - obj.writable = Desc['[[Writable]]']; - } - if ('[[Get]]' in Desc) { - obj.get = Desc['[[Get]]']; - } - if ('[[Set]]' in Desc) { - obj.set = Desc['[[Set]]']; - } - if ('[[Enumerable]]' in Desc) { - obj.enumerable = Desc['[[Enumerable]]']; - } - if ('[[Configurable]]' in Desc) { - obj.configurable = Desc['[[Configurable]]']; - } - return obj; - }; - - var $gOPD$1 = GetIntrinsic('%Object.getOwnPropertyDescriptor%'); - if ($gOPD$1) { - try { - $gOPD$1([], 'length'); - } catch (e) { - // IE 8 has a broken gOPD - $gOPD$1 = null; - } - } - - var getOwnPropertyDescriptor = $gOPD$1; - - var $Array = GetIntrinsic('%Array%'); - - // eslint-disable-next-line global-require - var toStr$6 = !$Array.isArray && callBound('Object.prototype.toString'); - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isarray - - var IsArray = $Array.isArray || function IsArray(argument) { - return toStr$6(argument) === '[object Array]'; - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-ispropertykey - - var IsPropertyKey = function IsPropertyKey(argument) { - return typeof argument === 'string' || typeof argument === 'symbol'; - }; - - var hasSymbols$4 = hasSymbols$1(); - var hasToStringTag$2 = hasSymbols$4 && typeof Symbol.toStringTag === 'symbol'; - var regexExec; - var isRegexMarker; - var badStringifier; - - if (hasToStringTag$2) { - regexExec = Function.call.bind(RegExp.prototype.exec); - isRegexMarker = {}; - - var throwRegexMarker = function () { - throw isRegexMarker; - }; - badStringifier = { - toString: throwRegexMarker, - valueOf: throwRegexMarker - }; - - if (typeof Symbol.toPrimitive === 'symbol') { - badStringifier[Symbol.toPrimitive] = throwRegexMarker; - } - } - - var toStr$7 = Object.prototype.toString; - var regexClass = '[object RegExp]'; - - var isRegex = hasToStringTag$2 - // eslint-disable-next-line consistent-return - ? function isRegex(value) { - if (!value || typeof value !== 'object') { - return false; - } - - try { - regexExec(value, badStringifier); - } catch (e) { - return e === isRegexMarker; - } - } - : function isRegex(value) { - // In older browsers, typeof regex incorrectly returns 'function' - if (!value || (typeof value !== 'object' && typeof value !== 'function')) { - return false; - } - - return toStr$7.call(value) === regexClass; - }; - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.2 - - var ToBoolean = function ToBoolean(value) { return !!value; }; - - var $match = GetIntrinsic('%Symbol.match%', true); - - - - - - // https://ecma-international.org/ecma-262/6.0/#sec-isregexp - - var IsRegExp = function IsRegExp(argument) { - if (!argument || typeof argument !== 'object') { - return false; - } - if ($match) { - var isRegExp = argument[$match]; - if (typeof isRegExp !== 'undefined') { - return ToBoolean(isRegExp); - } - } - return isRegex(argument); - }; - - var $TypeError$4 = GetIntrinsic('%TypeError%'); - - - - - - // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5 - - var ToPropertyDescriptor = function ToPropertyDescriptor(Obj) { - if (Type$1(Obj) !== 'Object') { - throw new $TypeError$4('ToPropertyDescriptor requires an object'); - } - - var desc = {}; - if (src(Obj, 'enumerable')) { - desc['[[Enumerable]]'] = ToBoolean(Obj.enumerable); - } - if (src(Obj, 'configurable')) { - desc['[[Configurable]]'] = ToBoolean(Obj.configurable); - } - if (src(Obj, 'value')) { - desc['[[Value]]'] = Obj.value; - } - if (src(Obj, 'writable')) { - desc['[[Writable]]'] = ToBoolean(Obj.writable); - } - if (src(Obj, 'get')) { - var getter = Obj.get; - if (typeof getter !== 'undefined' && !IsCallable(getter)) { - throw new TypeError('getter must be a function'); - } - desc['[[Get]]'] = getter; - } - if (src(Obj, 'set')) { - var setter = Obj.set; - if (typeof setter !== 'undefined' && !IsCallable(setter)) { - throw new $TypeError$4('setter must be a function'); - } - desc['[[Set]]'] = setter; - } - - if ((src(desc, '[[Get]]') || src(desc, '[[Set]]')) && (src(desc, '[[Value]]') || src(desc, '[[Writable]]'))) { - throw new $TypeError$4('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute'); - } - return desc; - }; - - var $TypeError$5 = GetIntrinsic('%TypeError%'); - - - - var $isEnumerable$1 = callBound('Object.prototype.propertyIsEnumerable'); - - - - - - - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-ordinarygetownproperty - - var OrdinaryGetOwnProperty = function OrdinaryGetOwnProperty(O, P) { - if (Type$1(O) !== 'Object') { - throw new $TypeError$5('Assertion failed: O must be an Object'); - } - if (!IsPropertyKey(P)) { - throw new $TypeError$5('Assertion failed: P must be a Property Key'); - } - if (!src(O, P)) { - return void 0; - } - if (!getOwnPropertyDescriptor) { - // ES3 / IE 8 fallback - var arrayLength = IsArray(O) && P === 'length'; - var regexLastIndex = IsRegExp(O) && P === 'lastIndex'; - return { - '[[Configurable]]': !(arrayLength || regexLastIndex), - '[[Enumerable]]': $isEnumerable$1(O, P), - '[[Value]]': O[P], - '[[Writable]]': true - }; - } - return ToPropertyDescriptor(getOwnPropertyDescriptor(O, P)); - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isdatadescriptor - - var IsDataDescriptor = function IsDataDescriptor(Desc) { - if (typeof Desc === 'undefined') { - return false; - } - - assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc); - - if (!src(Desc, '[[Value]]') && !src(Desc, '[[Writable]]')) { - return false; - } - - return true; - }; - - var $Object$1 = GetIntrinsic('%Object%'); - - - - var $preventExtensions = $Object$1.preventExtensions; - var $isExtensible = $Object$1.isExtensible; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isextensible-o - - var IsExtensible = $preventExtensions - ? function IsExtensible(obj) { - return !isPrimitive(obj) && $isExtensible(obj); - } - : function IsExtensible(obj) { - return !isPrimitive(obj); - }; - - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.12 - - var SameValue = function SameValue(x, y) { - if (x === y) { // 0 === -0, but they are not identical. - if (x === 0) { return 1 / x === 1 / y; } - return true; - } - return _isNaN(x) && _isNaN(y); - }; - - var $TypeError$6 = GetIntrinsic('%TypeError%'); - - - - - - - - - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-createdataproperty - - var CreateDataProperty = function CreateDataProperty(O, P, V) { - if (Type$1(O) !== 'Object') { - throw new $TypeError$6('Assertion failed: Type(O) is not Object'); - } - if (!IsPropertyKey(P)) { - throw new $TypeError$6('Assertion failed: IsPropertyKey(P) is not true'); - } - var oldDesc = OrdinaryGetOwnProperty(O, P); - var extensible = !oldDesc || IsExtensible(O); - var immutable = oldDesc && (!oldDesc['[[Writable]]'] || !oldDesc['[[Configurable]]']); - if (immutable || !extensible) { - return false; - } - return DefineOwnProperty( - IsDataDescriptor, - SameValue, - FromPropertyDescriptor, - O, - P, - { - '[[Configurable]]': true, - '[[Enumerable]]': true, - '[[Value]]': V, - '[[Writable]]': true - } - ); - }; - - var $TypeError$7 = GetIntrinsic('%TypeError%'); - - - - - - // // https://ecma-international.org/ecma-262/6.0/#sec-createdatapropertyorthrow - - var CreateDataPropertyOrThrow = function CreateDataPropertyOrThrow(O, P, V) { - if (Type$1(O) !== 'Object') { - throw new $TypeError$7('Assertion failed: Type(O) is not Object'); - } - if (!IsPropertyKey(P)) { - throw new $TypeError$7('Assertion failed: IsPropertyKey(P) is not true'); - } - var success = CreateDataProperty(O, P, V); - if (!success) { - throw new $TypeError$7('unable to create data property'); - } - return success; - }; - - var hasMap = typeof Map === 'function' && Map.prototype; - var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null; - var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null; - var mapForEach = hasMap && Map.prototype.forEach; - var hasSet = typeof Set === 'function' && Set.prototype; - var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null; - var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null; - var setForEach = hasSet && Set.prototype.forEach; - var booleanValueOf = Boolean.prototype.valueOf; - var objectToString$1 = Object.prototype.toString; - - var objectInspect = function inspect_ (obj, opts, depth, seen) { - if (typeof obj === 'undefined') { - return 'undefined'; - } - if (obj === null) { - return 'null'; - } - if (typeof obj === 'boolean') { - return obj ? 'true' : 'false'; - } - if (typeof obj === 'string') { - return inspectString(obj); - } - if (typeof obj === 'number') { - if (obj === 0) { - return Infinity / obj > 0 ? '0' : '-0'; - } - return String(obj); - } - - if (!opts) opts = {}; - - var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth; - if (typeof depth === 'undefined') depth = 0; - if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') { - return '[Object]'; - } - - if (typeof seen === 'undefined') seen = []; - else if (indexOf$2(seen, obj) >= 0) { - return '[Circular]'; - } - - function inspect (value, from) { - if (from) { - seen = seen.slice(); - seen.push(from); - } - return inspect_(value, opts, depth + 1, seen); - } - - if (typeof obj === 'function') { - var name = nameOf(obj); - return '[Function' + (name ? ': ' + name : '') + ']'; - } - if (isSymbol$3(obj)) { - var symString = Symbol.prototype.toString.call(obj); - return typeof obj === 'object' ? markBoxed(symString) : symString; - } - if (isElement(obj)) { - var s = '<' + String(obj.nodeName).toLowerCase(); - var attrs = obj.attributes || []; - for (var i = 0; i < attrs.length; i++) { - s += ' ' + attrs[i].name + '="' + quote(attrs[i].value) + '"'; - } - s += '>'; - if (obj.childNodes && obj.childNodes.length) s += '...'; - s += ''; - return s; - } - if (isArray$3(obj)) { - if (obj.length === 0) return '[]'; - return '[ ' + arrObjKeys(obj, inspect).join(', ') + ' ]'; - } - if (isError(obj)) { - var parts = arrObjKeys(obj, inspect); - if (parts.length === 0) return '[' + String(obj) + ']'; - return '{ [' + String(obj) + '] ' + parts.join(', ') + ' }'; - } - if (typeof obj === 'object' && typeof obj.inspect === 'function') { - return obj.inspect(); - } - if (isMap(obj)) { - var parts = []; - mapForEach.call(obj, function (value, key) { - parts.push(inspect(key, obj) + ' => ' + inspect(value, obj)); - }); - return collectionOf('Map', mapSize.call(obj), parts); - } - if (isSet(obj)) { - var parts = []; - setForEach.call(obj, function (value ) { - parts.push(inspect(value, obj)); - }); - return collectionOf('Set', setSize.call(obj), parts); - } - if (isNumber(obj)) { - return markBoxed(Number(obj)); - } - if (isBoolean(obj)) { - return markBoxed(booleanValueOf.call(obj)); - } - if (isString$1(obj)) { - return markBoxed(inspect(String(obj))); - } - if (!isDate(obj) && !isRegExp(obj)) { - var xs = arrObjKeys(obj, inspect); - if (xs.length === 0) return '{}'; - return '{ ' + xs.join(', ') + ' }'; - } - return String(obj); - }; - - function quote (s) { - return String(s).replace(/"/g, '"'); - } - - function isArray$3 (obj) { return toStr$8(obj) === '[object Array]' } - function isDate (obj) { return toStr$8(obj) === '[object Date]' } - function isRegExp (obj) { return toStr$8(obj) === '[object RegExp]' } - function isError (obj) { return toStr$8(obj) === '[object Error]' } - function isSymbol$3 (obj) { return toStr$8(obj) === '[object Symbol]' } - function isString$1 (obj) { return toStr$8(obj) === '[object String]' } - function isNumber (obj) { return toStr$8(obj) === '[object Number]' } - function isBoolean (obj) { return toStr$8(obj) === '[object Boolean]' } - - var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; }; - function has$1 (obj, key) { - return hasOwn.call(obj, key); - } - - function toStr$8 (obj) { - return objectToString$1.call(obj); - } - - function nameOf (f) { - if (f.name) return f.name; - var m = String(f).match(/^function\s*([\w$]+)/); - if (m) return m[1]; - } - - function indexOf$2 (xs, x) { - if (xs.indexOf) return xs.indexOf(x); - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - return -1; - } - - function isMap (x) { - if (!mapSize) { - return false; - } - try { - mapSize.call(x); - try { - setSize.call(x); - } catch (s) { - return true; - } - return x instanceof Map; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; - } - - function isSet (x) { - if (!setSize) { - return false; - } - try { - setSize.call(x); - try { - mapSize.call(x); - } catch (m) { - return true; - } - return x instanceof Set; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; - } - - function isElement (x) { - if (!x || typeof x !== 'object') return false; - if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) { - return true; - } - return typeof x.nodeName === 'string' - && typeof x.getAttribute === 'function' - ; - } - - function inspectString (str) { - var s = str.replace(/(['\\])/g, '\\$1').replace(/[\x00-\x1f]/g, lowbyte); - return "'" + s + "'"; - } - - function lowbyte (c) { - var n = c.charCodeAt(0); - var x = { 8: 'b', 9: 't', 10: 'n', 12: 'f', 13: 'r' }[n]; - if (x) return '\\' + x; - return '\\x' + (n < 0x10 ? '0' : '') + n.toString(16); - } - - function markBoxed (str) { - return 'Object(' + str + ')'; - } - - function collectionOf (type, size, entries) { - return type + ' (' + size + ') {' + entries.join(', ') + '}'; - } - - function arrObjKeys (obj, inspect) { - var isArr = isArray$3(obj); - var xs = []; - if (isArr) { - xs.length = obj.length; - for (var i = 0; i < obj.length; i++) { - xs[i] = has$1(obj, i) ? inspect(obj[i], obj) : ''; - } - } - for (var key in obj) { - if (!has$1(obj, key)) continue; - if (isArr && String(Number(key)) === key && key < obj.length) continue; - if (/[^\w$]/.test(key)) { - xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj)); - } else { - xs.push(key + ': ' + inspect(obj[key], obj)); - } - } - return xs; - } - - var $TypeError$8 = GetIntrinsic('%TypeError%'); - - - - - - - /** - * 7.3.1 Get (O, P) - https://ecma-international.org/ecma-262/6.0/#sec-get-o-p - * 1. Assert: Type(O) is Object. - * 2. Assert: IsPropertyKey(P) is true. - * 3. Return O.[[Get]](P, O). - */ - - var Get = function Get(O, P) { - // 7.3.1.1 - if (Type$1(O) !== 'Object') { - throw new $TypeError$8('Assertion failed: Type(O) is not Object'); - } - // 7.3.1.2 - if (!IsPropertyKey(P)) { - throw new $TypeError$8('Assertion failed: IsPropertyKey(P) is not true, got ' + objectInspect(P)); - } - // 7.3.1.3 - return O[P]; - }; - - var $TypeError$9 = GetIntrinsic('%TypeError%'); - - var isPropertyDescriptor = function IsPropertyDescriptor(ES, Desc) { - if (ES.Type(Desc) !== 'Object') { - return false; - } - var allowed = { - '[[Configurable]]': true, - '[[Enumerable]]': true, - '[[Get]]': true, - '[[Set]]': true, - '[[Value]]': true, - '[[Writable]]': true - }; - - for (var key in Desc) { // eslint-disable-line no-restricted-syntax - if (src(Desc, key) && !allowed[key]) { - return false; - } - } - - if (ES.IsDataDescriptor(Desc) && ES.IsAccessorDescriptor(Desc)) { - throw new $TypeError$9('Property Descriptors may not be both accessor and data descriptors'); - } - return true; - }; - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isaccessordescriptor - - var IsAccessorDescriptor = function IsAccessorDescriptor(Desc) { - if (typeof Desc === 'undefined') { - return false; - } - - assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc); - - if (!src(Desc, '[[Get]]') && !src(Desc, '[[Set]]')) { - return false; - } - - return true; - }; - - var $TypeError$a = GetIntrinsic('%TypeError%'); - - - - - - - - - - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-definepropertyorthrow - - var DefinePropertyOrThrow = function DefinePropertyOrThrow(O, P, desc) { - if (Type$1(O) !== 'Object') { - throw new $TypeError$a('Assertion failed: Type(O) is not Object'); - } - - if (!IsPropertyKey(P)) { - throw new $TypeError$a('Assertion failed: IsPropertyKey(P) is not true'); - } - - var Desc = isPropertyDescriptor({ - Type: Type$1, - IsDataDescriptor: IsDataDescriptor, - IsAccessorDescriptor: IsAccessorDescriptor - }, desc) ? desc : ToPropertyDescriptor(desc); - if (!isPropertyDescriptor({ - Type: Type$1, - IsDataDescriptor: IsDataDescriptor, - IsAccessorDescriptor: IsAccessorDescriptor - }, Desc)) { - throw new $TypeError$a('Assertion failed: Desc is not a valid Property Descriptor'); - } - - return DefineOwnProperty( - IsDataDescriptor, - SameValue, - FromPropertyDescriptor, - O, - P, - Desc - ); - }; - - var IsConstructor = createCommonjsModule(function (module) { - - - - var $construct = GetIntrinsic('%Reflect.construct%', true); - - var DefinePropertyOrThrow$1 = DefinePropertyOrThrow; - try { - DefinePropertyOrThrow$1({}, '', { '[[Get]]': function () {} }); - } catch (e) { - // Accessor properties aren't supported - DefinePropertyOrThrow$1 = null; - } - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor - - if (DefinePropertyOrThrow$1 && $construct) { - var isConstructorMarker = {}; - var badArrayLike = {}; - DefinePropertyOrThrow$1(badArrayLike, 'length', { - '[[Get]]': function () { - throw isConstructorMarker; - }, - '[[Enumerable]]': true - }); - - module.exports = function IsConstructor(argument) { - try { - // `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`: - $construct(argument, badArrayLike); - } catch (err) { - return err === isConstructorMarker; - } - }; - } else { - module.exports = function IsConstructor(argument) { - // unfortunately there's no way to truly check this without try/catch `new argument` in old environments - return typeof argument === 'function' && !!argument.prototype; - }; - } - }); - - var $String = GetIntrinsic('%String%'); - var $TypeError$b = GetIntrinsic('%TypeError%'); - - // https://www.ecma-international.org/ecma-262/6.0/#sec-tostring - - var ToString = function ToString(argument) { - if (typeof argument === 'symbol') { - throw new $TypeError$b('Cannot convert a Symbol value to a string'); - } - return $String(argument); - }; - - var hasToStringTag$3 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'; - var toStr$9 = Object.prototype.toString; - - var isStandardArguments = function isArguments(value) { - if (hasToStringTag$3 && value && typeof value === 'object' && Symbol.toStringTag in value) { - return false; - } - return toStr$9.call(value) === '[object Arguments]'; - }; - - var isLegacyArguments = function isArguments(value) { - if (isStandardArguments(value)) { - return true; - } - return value !== null && - typeof value === 'object' && - typeof value.length === 'number' && - value.length >= 0 && - toStr$9.call(value) !== '[object Array]' && - toStr$9.call(value.callee) === '[object Function]'; - }; - - var supportsStandardArguments = (function () { - return isStandardArguments(arguments); - }()); - - isStandardArguments.isLegacyArguments = isLegacyArguments; // for tests - - var isArguments$2 = supportsStandardArguments ? isStandardArguments : isLegacyArguments; - - var toString = {}.toString; - - var isarray = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; - }; - - var strValue = String.prototype.valueOf; - var tryStringObject = function tryStringObject(value) { - try { - strValue.call(value); - return true; - } catch (e) { - return false; - } - }; - var toStr$a = Object.prototype.toString; - var strClass = '[object String]'; - var hasToStringTag$4 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'; - - var isString$2 = function isString(value) { - if (typeof value === 'string') { - return true; - } - if (typeof value !== 'object') { - return false; - } - return hasToStringTag$4 ? tryStringObject(value) : toStr$a.call(value) === strClass; - }; - - var $Map = typeof Map === 'function' && Map.prototype ? Map : null; - var $Set = typeof Set === 'function' && Set.prototype ? Set : null; - - var exported; - - if (!$Map) { - // eslint-disable-next-line no-unused-vars - exported = function isMap(x) { - // `Map` is not present in this environment. - return false; - }; - } - - var $mapHas = $Map ? Map.prototype.has : null; - var $setHas = $Set ? Set.prototype.has : null; - if (!exported && !$mapHas) { - // eslint-disable-next-line no-unused-vars - exported = function isMap(x) { - // `Map` does not have a `has` method - return false; - }; - } - - var isMap$1 = exported || function isMap(x) { - if (!x || typeof x !== 'object') { - return false; - } - try { - $mapHas.call(x); - if ($setHas) { - try { - $setHas.call(x); - } catch (e) { - return true; - } - } - return x instanceof $Map; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; - }; - - var $Map$1 = typeof Map === 'function' && Map.prototype ? Map : null; - var $Set$1 = typeof Set === 'function' && Set.prototype ? Set : null; - - var exported$1; - - if (!$Set$1) { - // eslint-disable-next-line no-unused-vars - exported$1 = function isSet(x) { - // `Set` is not present in this environment. - return false; - }; - } - - var $mapHas$1 = $Map$1 ? Map.prototype.has : null; - var $setHas$1 = $Set$1 ? Set.prototype.has : null; - if (!exported$1 && !$setHas$1) { - // eslint-disable-next-line no-unused-vars - exported$1 = function isSet(x) { - // `Set` does not have a `has` method - return false; - }; - } - - var isSet$1 = exported$1 || function isSet(x) { - if (!x || typeof x !== 'object') { - return false; - } - try { - $setHas$1.call(x); - if ($mapHas$1) { - try { - $mapHas$1.call(x); - } catch (e) { - return true; - } - } - return x instanceof $Set$1; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; - }; - - var esGetIterator = createCommonjsModule(function (module) { - - /* eslint global-require: 0 */ - // the code is structured this way so that bundlers can - // alias out `has-symbols` to `() => true` or `() => false` if your target - // environments' Symbol capabilities are known, and then use - // dead code elimination on the rest of this module. - // - // Similarly, `isarray` can be aliased to `Array.isArray` if - // available in all target environments. - - - - if (hasSymbols$1() || shams()) { - var $iterator = Symbol.iterator; - // Symbol is available natively or shammed - // natively: - // - Chrome >= 38 - // - Edge 12-14?, Edge >= 15 for sure - // - FF >= 36 - // - Safari >= 9 - // - node >= 0.12 - module.exports = function getIterator(iterable) { - // alternatively, `iterable[$iterator]?.()` - if (iterable != null && typeof iterable[$iterator] !== 'undefined') { - return iterable[$iterator](); - } - if (isArguments$2(iterable)) { - // arguments objects lack Symbol.iterator - // - node 0.12 - return Array.prototype[$iterator].call(iterable); - } - }; - } else { - // Symbol is not available, native or shammed - var isArray = isarray; - var isString = isString$2; - var GetIntrinsic$1 = GetIntrinsic; - var $Map = GetIntrinsic$1('%Map%', true); - var $Set = GetIntrinsic$1('%Set%', true); - var callBound$1 = callBound; - var $arrayPush = callBound$1('Array.prototype.push'); - var $charCodeAt = callBound$1('String.prototype.charCodeAt'); - var $stringSlice = callBound$1('String.prototype.slice'); - - var advanceStringIndex = function advanceStringIndex(S, index) { - var length = S.length; - if ((index + 1) >= length) { - return index + 1; - } - - var first = $charCodeAt(S, index); - if (first < 0xD800 || first > 0xDBFF) { - return index + 1; - } - - var second = $charCodeAt(S, index + 1); - if (second < 0xDC00 || second > 0xDFFF) { - return index + 1; - } - - return index + 2; - }; - - var getArrayIterator = function getArrayIterator(arraylike) { - var i = 0; - return { - next: function next() { - var done = i >= arraylike.length; - var value; - if (!done) { - value = arraylike[i]; - i += 1; - } - return { - done: done, - value: value - }; - } - }; - }; - - var getNonCollectionIterator = function getNonCollectionIterator(iterable) { - if (isArray(iterable) || isArguments$2(iterable)) { - return getArrayIterator(iterable); - } - if (isString(iterable)) { - var i = 0; - return { - next: function next() { - var nextIndex = advanceStringIndex(iterable, i); - var value = $stringSlice(iterable, i, nextIndex); - i = nextIndex; - return { - done: nextIndex > iterable.length, - value: value - }; - } - }; - } - }; - - if (!$Map && !$Set) { - // the only language iterables are Array, String, arguments - // - Safari <= 6.0 - // - Chrome < 38 - // - node < 0.12 - // - FF < 13 - // - IE < 11 - // - Edge < 11 - - module.exports = getNonCollectionIterator; - } else { - // either Map or Set are available, but Symbol is not - // - es6-shim on an ES5 browser - // - Safari 6.2 (maybe 6.1?) - // - FF v[13, 36) - // - IE 11 - // - Edge 11 - // - Safari v[6, 9) - - var isMap = isMap$1; - var isSet = isSet$1; - - // Firefox >= 27, IE 11, Safari 6.2 - 9, Edge 11, es6-shim in older envs, all have forEach - var $mapForEach = callBound$1('Map.prototype.forEach', true); - var $setForEach = callBound$1('Set.prototype.forEach', true); - if (typeof process === 'undefined' || !process.versions || !process.versions.node) { // "if is not node" - - // Firefox 17 - 26 has `.iterator()`, whose iterator `.next()` either - // returns a value, or throws a StopIteration object. These browsers - // do not have any other mechanism for iteration. - var $mapIterator = callBound$1('Map.prototype.iterator', true); - var $setIterator = callBound$1('Set.prototype.iterator', true); - var getStopIterationIterator = function (iterator) { - var done = false; - return { - next: function next() { - try { - return { - done: done, - value: done ? undefined : iterator.next() - }; - } catch (e) { - done = true; - return { - done: true, - value: undefined - }; - } - } - }; - }; - } - // Firefox 27-35, and some older es6-shim versions, use a string "@@iterator" property - // this returns a proper iterator object, so we should use it instead of forEach. - // newer es6-shim versions use a string "_es6-shim iterator_" property. - var $mapAtAtIterator = callBound$1('Map.prototype.@@iterator', true) || callBound$1('Map.prototype._es6-shim iterator_', true); - var $setAtAtIterator = callBound$1('Set.prototype.@@iterator', true) || callBound$1('Set.prototype._es6-shim iterator_', true); - - var getCollectionIterator = function getCollectionIterator(iterable) { - if (isMap(iterable)) { - if ($mapIterator) { - return getStopIterationIterator($mapIterator(iterable)); - } - if ($mapAtAtIterator) { - return $mapAtAtIterator(iterable); - } - if ($mapForEach) { - var entries = []; - $mapForEach(iterable, function (v, k) { - $arrayPush(entries, [k, v]); - }); - return getArrayIterator(entries); - } - } - if (isSet(iterable)) { - if ($setIterator) { - return getStopIterationIterator($setIterator(iterable)); - } - if ($setAtAtIterator) { - return $setAtAtIterator(iterable); - } - if ($setForEach) { - var values = []; - $setForEach(iterable, function (v) { - $arrayPush(values, v); - }); - return getArrayIterator(values); - } - } - }; - - module.exports = function getIterator(iterable) { - return getCollectionIterator(iterable) || getNonCollectionIterator(iterable); - }; - } - } - }); - - var $TypeError$c = TypeError; - - // eslint-disable-next-line consistent-return - var iterateIterator = function iterateIterator(iterator) { - if (!iterator || typeof iterator.next !== 'function') { - throw new $TypeError$c('iterator must be an object with a `next` method'); - } - if (arguments.length > 1) { - var callback = arguments[1]; - if (typeof callback !== 'function') { - throw new $TypeError$c('`callback`, if provided, must be a function'); - } - } - var values = callback || []; - var result; - while ((result = iterator.next()) && !result.done) { - if (callback) { - callback(result.value); // eslint-disable-line callback-return - } else { - values.push(result.value); - } - } - if (!callback) { - return values; - } - }; - - var $TypeError$d = TypeError; - - - var iterateValue = function iterateValue(iterable) { - var iterator = esGetIterator(iterable); - if (!iterator) { - throw new $TypeError$d('non-iterable value provided'); - } - if (arguments.length > 1) { - return iterateIterator(iterator, arguments[1]); - } - return iterateIterator(iterator); - }; - - var implementation$5 = function from(items) { - var C = this; - if (items === null || typeof items === 'undefined') { - throw new TypeError('`Array.from` requires an array-like object, not `null` or `undefined`'); - } - var mapFn, T; - if (typeof arguments[1] !== 'undefined') { - mapFn = arguments[1]; - if (!IsCallable(mapFn)) { - throw new TypeError('When provided, the second argument to `Array.from` must be a function'); - } - if (arguments.length > 2) { - T = arguments[2]; - } - } - - var values; - try { - values = iterateValue(items); - } catch (e) { - values = items; - } - - var arrayLike = ToObject(values); - var len = ToLength(arrayLike.length); - var A = IsConstructor(C) ? ToObject(new C(len)) : new Array(len); - var k = 0; - var kValue, mappedValue; - - while (k < len) { - var Pk = ToString(k); - kValue = Get(arrayLike, Pk); - if (mapFn) { - mappedValue = typeof T === 'undefined' ? mapFn(kValue, k) : Call(mapFn, T, [kValue, k]); - } else { - mappedValue = kValue; - } - CreateDataPropertyOrThrow(A, Pk, mappedValue); - k += 1; - } - A.length = len; - return A; - }; - - var tryCall = function (fn) { - try { - return fn(); - } catch (e) { - return false; - } - }; - - var polyfill$8 = function getPolyfill() { - if (IsCallable(Array.from)) { - var handlesUndefMapper = tryCall(function () { - // Microsoft Edge v0.11 throws if the mapFn argument is *provided* but undefined, - // but the spec doesn't care if it's provided or not - undefined doesn't throw. - return Array.from([0], undefined); - }); - if (!handlesUndefMapper) { - var origArrayFrom = Array.from; - return function from(items) { - /* eslint no-invalid-this: 0 */ - if (arguments.length > 1 && typeof arguments[1] !== 'undefined') { - return Call(origArrayFrom, this, arguments); - } else { - return Call(origArrayFrom, this, [items]); - } - }; - } - var implemented = tryCall(function () { - // Detects a Firefox bug in v32 - // https://bugzilla.mozilla.org/show_bug.cgi?id=1063993 - return Array.from({ 'length': -1 }) === 0; - }) - && tryCall(function () { - // Detects a bug in Webkit nightly r181886 - var arr = Array.from([0].entries()); - return arr.length === 1 && IsArray(arr[0]) && arr[0][0] === 0 && arr[0][1] === 0; - }) - && tryCall(function () { - return Array.from({ 'length': -Infinity }); - }); - if (implemented) { - return Array.from; - } - } - - return implementation$5; - }; - - var shim$a = function shimArrayFrom() { - var polyfill = polyfill$8(); - - defineProperties_1(Array, { 'from': polyfill }, { - 'from': function () { - return Array.from !== polyfill; - } - }); - - return polyfill; - }; - - var polyfill$9 = polyfill$8(); - - // eslint-disable-next-line no-unused-vars - var boundFromShim = function from(items) { - // eslint-disable-next-line no-invalid-this - return polyfill$9.apply(this || Array, arguments); - }; - - defineProperties_1(boundFromShim, { - 'getPolyfill': polyfill$8, - 'implementation': implementation$5, - 'shim': shim$a - }); - - var array_from = boundFromShim; - - var $isEnumerable$2 = callBound('Object.prototype.propertyIsEnumerable'); - - var implementation$6 = function values(O) { - var obj = RequireObjectCoercible(O); - var vals = []; - for (var key in obj) { - if (src(obj, key) && $isEnumerable$2(obj, key)) { - vals.push(obj[key]); - } - } - return vals; - }; - - var polyfill$a = function getPolyfill() { - return typeof Object.values === 'function' ? Object.values : implementation$6; - }; - - var shim$b = function shimValues() { - var polyfill = polyfill$a(); - defineProperties_1(Object, { values: polyfill }, { - values: function testValues() { - return Object.values !== polyfill; - } - }); - return polyfill; - }; - - var polyfill$b = polyfill$a(); - - defineProperties_1(polyfill$b, { - getPolyfill: polyfill$a, - implementation: implementation$6, - shim: shim$b - }); - - var object_values = polyfill$b; - - // modified from https://github.com/es-shims/es6-shim - - - var canBeObject = function (obj) { - return typeof obj !== 'undefined' && obj !== null; - }; - var hasSymbols$5 = shams(); - var toObject = Object; - var push = functionBind.call(Function.call, Array.prototype.push); - var propIsEnumerable = functionBind.call(Function.call, Object.prototype.propertyIsEnumerable); - var originalGetSymbols = hasSymbols$5 ? Object.getOwnPropertySymbols : null; - - var implementation$7 = function assign(target, source1) { - if (!canBeObject(target)) { throw new TypeError('target must be an object'); } - var objTarget = toObject(target); - var s, source, i, props, syms, value, key; - for (s = 1; s < arguments.length; ++s) { - source = toObject(arguments[s]); - props = objectKeys(source); - var getSymbols = hasSymbols$5 && (Object.getOwnPropertySymbols || originalGetSymbols); - if (getSymbols) { - syms = getSymbols(source); - for (i = 0; i < syms.length; ++i) { - key = syms[i]; - if (propIsEnumerable(source, key)) { - push(props, key); - } - } - } - for (i = 0; i < props.length; ++i) { - key = props[i]; - value = source[key]; - if (propIsEnumerable(source, key)) { - objTarget[key] = value; - } - } - } - return objTarget; - }; - - var lacksProperEnumerationOrder = function () { - if (!Object.assign) { - return false; - } - // v8, specifically in node 4.x, has a bug with incorrect property enumeration order - // note: this does not detect the bug unless there's 20 characters - var str = 'abcdefghijklmnopqrst'; - var letters = str.split(''); - var map = {}; - for (var i = 0; i < letters.length; ++i) { - map[letters[i]] = letters[i]; - } - var obj = Object.assign({}, map); - var actual = ''; - for (var k in obj) { - actual += k; - } - return str !== actual; - }; - - var assignHasPendingExceptions = function () { - if (!Object.assign || !Object.preventExtensions) { - return false; - } - // Firefox 37 still has "pending exception" logic in its Object.assign implementation, - // which is 72% slower than our shim, and Firefox 40's native implementation. - var thrower = Object.preventExtensions({ 1: 2 }); - try { - Object.assign(thrower, 'xy'); - } catch (e) { - return thrower[1] === 'y'; - } - return false; - }; - - var polyfill$c = function getPolyfill() { - if (!Object.assign) { - return implementation$7; - } - if (lacksProperEnumerationOrder()) { - return implementation$7; - } - if (assignHasPendingExceptions()) { - return implementation$7; - } - return Object.assign; - }; - - var shim$c = function shimAssign() { - var polyfill = polyfill$c(); - defineProperties_1( - Object, - { assign: polyfill }, - { assign: function () { return Object.assign !== polyfill; } } - ); - return polyfill; - }; - - var polyfill$d = polyfill$c(); - - defineProperties_1(polyfill$d, { - getPolyfill: polyfill$c, - implementation: implementation$7, - shim: shim$c - }); - - var object_assign = polyfill$d; - - /** - * @this {Promise} - */ - function finallyConstructor(callback) { - var constructor = this.constructor; - return this.then( - function(value) { - // @ts-ignore - return constructor.resolve(callback()).then(function() { - return value; - }); - }, - function(reason) { - // @ts-ignore - return constructor.resolve(callback()).then(function() { - // @ts-ignore - return constructor.reject(reason); - }); - } - ); - } - - // Store setTimeout reference so promise-polyfill will be unaffected by - // other code modifying setTimeout (like sinon.useFakeTimers()) - var setTimeoutFunc = setTimeout; - - function isArray$4(x) { - return Boolean(x && typeof x.length !== 'undefined'); - } - - function noop$1() {} - - // Polyfill for Function.prototype.bind - function bind$2(fn, thisArg) { - return function() { - fn.apply(thisArg, arguments); - }; - } - - /** - * @constructor - * @param {Function} fn - */ - function Promise$1(fn) { - if (!(this instanceof Promise$1)) - throw new TypeError('Promises must be constructed via new'); - if (typeof fn !== 'function') throw new TypeError('not a function'); - /** @type {!number} */ - this._state = 0; - /** @type {!boolean} */ - this._handled = false; - /** @type {Promise|undefined} */ - this._value = undefined; - /** @type {!Array} */ - this._deferreds = []; - - doResolve(fn, this); - } - - function handle(self, deferred) { - while (self._state === 3) { - self = self._value; - } - if (self._state === 0) { - self._deferreds.push(deferred); - return; - } - self._handled = true; - Promise$1._immediateFn(function() { - var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; - if (cb === null) { - (self._state === 1 ? resolve : reject)(deferred.promise, self._value); - return; - } - var ret; - try { - ret = cb(self._value); - } catch (e) { - reject(deferred.promise, e); - return; - } - resolve(deferred.promise, ret); - }); - } - - function resolve(self, newValue) { - try { - // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure - if (newValue === self) - throw new TypeError('A promise cannot be resolved with itself.'); - if ( - newValue && - (typeof newValue === 'object' || typeof newValue === 'function') - ) { - var then = newValue.then; - if (newValue instanceof Promise$1) { - self._state = 3; - self._value = newValue; - finale(self); - return; - } else if (typeof then === 'function') { - doResolve(bind$2(then, newValue), self); - return; - } - } - self._state = 1; - self._value = newValue; - finale(self); - } catch (e) { - reject(self, e); - } - } - - function reject(self, newValue) { - self._state = 2; - self._value = newValue; - finale(self); - } - - function finale(self) { - if (self._state === 2 && self._deferreds.length === 0) { - Promise$1._immediateFn(function() { - if (!self._handled) { - Promise$1._unhandledRejectionFn(self._value); - } - }); - } - - for (var i = 0, len = self._deferreds.length; i < len; i++) { - handle(self, self._deferreds[i]); - } - self._deferreds = null; - } - - /** - * @constructor - */ - function Handler(onFulfilled, onRejected, promise) { - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; - this.onRejected = typeof onRejected === 'function' ? onRejected : null; - this.promise = promise; - } - - /** - * Take a potentially misbehaving resolver function and make sure - * onFulfilled and onRejected are only called once. - * - * Makes no guarantees about asynchrony. - */ - function doResolve(fn, self) { - var done = false; - try { - fn( - function(value) { - if (done) return; - done = true; - resolve(self, value); - }, - function(reason) { - if (done) return; - done = true; - reject(self, reason); - } - ); - } catch (ex) { - if (done) return; - done = true; - reject(self, ex); - } - } - - Promise$1.prototype['catch'] = function(onRejected) { - return this.then(null, onRejected); - }; - - Promise$1.prototype.then = function(onFulfilled, onRejected) { - // @ts-ignore - var prom = new this.constructor(noop$1); - - handle(this, new Handler(onFulfilled, onRejected, prom)); - return prom; - }; - - Promise$1.prototype['finally'] = finallyConstructor; - - Promise$1.all = function(arr) { - return new Promise$1(function(resolve, reject) { - if (!isArray$4(arr)) { - return reject(new TypeError('Promise.all accepts an array')); - } - - var args = Array.prototype.slice.call(arr); - if (args.length === 0) return resolve([]); - var remaining = args.length; - - function res(i, val) { - try { - if (val && (typeof val === 'object' || typeof val === 'function')) { - var then = val.then; - if (typeof then === 'function') { - then.call( - val, - function(val) { - res(i, val); - }, - reject - ); - return; - } - } - args[i] = val; - if (--remaining === 0) { - resolve(args); - } - } catch (ex) { - reject(ex); - } - } - - for (var i = 0; i < args.length; i++) { - res(i, args[i]); - } - }); - }; - - Promise$1.resolve = function(value) { - if (value && typeof value === 'object' && value.constructor === Promise$1) { - return value; - } - - return new Promise$1(function(resolve) { - resolve(value); - }); - }; - - Promise$1.reject = function(value) { - return new Promise$1(function(resolve, reject) { - reject(value); - }); - }; - - Promise$1.race = function(arr) { - return new Promise$1(function(resolve, reject) { - if (!isArray$4(arr)) { - return reject(new TypeError('Promise.race accepts an array')); - } - - for (var i = 0, len = arr.length; i < len; i++) { - Promise$1.resolve(arr[i]).then(resolve, reject); - } - }); - }; - - // Use polyfill for setImmediate for performance gains - Promise$1._immediateFn = - // @ts-ignore - (typeof setImmediate === 'function' && - function(fn) { - // @ts-ignore - setImmediate(fn); - }) || - function(fn) { - setTimeoutFunc(fn, 0); - }; - - Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { - if (typeof console !== 'undefined' && console) { - console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console - } - }; - - /** @suppress {undefinedVars} */ - var globalNS = (function() { - // the only reliable means to get the global object is - // `Function('return this')()` - // However, this causes CSP violations in Chrome apps. - if (typeof self !== 'undefined') { - return self; - } - if (typeof window !== 'undefined') { - return window; - } - if (typeof global !== 'undefined') { - return global; - } - throw new Error('unable to locate global object'); - })(); - - if (!('Promise' in globalNS)) { - globalNS['Promise'] = Promise$1; - } else if (!globalNS.Promise.prototype['finally']) { - globalNS.Promise.prototype['finally'] = finallyConstructor; - } - - var polyfill$e = /*#__PURE__*/Object.freeze({ - __proto__: null - }); - - var setAsap = createCommonjsModule(function (module) { - (function (thisVar, undefined$1) { - var main = (typeof window === 'object' && window) || (typeof commonjsGlobal === 'object' && commonjsGlobal) || - typeof self === 'object' && self || thisVar; - - var hasSetImmediate = typeof setImmediate === 'function'; - var hasNextTick = typeof process === 'object' && !!process && typeof process.nextTick === 'function'; - var index = 0; - - function getNewIndex() { - if (index === 9007199254740991) { - return 0; - } - return ++index; - } - - var setAsap = (function () { - var hiddenDiv, scriptEl, timeoutFn, callbacks; - - // Modern browsers, fastest async - if (main.MutationObserver) { - return function setAsap(callback) { - hiddenDiv = document.createElement("div"); - (new MutationObserver(function() { - callback(); - hiddenDiv = null; - })).observe(hiddenDiv, { attributes: true }); - hiddenDiv.setAttribute('i', '1'); - }; - - // Browsers that support postMessage - } else if (!hasSetImmediate && main.postMessage && !main.importScripts && main.addEventListener) { - - var MESSAGE_PREFIX = "com.setImmediate" + Math.random(); - callbacks = {}; - - var onGlobalMessage = function (event) { - if (event.source === main && event.data.indexOf(MESSAGE_PREFIX) === 0) { - var i = +event.data.split(':')[1]; - callbacks[i](); - delete callbacks[i]; - } - }; - - main.addEventListener("message", onGlobalMessage, false); - - return function setAsap(callback) { - var i = getNewIndex(); - callbacks[i] = callback; - main.postMessage(MESSAGE_PREFIX + ':' + i, "*"); - }; - - // IE browsers without postMessage - } else if (!hasSetImmediate && main.document && 'onreadystatechange' in document.createElement('script')) { - - return function setAsap(callback) { - scriptEl = document.createElement("script"); - scriptEl.onreadystatechange = function onreadystatechange() { - scriptEl.onreadystatechange = null; - scriptEl.parentNode.removeChild(scriptEl); - scriptEl = null; - callback(); - }; - document.body.appendChild(scriptEl); - }; - - // All other browsers and node - } else { - - timeoutFn = (hasSetImmediate && setImmediate) || (hasNextTick && process.nextTick) || setTimeout; - return function setAsap(callback) { - timeoutFn(callback); - }; - } - - })(); - - if ( module.exports) { - module.exports = setAsap; - } else if (typeof commonjsRequire !== 'undefined' && commonjsRequire.amd) { - undefined$1(function () { - return setAsap; - }); - } else { - main.setAsap = setAsap; - } - })(commonjsGlobal); - }); - - var performanceNow = createCommonjsModule(function (module) { - // Generated by CoffeeScript 1.12.2 - (function() { - var getNanoSeconds, hrtime, loadTime, moduleLoadTime, nodeLoadTime, upTime; - - if ((typeof performance !== "undefined" && performance !== null) && performance.now) { - module.exports = function() { - return performance.now(); - }; - } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) { - module.exports = function() { - return (getNanoSeconds() - nodeLoadTime) / 1e6; - }; - hrtime = process.hrtime; - getNanoSeconds = function() { - var hr; - hr = hrtime(); - return hr[0] * 1e9 + hr[1]; - }; - moduleLoadTime = getNanoSeconds(); - upTime = process.uptime() * 1e9; - nodeLoadTime = moduleLoadTime - upTime; - } else if (Date.now) { - module.exports = function() { - return Date.now() - loadTime; - }; - loadTime = Date.now(); - } else { - module.exports = function() { - return new Date().getTime() - loadTime; - }; - loadTime = new Date().getTime(); - } - - }).call(commonjsGlobal); - - - }); - - var root = typeof window === 'undefined' ? commonjsGlobal : window - , vendors = ['moz', 'webkit'] - , suffix = 'AnimationFrame' - , raf = root['request' + suffix] - , caf = root['cancel' + suffix] || root['cancelRequest' + suffix]; - - for(var i = 0; !raf && i < vendors.length; i++) { - raf = root[vendors[i] + 'Request' + suffix]; - caf = root[vendors[i] + 'Cancel' + suffix] - || root[vendors[i] + 'CancelRequest' + suffix]; - } - - // Some versions of FF have rAF but not cAF - if(!raf || !caf) { - var last = 0 - , id$2 = 0 - , queue = [] - , frameDuration = 1000 / 60; - - raf = function(callback) { - if(queue.length === 0) { - var _now = performanceNow() - , next = Math.max(0, frameDuration - (_now - last)); - last = next + _now; - setTimeout(function() { - var cp = queue.slice(0); - // Clear queue here to prevent - // callbacks from appending listeners - // to the current frame's queue - queue.length = 0; - for(var i = 0; i < cp.length; i++) { - if(!cp[i].cancelled) { - try{ - cp[i].callback(last); - } catch(e) { - setTimeout(function() { throw e }, 0); - } - } - } - }, Math.round(next)); - } - queue.push({ - handle: ++id$2, - callback: callback, - cancelled: false - }); - return id$2 - }; - - caf = function(handle) { - for(var i = 0; i < queue.length; i++) { - if(queue[i].handle === handle) { - queue[i].cancelled = true; - } - } - }; - } - - var raf_1 = function(fn) { - // Wrap in a new function to prevent - // `cancel` potentially being assigned - // to the native rAF function - return raf.call(root, fn) - }; - var cancel = function() { - caf.apply(root, arguments); - }; - var polyfill$f = function(object) { - if (!object) { - object = root; - } - object.requestAnimationFrame = raf; - object.cancelAnimationFrame = caf; - }; - raf_1.cancel = cancel; - raf_1.polyfill = polyfill$f; - - var global$1 = (function(self) { - return self - // eslint-disable-next-line no-invalid-this - })(typeof self !== 'undefined' ? self : undefined); - var support = { - searchParams: 'URLSearchParams' in global$1, - iterable: 'Symbol' in global$1 && 'iterator' in Symbol, - blob: - 'FileReader' in global$1 && - 'Blob' in global$1 && - (function() { - try { - new Blob(); - return true - } catch (e) { - return false - } - })(), - formData: 'FormData' in global$1, - arrayBuffer: 'ArrayBuffer' in global$1 - }; - - function isDataView(obj) { - return obj && DataView.prototype.isPrototypeOf(obj) - } - - if (support.arrayBuffer) { - var viewClasses = [ - '[object Int8Array]', - '[object Uint8Array]', - '[object Uint8ClampedArray]', - '[object Int16Array]', - '[object Uint16Array]', - '[object Int32Array]', - '[object Uint32Array]', - '[object Float32Array]', - '[object Float64Array]' - ]; - - var isArrayBufferView = - ArrayBuffer.isView || - function(obj) { - return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 - }; - } - - function normalizeName(name) { - if (typeof name !== 'string') { - name = String(name); - } - if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') { - throw new TypeError('Invalid character in header field name') - } - return name.toLowerCase() - } - - function normalizeValue(value) { - if (typeof value !== 'string') { - value = String(value); - } - return value - } - - // Build a destructive iterator for the value list - function iteratorFor(items) { - var iterator = { - next: function() { - var value = items.shift(); - return {done: value === undefined, value: value} - } - }; - - if (support.iterable) { - iterator[Symbol.iterator] = function() { - return iterator - }; - } - - return iterator - } - - function Headers(headers) { - this.map = {}; - - if (headers instanceof Headers) { - headers.forEach(function(value, name) { - this.append(name, value); - }, this); - } else if (Array.isArray(headers)) { - headers.forEach(function(header) { - this.append(header[0], header[1]); - }, this); - } else if (headers) { - Object.getOwnPropertyNames(headers).forEach(function(name) { - this.append(name, headers[name]); - }, this); - } - } - - Headers.prototype.append = function(name, value) { - name = normalizeName(name); - value = normalizeValue(value); - var oldValue = this.map[name]; - this.map[name] = oldValue ? oldValue + ', ' + value : value; - }; - - Headers.prototype['delete'] = function(name) { - delete this.map[normalizeName(name)]; - }; - - Headers.prototype.get = function(name) { - name = normalizeName(name); - return this.has(name) ? this.map[name] : null - }; - - Headers.prototype.has = function(name) { - return this.map.hasOwnProperty(normalizeName(name)) - }; - - Headers.prototype.set = function(name, value) { - this.map[normalizeName(name)] = normalizeValue(value); - }; - - Headers.prototype.forEach = function(callback, thisArg) { - for (var name in this.map) { - if (this.map.hasOwnProperty(name)) { - callback.call(thisArg, this.map[name], name, this); - } - } - }; - - Headers.prototype.keys = function() { - var items = []; - this.forEach(function(value, name) { - items.push(name); - }); - return iteratorFor(items) - }; - - Headers.prototype.values = function() { - var items = []; - this.forEach(function(value) { - items.push(value); - }); - return iteratorFor(items) - }; - - Headers.prototype.entries = function() { - var items = []; - this.forEach(function(value, name) { - items.push([name, value]); - }); - return iteratorFor(items) - }; - - if (support.iterable) { - Headers.prototype[Symbol.iterator] = Headers.prototype.entries; - } - - function consumed(body) { - if (body.bodyUsed) { - return Promise.reject(new TypeError('Already read')) - } - body.bodyUsed = true; - } - - function fileReaderReady(reader) { - return new Promise(function(resolve, reject) { - reader.onload = function() { - resolve(reader.result); - }; - reader.onerror = function() { - reject(reader.error); - }; - }) - } - - function readBlobAsArrayBuffer(blob) { - var reader = new FileReader(); - var promise = fileReaderReady(reader); - reader.readAsArrayBuffer(blob); - return promise - } - - function readBlobAsText(blob) { - var reader = new FileReader(); - var promise = fileReaderReady(reader); - reader.readAsText(blob); - return promise - } - - function readArrayBufferAsText(buf) { - var view = new Uint8Array(buf); - var chars = new Array(view.length); - - for (var i = 0; i < view.length; i++) { - chars[i] = String.fromCharCode(view[i]); - } - return chars.join('') - } - - function bufferClone(buf) { - if (buf.slice) { - return buf.slice(0) - } else { - var view = new Uint8Array(buf.byteLength); - view.set(new Uint8Array(buf)); - return view.buffer - } - } - - function Body() { - this.bodyUsed = false; - - this._initBody = function(body) { - /* - fetch-mock wraps the Response object in an ES6 Proxy to - provide useful test harness features such as flush. However, on - ES5 browsers without fetch or Proxy support pollyfills must be used; - the proxy-pollyfill is unable to proxy an attribute unless it exists - on the object before the Proxy is created. This change ensures - Response.bodyUsed exists on the instance, while maintaining the - semantic of setting Request.bodyUsed in the constructor before - _initBody is called. - */ - this.bodyUsed = this.bodyUsed; - this._bodyInit = body; - if (!body) { - this._bodyText = ''; - } else if (typeof body === 'string') { - this._bodyText = body; - } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { - this._bodyBlob = body; - } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { - this._bodyFormData = body; - } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { - this._bodyText = body.toString(); - } else if (support.arrayBuffer && support.blob && isDataView(body)) { - this._bodyArrayBuffer = bufferClone(body.buffer); - // IE 10-11 can't handle a DataView body. - this._bodyInit = new Blob([this._bodyArrayBuffer]); - } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { - this._bodyArrayBuffer = bufferClone(body); - } else { - this._bodyText = body = Object.prototype.toString.call(body); - } - - if (!this.headers.get('content-type')) { - if (typeof body === 'string') { - this.headers.set('content-type', 'text/plain;charset=UTF-8'); - } else if (this._bodyBlob && this._bodyBlob.type) { - this.headers.set('content-type', this._bodyBlob.type); - } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { - this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); - } - } - }; - - if (support.blob) { - this.blob = function() { - var rejected = consumed(this); - if (rejected) { - return rejected - } - - if (this._bodyBlob) { - return Promise.resolve(this._bodyBlob) - } else if (this._bodyArrayBuffer) { - return Promise.resolve(new Blob([this._bodyArrayBuffer])) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as blob') - } else { - return Promise.resolve(new Blob([this._bodyText])) - } - }; - - this.arrayBuffer = function() { - if (this._bodyArrayBuffer) { - return consumed(this) || Promise.resolve(this._bodyArrayBuffer) - } else { - return this.blob().then(readBlobAsArrayBuffer) - } - }; - } - - this.text = function() { - var rejected = consumed(this); - if (rejected) { - return rejected - } - - if (this._bodyBlob) { - return readBlobAsText(this._bodyBlob) - } else if (this._bodyArrayBuffer) { - return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as text') - } else { - return Promise.resolve(this._bodyText) - } - }; - - if (support.formData) { - this.formData = function() { - return this.text().then(decode) - }; - } - - this.json = function() { - return this.text().then(JSON.parse) - }; - - return this - } - - // HTTP methods whose capitalization should be normalized - var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; - - function normalizeMethod(method) { - var upcased = method.toUpperCase(); - return methods.indexOf(upcased) > -1 ? upcased : method - } - - function Request(input, options) { - options = options || {}; - var body = options.body; - - if (input instanceof Request) { - if (input.bodyUsed) { - throw new TypeError('Already read') - } - this.url = input.url; - this.credentials = input.credentials; - if (!options.headers) { - this.headers = new Headers(input.headers); - } - this.method = input.method; - this.mode = input.mode; - this.signal = input.signal; - if (!body && input._bodyInit != null) { - body = input._bodyInit; - input.bodyUsed = true; - } - } else { - this.url = String(input); - } - - this.credentials = options.credentials || this.credentials || 'same-origin'; - if (options.headers || !this.headers) { - this.headers = new Headers(options.headers); - } - this.method = normalizeMethod(options.method || this.method || 'GET'); - this.mode = options.mode || this.mode || null; - this.signal = options.signal || this.signal; - this.referrer = null; - - if ((this.method === 'GET' || this.method === 'HEAD') && body) { - throw new TypeError('Body not allowed for GET or HEAD requests') - } - this._initBody(body); - - if (this.method === 'GET' || this.method === 'HEAD') { - if (options.cache === 'no-store' || options.cache === 'no-cache') { - // Search for a '_' parameter in the query string - var reParamSearch = /([?&])_=[^&]*/; - if (reParamSearch.test(this.url)) { - // If it already exists then set the value with the current time - this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime()); - } else { - // Otherwise add a new '_' parameter to the end with the current time - var reQueryString = /\?/; - this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime(); - } - } - } - } - - Request.prototype.clone = function() { - return new Request(this, {body: this._bodyInit}) - }; - - function decode(body) { - var form = new FormData(); - body - .trim() - .split('&') - .forEach(function(bytes) { - if (bytes) { - var split = bytes.split('='); - var name = split.shift().replace(/\+/g, ' '); - var value = split.join('=').replace(/\+/g, ' '); - form.append(decodeURIComponent(name), decodeURIComponent(value)); - } - }); - return form - } - - function parseHeaders(rawHeaders) { - var headers = new Headers(); - // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space - // https://tools.ietf.org/html/rfc7230#section-3.2 - var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); - preProcessedHeaders.split(/\r?\n/).forEach(function(line) { - var parts = line.split(':'); - var key = parts.shift().trim(); - if (key) { - var value = parts.join(':').trim(); - headers.append(key, value); - } - }); - return headers - } - - Body.call(Request.prototype); - - function Response(bodyInit, options) { - if (!options) { - options = {}; - } - - this.type = 'default'; - this.status = options.status === undefined ? 200 : options.status; - this.ok = this.status >= 200 && this.status < 300; - this.statusText = 'statusText' in options ? options.statusText : ''; - this.headers = new Headers(options.headers); - this.url = options.url || ''; - this._initBody(bodyInit); - } - - Body.call(Response.prototype); - - Response.prototype.clone = function() { - return new Response(this._bodyInit, { - status: this.status, - statusText: this.statusText, - headers: new Headers(this.headers), - url: this.url - }) - }; - - Response.error = function() { - var response = new Response(null, {status: 0, statusText: ''}); - response.type = 'error'; - return response - }; - - var redirectStatuses = [301, 302, 303, 307, 308]; - - Response.redirect = function(url, status) { - if (redirectStatuses.indexOf(status) === -1) { - throw new RangeError('Invalid status code') - } - - return new Response(null, {status: status, headers: {location: url}}) - }; - - var DOMException$1 = global$1.DOMException; - - if (typeof DOMException$1 !== 'function') { - DOMException$1 = function(message, name) { - this.message = message; - this.name = name; - var error = Error(message); - this.stack = error.stack; - }; - DOMException$1.prototype = Object.create(Error.prototype); - DOMException$1.prototype.constructor = DOMException$1; - } - - function fetch$1(input, init) { - return new Promise(function(resolve, reject) { - var request = new Request(input, init); - - if (request.signal && request.signal.aborted) { - return reject(new DOMException$1('Aborted', 'AbortError')) - } - - var xhr = new XMLHttpRequest(); - - function abortXhr() { - xhr.abort(); - } - - xhr.onload = function() { - var options = { - status: xhr.status, - statusText: xhr.statusText, - headers: parseHeaders(xhr.getAllResponseHeaders() || '') - }; - options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); - var body = 'response' in xhr ? xhr.response : xhr.responseText; - setTimeout(function() { - resolve(new Response(body, options)); - }, 0); - }; - - xhr.onerror = function() { - setTimeout(function() { - reject(new TypeError('Network request failed')); - }, 0); - }; - - xhr.ontimeout = function() { - setTimeout(function() { - reject(new TypeError('Network request failed')); - }, 0); - }; - - xhr.onabort = function() { - setTimeout(function() { - reject(new DOMException$1('Aborted', 'AbortError')); - }, 0); - }; - - function fixUrl(url) { - try { - return url === '' && global$1.location.href ? global$1.location.href : url - } catch (e) { - return url - } - } - - xhr.open(request.method, fixUrl(request.url), true); - - if (request.credentials === 'include') { - xhr.withCredentials = true; - } else if (request.credentials === 'omit') { - xhr.withCredentials = false; - } - - if ('responseType' in xhr) { - if (support.blob) { - xhr.responseType = 'blob'; - } else if ( - support.arrayBuffer && - request.headers.get('Content-Type') && - request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1 - ) { - xhr.responseType = 'arraybuffer'; - } - } - - request.headers.forEach(function(value, name) { - xhr.setRequestHeader(name, value); - }); - - if (request.signal) { - request.signal.addEventListener('abort', abortXhr); - - xhr.onreadystatechange = function() { - // DONE (success or failure) - if (xhr.readyState === 4) { - request.signal.removeEventListener('abort', abortXhr); - } - }; - } - - xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); - }) - } - - fetch$1.polyfill = true; - - if (!global$1.fetch) { - global$1.fetch = fetch$1; - global$1.Headers = Headers; - global$1.Request = Request; - global$1.Response = Response; - } - - var lib = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - - - - - - - - - if (!window.Set) { - window.Set = es6Set; - } - if (!window.Map) { - window.Map = es6Map; - } - if (!window.Promise) { - window.Promise = polyfill$e; - window.Promise._immediateFn = setAsap; - } - if (!Array.prototype.find) { - array_prototype_find.shim(); - } - if (!Array.prototype.findIndex) { - array_prototype_findindex.shim(); - } - if (!Array.from) { - array_from.shim(); - } - if (!Object.values) { - object_values.shim(); - } - if (!Object.assign) { - object_assign.shim(); - } - if (!window.requestAnimationFrame || !window.cancelAnimationFrame) { - window.requestAnimationFrame = raf_1; - window.cancelAnimationFrame = raf_1.cancel; - } - - var finalFetch = window.fetch; - var finalPromise = window.Promise; - window.fetch = function (input, init) { - try { - return finalFetch(input, init); - } - catch (error) { - return new finalPromise(function (_, reject) { return reject(error); }); - } - }; - }); - - var $Math$2 = GetIntrinsic('%Math%'); - - var $floor$1 = $Math$2.floor; - var $abs$1 = $Math$2.abs; - - - - - // https://www.ecma-international.org/ecma-262/6.0/#sec-isinteger - - var IsInteger = function IsInteger(argument) { - if (typeof argument !== 'number' || _isNaN(argument) || !_isFinite(argument)) { - return false; - } - var abs = $abs$1(argument); - return $floor$1(abs) === abs; - }; - - var ArrayPush = callBound('Array.prototype.push'); - var StringFromCharCodeSpread = callBind.apply(String.fromCharCode, null); - - var implementation$8 = function fromCodePoint(_ /* fromCodePoint.length is 1 */) { - var MAX_SIZE = 0x4000; - var codeUnits = []; - var highSurrogate; - var lowSurrogate; - var index = -1; - var length = arguments.length; - if (!length) { - return ''; - } - var result = ''; - while (++index < length) { - var codePoint = ToNumber$1(arguments[index]); - if ( - !IsInteger(codePoint) || - codePoint < 0 || codePoint > 0x10FFFF // not a valid Unicode code point - ) { - throw RangeError('Invalid code point: ' + codePoint); - } - if (codePoint <= 0xFFFF) { // BMP code point - ArrayPush(codeUnits, codePoint); - } else { // Astral code point; split in surrogate halves - // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae - codePoint -= 0x10000; - highSurrogate = (codePoint >> 10) + 0xD800; - lowSurrogate = (codePoint % 0x400) + 0xDC00; - ArrayPush(codeUnits, highSurrogate, lowSurrogate); - } - if (index + 1 == length || codeUnits.length > MAX_SIZE) { - result += StringFromCharCodeSpread(codeUnits); - codeUnits.length = 0; - } - } - return result; - }; - - var polyfill$g = function getPolyfill() { - return String.fromCodePoint || implementation$8; - }; - - var shim$d = function shimFromCodePoint() { - var polyfill = polyfill$g(); - - if (String.fromCodePoint !== polyfill) { - defineProperties_1(String, { fromCodePoint: polyfill }); - } - - return polyfill; - }; - - /*! https://mths.be/fromcodepoint v1.0.0 by @mathias */ - - shim$d(); - - (function (factory) { - - factory(); - }((function () { - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - return Constructor; - } - - function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); - } - - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - if (superClass) _setPrototypeOf(subClass, superClass); - } - - function _getPrototypeOf(o) { - _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }; - return _getPrototypeOf(o); - } - - function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - - return _setPrototypeOf(o, p); - } - - function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - - return self; - } - - function _possibleConstructorReturn(self, call) { - if (call && (typeof call === "object" || typeof call === "function")) { - return call; - } - - return _assertThisInitialized(self); - } - - function _superPropBase(object, property) { - while (!Object.prototype.hasOwnProperty.call(object, property)) { - object = _getPrototypeOf(object); - if (object === null) break; - } - - return object; - } - - function _get(target, property, receiver) { - if (typeof Reflect !== "undefined" && Reflect.get) { - _get = Reflect.get; - } else { - _get = function _get(target, property, receiver) { - var base = _superPropBase(target, property); - - if (!base) return; - var desc = Object.getOwnPropertyDescriptor(base, property); - - if (desc.get) { - return desc.get.call(receiver); - } - - return desc.value; - }; - } - - return _get(target, property, receiver || target); - } - - var Emitter = - /*#__PURE__*/ - function () { - function Emitter() { - _classCallCheck(this, Emitter); - - Object.defineProperty(this, 'listeners', { - value: {}, - writable: true, - configurable: true - }); - } - - _createClass(Emitter, [{ - key: "addEventListener", - value: function addEventListener(type, callback) { - if (!(type in this.listeners)) { - this.listeners[type] = []; - } - - this.listeners[type].push(callback); - } - }, { - key: "removeEventListener", - value: function removeEventListener(type, callback) { - if (!(type in this.listeners)) { - return; - } - - var stack = this.listeners[type]; - - for (var i = 0, l = stack.length; i < l; i++) { - if (stack[i] === callback) { - stack.splice(i, 1); - return; - } - } - } - }, { - key: "dispatchEvent", - value: function dispatchEvent(event) { - var _this = this; - - if (!(event.type in this.listeners)) { - return; - } - - var debounce = function debounce(callback) { - setTimeout(function () { - return callback.call(_this, event); - }); - }; - - var stack = this.listeners[event.type]; - - for (var i = 0, l = stack.length; i < l; i++) { - debounce(stack[i]); - } - - return !event.defaultPrevented; - } - }]); - - return Emitter; - }(); - - var AbortSignal = - /*#__PURE__*/ - function (_Emitter) { - _inherits(AbortSignal, _Emitter); - - function AbortSignal() { - var _this2; - - _classCallCheck(this, AbortSignal); - - _this2 = _possibleConstructorReturn(this, _getPrototypeOf(AbortSignal).call(this)); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent - // constructor has failed to run, then "this.listeners" will still be undefined and then we call - // the parent constructor directly instead as a workaround. For general details, see babel bug: - // https://github.com/babel/babel/issues/3041 - // This hack was added as a fix for the issue described here: - // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042 - - if (!_this2.listeners) { - Emitter.call(_assertThisInitialized(_this2)); - } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and - // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl - - - Object.defineProperty(_assertThisInitialized(_this2), 'aborted', { - value: false, - writable: true, - configurable: true - }); - Object.defineProperty(_assertThisInitialized(_this2), 'onabort', { - value: null, - writable: true, - configurable: true - }); - return _this2; - } - - _createClass(AbortSignal, [{ - key: "toString", - value: function toString() { - return '[object AbortSignal]'; - } - }, { - key: "dispatchEvent", - value: function dispatchEvent(event) { - if (event.type === 'abort') { - this.aborted = true; - - if (typeof this.onabort === 'function') { - this.onabort.call(this, event); - } - } - - _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event); - } - }]); - - return AbortSignal; - }(Emitter); - var AbortController = - /*#__PURE__*/ - function () { - function AbortController() { - _classCallCheck(this, AbortController); - - // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and - // we want Object.keys(new AbortController()) to be [] for compat with the native impl - Object.defineProperty(this, 'signal', { - value: new AbortSignal(), - writable: true, - configurable: true - }); - } - - _createClass(AbortController, [{ - key: "abort", - value: function abort() { - var event; - - try { - event = new Event('abort'); - } catch (e) { - if (typeof document !== 'undefined') { - if (!document.createEvent) { - // For Internet Explorer 8: - event = document.createEventObject(); - event.type = 'abort'; - } else { - // For Internet Explorer 11: - event = document.createEvent('Event'); - event.initEvent('abort', false, false); - } - } else { - // Fallback where document isn't available: - event = { - type: 'abort', - bubbles: false, - cancelable: false - }; - } - } - - this.signal.dispatchEvent(event); - } - }, { - key: "toString", - value: function toString() { - return '[object AbortController]'; - } - }]); - - return AbortController; - }(); - - if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { - // These are necessary to make sure that we get correct output for: - // Object.prototype.toString.call(new AbortController()) - AbortController.prototype[Symbol.toStringTag] = 'AbortController'; - AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal'; - } - - function polyfillNeeded(self) { - if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { - console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill'); - return true; - } // Note that the "unfetch" minimal fetch polyfill defines fetch() without - // defining window.Request, and this polyfill need to work on top of unfetch - // so the below feature detection needs the !self.AbortController part. - // The Request.prototype check is also needed because Safari versions 11.1.2 - // up to and including 12.1.x has a window.AbortController present but still - // does NOT correctly implement abortable fetch: - // https://bugs.webkit.org/show_bug.cgi?id=174980#c2 - - - return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController; - } - - /** - * Note: the "fetch.Request" default value is available for fetch imported from - * the "node-fetch" package and not in browsers. This is OK since browsers - * will be importing umd-polyfill.js from that path "self" is passed the - * decorator so the default value will not be used (because browsers that define - * fetch also has Request). One quirky setup where self.fetch exists but - * self.Request does not is when the "unfetch" minimal fetch polyfill is used - * on top of IE11; for this case the browser will try to use the fetch.Request - * default value which in turn will be undefined but then then "if (Request)" - * will ensure that you get a patched fetch but still no Request (as expected). - * @param {fetch, Request = fetch.Request} - * @returns {fetch: abortableFetch, Request: AbortableRequest} - */ - - function abortableFetchDecorator(patchTargets) { - if ('function' === typeof patchTargets) { - patchTargets = { - fetch: patchTargets - }; - } - - var _patchTargets = patchTargets, - fetch = _patchTargets.fetch, - _patchTargets$Request = _patchTargets.Request, - NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request, - NativeAbortController = _patchTargets.AbortController, - _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL, - __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE; - - if (!polyfillNeeded({ - fetch: fetch, - Request: NativeRequest, - AbortController: NativeAbortController, - __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL - })) { - return { - fetch: fetch, - Request: Request - }; - } - - var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without - // defining window.Request, and this polyfill need to work on top of unfetch - // hence we only patch it if it's available. Also we don't patch it if signal - // is already available on the Request prototype because in this case support - // is present and the patching below can cause a crash since it assigns to - // request.signal which is technically a read-only property. This latter error - // happens when you run the main5.js node-fetch example in the repo - // "abortcontroller-polyfill-examples". The exact error is: - // request.signal = init.signal; - // ^ - // TypeError: Cannot set property signal of # which has only a getter - - if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { - Request = function Request(input, init) { - var signal; - - if (init && init.signal) { - signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has - // been installed because if we're running on top of a browser with a - // working native AbortController (i.e. the polyfill was installed due to - // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our - // fake AbortSignal to the native fetch will trigger: - // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal. - - delete init.signal; - } - - var request = new NativeRequest(input, init); - - if (signal) { - Object.defineProperty(request, 'signal', { - writable: false, - enumerable: false, - configurable: true, - value: signal - }); - } - - return request; - }; - - Request.prototype = NativeRequest.prototype; - } - - var realFetch = fetch; - - var abortableFetch = function abortableFetch(input, init) { - var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined; - - if (signal) { - var abortError; - - try { - abortError = new DOMException('Aborted', 'AbortError'); - } catch (err) { - // IE 11 does not support calling the DOMException constructor, use a - // regular error object on it instead. - abortError = new Error('Aborted'); - abortError.name = 'AbortError'; - } // Return early if already aborted, thus avoiding making an HTTP request - - - if (signal.aborted) { - return Promise.reject(abortError); - } // Turn an event into a promise, reject it once `abort` is dispatched - - - var cancellation = new Promise(function (_, reject) { - signal.addEventListener('abort', function () { - return reject(abortError); - }, { - once: true - }); - }); - - if (init && init.signal) { - // Never pass .signal to the native implementation when the polyfill has - // been installed because if we're running on top of a browser with a - // working native AbortController (i.e. the polyfill was installed due to - // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our - // fake AbortSignal to the native fetch will trigger: - // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal. - delete init.signal; - } // Return the fastest promise (don't need to wait for request to finish) - - - return Promise.race([cancellation, realFetch(input, init)]); - } - - return realFetch(input, init); - }; - - return { - fetch: abortableFetch, - Request: Request - }; - } - - (function (self) { - - if (!polyfillNeeded(self)) { - return; - } - - if (!self.fetch) { - console.warn('fetch() is not available, cannot install abortcontroller-polyfill'); - return; - } - - var _abortableFetch = abortableFetchDecorator(self), - fetch = _abortableFetch.fetch, - Request = _abortableFetch.Request; - - self.fetch = fetch; - self.Request = Request; - Object.defineProperty(self, 'AbortController', { - writable: true, - enumerable: false, - configurable: true, - value: AbortController - }); - Object.defineProperty(self, 'AbortSignal', { - writable: true, - enumerable: false, - configurable: true, - value: AbortSignal - }); - })(typeof self !== 'undefined' ? self : commonjsGlobal); - - }))); - - function actionAddEntity(way) { - return function(graph) { - return graph.replace(way); - }; - } - - /* - Order the nodes of a way in reverse order and reverse any direction dependent tags - other than `oneway`. (We assume that correcting a backwards oneway is the primary - reason for reversing a way.) - - In addition, numeric-valued `incline` tags are negated. - - The JOSM implementation was used as a guide, but transformations that were of unclear benefit - or adjusted tags that don't seem to be used in practice were omitted. - - References: - http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right - http://wiki.openstreetmap.org/wiki/Key:direction#Steps - http://wiki.openstreetmap.org/wiki/Key:incline - http://wiki.openstreetmap.org/wiki/Route#Members - http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java - http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop - http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area - */ - function actionReverse(entityID, options) { - var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/; - var numeric = /^([+\-]?)(?=[\d.])/; - var directionKey = /direction$/; - var turn_lanes = /^turn:lanes:?/; - var keyReplacements = [ - [/:right$/, ':left'], - [/:left$/, ':right'], - [/:forward$/, ':backward'], - [/:backward$/, ':forward'], - [/:right:/, ':left:'], - [/:left:/, ':right:'], - [/:forward:/, ':backward:'], - [/:backward:/, ':forward:'] - ]; - var valueReplacements = { - left: 'right', - right: 'left', - up: 'down', - down: 'up', - forward: 'backward', - backward: 'forward', - forwards: 'backward', - backwards: 'forward', - }; - var roleReplacements = { - forward: 'backward', - backward: 'forward', - forwards: 'backward', - backwards: 'forward' - }; - var onewayReplacements = { - yes: '-1', - '1': '-1', - '-1': 'yes' - }; - - var compassReplacements = { - N: 'S', - NNE: 'SSW', - NE: 'SW', - ENE: 'WSW', - E: 'W', - ESE: 'WNW', - SE: 'NW', - SSE: 'NNW', - S: 'N', - SSW: 'NNE', - SW: 'NE', - WSW: 'ENE', - W: 'E', - WNW: 'ESE', - NW: 'SE', - NNW: 'SSE' - }; - - - function reverseKey(key) { - for (var i = 0; i < keyReplacements.length; ++i) { - var replacement = keyReplacements[i]; - if (replacement[0].test(key)) { - return key.replace(replacement[0], replacement[1]); - } - } - return key; - } - - - function reverseValue(key, value, includeAbsolute) { - if (ignoreKey.test(key)) return value; - - // Turn lanes are left/right to key (not way) direction - #5674 - if (turn_lanes.test(key)) { - return value; - - } else if (key === 'incline' && numeric.test(value)) { - return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; }); - - } else if (options && options.reverseOneway && key === 'oneway') { - return onewayReplacements[value] || value; - - } else if (includeAbsolute && directionKey.test(key)) { - if (compassReplacements[value]) return compassReplacements[value]; - - var degrees = parseFloat(value); - if (typeof degrees === 'number' && !isNaN(degrees)) { - if (degrees < 180) { - degrees += 180; - } else { - degrees -= 180; - } - return degrees.toString(); - } - } - - return valueReplacements[value] || value; - } - - - // Reverse the direction of tags attached to the nodes - #3076 - function reverseNodeTags(graph, nodeIDs) { - for (var i = 0; i < nodeIDs.length; i++) { - var node = graph.hasEntity(nodeIDs[i]); - if (!node || !Object.keys(node.tags).length) continue; - - var tags = {}; - for (var key in node.tags) { - tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID); - } - graph = graph.replace(node.update({tags: tags})); - } - return graph; - } - - - function reverseWay(graph, way) { - var nodes = way.nodes.slice().reverse(); - var tags = {}; - var role; - - for (var key in way.tags) { - tags[reverseKey(key)] = reverseValue(key, way.tags[key]); - } - - graph.parentRelations(way).forEach(function(relation) { - relation.members.forEach(function(member, index) { - if (member.id === way.id && (role = roleReplacements[member.role])) { - relation = relation.updateMember({role: role}, index); - graph = graph.replace(relation); - } - }); - }); - - // Reverse any associated directions on nodes on the way and then replace - // the way itself with the reversed node ids and updated way tags - return reverseNodeTags(graph, nodes) - .replace(way.update({nodes: nodes, tags: tags})); - } - - - var action = function(graph) { - var entity = graph.entity(entityID); - if (entity.type === 'way') { - return reverseWay(graph, entity); - } - return reverseNodeTags(graph, [entityID]); - }; - - action.disabled = function(graph) { - var entity = graph.hasEntity(entityID); - if (!entity || entity.type === 'way') return false; - - for (var key in entity.tags) { - var value = entity.tags[key]; - if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) { - return false; - } - } - return 'nondirectional_node'; - }; - - action.entityID = function() { - return entityID; - }; - - return action; - } - - function osmIsInterestingTag(key) { - return key !== 'attribution' && - key !== 'created_by' && - key !== 'source' && - key !== 'odbl' && - key.indexOf('source:') !== 0 && - key.indexOf('source_ref') !== 0 && // purposely exclude colon - key.indexOf('tiger:') !== 0; - } - - var osmAreaKeys = {}; - function osmSetAreaKeys(value) { - osmAreaKeys = value; - } - - // returns an object with the tag from `tags` that implies an area geometry, if any - function osmTagSuggestingArea(tags) { - if (tags.area === 'yes') return { area: 'yes' }; - if (tags.area === 'no') return null; - - // `highway` and `railway` are typically linear features, but there - // are a few exceptions that should be treated as areas, even in the - // absence of a proper `area=yes` or `areaKeys` tag.. see #4194 - var lineKeys = { - highway: { - rest_area: true, - services: true - }, - railway: { - roundhouse: true, - station: true, - traverser: true, - turntable: true, - wash: true - } - }; - var returnTags = {}; - for (var key in tags) { - if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) { - returnTags[key] = tags[key]; - return returnTags; - } - if (key in lineKeys && tags[key] in lineKeys[key]) { - returnTags[key] = tags[key]; - return returnTags; - } - } - return null; - } - - // Tags that indicate a node can be a standalone point - // e.g. { amenity: { bar: true, parking: true, ... } ... } - var osmPointTags = {}; - function osmSetPointTags(value) { - osmPointTags = value; - } - // Tags that indicate a node can be part of a way - // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... } - var osmVertexTags = {}; - function osmSetVertexTags(value) { - osmVertexTags = value; - } - - function osmNodeGeometriesForTags(nodeTags) { - var geometries = {}; - for (var key in nodeTags) { - if (osmPointTags[key] && - (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) { - geometries.point = true; - } - if (osmVertexTags[key] && - (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) { - geometries.vertex = true; - } - // break early if both are already supported - if (geometries.point && geometries.vertex) break; - } - return geometries; - } - - var osmOneWayTags = { - 'aerialway': { - 'chair_lift': true, - 'drag_lift': true, - 'j-bar': true, - 'magic_carpet': true, - 'mixed_lift': true, - 'platter': true, - 'rope_tow': true, - 't-bar': true, - 'zip_line': true - }, - 'highway': { - 'motorway': true - }, - 'junction': { - 'circular': true, - 'roundabout': true - }, - 'man_made': { - 'goods_conveyor': true, - 'piste:halfpipe': true - }, - 'piste:type': { - 'downhill': true, - 'sled': true, - 'yes': true - }, - 'waterway': { - 'canal': true, - 'ditch': true, - 'drain': true, - 'fish_pass': true, - 'river': true, - 'stream': true, - 'tidal_channel': true - } - }; - - // solid and smooth surfaces akin to the assumed default road surface in OSM - var osmPavedTags = { - 'surface': { - 'paved': true, - 'asphalt': true, - 'concrete': true, - 'concrete:lanes': true, - 'concrete:plates': true - }, - 'tracktype': { - 'grade1': true - } - }; - - // solid, if somewhat uncommon surfaces with a high range of smoothness - var osmSemipavedTags = { - 'surface': { - 'cobblestone': true, - 'cobblestone:flattened': true, - 'unhewn_cobblestone': true, - 'sett': true, - 'paving_stones': true, - 'metal': true, - 'wood': true - } - }; - - var osmRightSideIsInsideTags = { - 'natural': { - 'cliff': true, - 'coastline': 'coastline', - }, - 'barrier': { - 'retaining_wall': true, - 'kerb': true, - 'guard_rail': true, - 'city_wall': true, - }, - 'man_made': { - 'embankment': true - }, - 'waterway': { - 'weir': true - } - }; - - // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network - // (does not include `raceway`) - var osmRoutableHighwayTagValues = { - motorway: true, trunk: true, primary: true, secondary: true, tertiary: true, residential: true, - motorway_link: true, trunk_link: true, primary_link: true, secondary_link: true, tertiary_link: true, - unclassified: true, road: true, service: true, track: true, living_street: true, bus_guideway: true, - path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true - }; - // "highway" tag values that generally do not allow motor vehicles - var osmPathHighwayTagValues = { - path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true - }; - - // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned') - var osmRailwayTrackTagValues = { - rail: true, light_rail: true, tram: true, subway: true, - monorail: true, funicular: true, miniature: true, narrow_gauge: true, - disused: true, preserved: true - }; - - // "waterway" tag values for line features representing water flow - var osmFlowingWaterwayTagValues = { - canal: true, ditch: true, drain: true, fish_pass: true, river: true, stream: true, tidal_channel: true - }; - - // Adds floating point numbers with twice the normal precision. - // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and - // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) - // 305–363 (1997). - // Code adapted from GeographicLib by Charles F. F. Karney, - // http://geographiclib.sourceforge.net/ - - function adder() { - return new Adder; - } - - function Adder() { - this.reset(); - } - - Adder.prototype = { - constructor: Adder, - reset: function() { - this.s = // rounded value - this.t = 0; // exact error - }, - add: function(y) { - add(temp, y, this.t); - add(this, temp.s, this.s); - if (this.s) this.t += temp.t; - else this.s = temp.t; - }, - valueOf: function() { - return this.s; - } - }; - - var temp = new Adder; - - function add(adder, a, b) { - var x = adder.s = a + b, - bv = x - a, - av = x - bv; - adder.t = (a - av) + (b - bv); - } - - var epsilon = 1e-6; - var epsilon2 = 1e-12; - var pi = Math.PI; - var halfPi = pi / 2; - var quarterPi = pi / 4; - var tau = pi * 2; - - var degrees = 180 / pi; - var radians = pi / 180; - - var abs$2 = Math.abs; - var atan = Math.atan; - var atan2 = Math.atan2; - var cos = Math.cos; - var exp = Math.exp; - var log = Math.log; - var sin = Math.sin; - var sign$2 = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }; - var sqrt = Math.sqrt; - var tan = Math.tan; - - function acos(x) { - return x > 1 ? 0 : x < -1 ? pi : Math.acos(x); - } - - function asin(x) { - return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x); - } - - function noop$2() {} - - function streamGeometry(geometry, stream) { - if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { - streamGeometryType[geometry.type](geometry, stream); - } - } - - var streamObjectType = { - Feature: function(object, stream) { - streamGeometry(object.geometry, stream); - }, - FeatureCollection: function(object, stream) { - var features = object.features, i = -1, n = features.length; - while (++i < n) streamGeometry(features[i].geometry, stream); - } - }; - - var streamGeometryType = { - Sphere: function(object, stream) { - stream.sphere(); - }, - Point: function(object, stream) { - object = object.coordinates; - stream.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]); - }, - LineString: function(object, stream) { - streamLine(object.coordinates, stream, 0); - }, - MultiLineString: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) streamLine(coordinates[i], stream, 0); - }, - Polygon: function(object, stream) { - streamPolygon(object.coordinates, stream); - }, - MultiPolygon: function(object, stream) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) streamPolygon(coordinates[i], stream); - }, - GeometryCollection: function(object, stream) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) streamGeometry(geometries[i], stream); - } - }; - - function streamLine(coordinates, stream, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - stream.lineStart(); - while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); - stream.lineEnd(); - } - - function streamPolygon(coordinates, stream) { - var i = -1, n = coordinates.length; - stream.polygonStart(); - while (++i < n) streamLine(coordinates[i], stream, 1); - stream.polygonEnd(); - } - - function d3_geoStream(object, stream) { - if (object && streamObjectType.hasOwnProperty(object.type)) { - streamObjectType[object.type](object, stream); - } else { - streamGeometry(object, stream); - } - } - - var areaRingSum = adder(); - - var areaSum = adder(), - lambda00, - phi00, - lambda0, - cosPhi0, - sinPhi0; - - var areaStream = { - point: noop$2, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: function() { - areaRingSum.reset(); - areaStream.lineStart = areaRingStart; - areaStream.lineEnd = areaRingEnd; - }, - polygonEnd: function() { - var areaRing = +areaRingSum; - areaSum.add(areaRing < 0 ? tau + areaRing : areaRing); - this.lineStart = this.lineEnd = this.point = noop$2; - }, - sphere: function() { - areaSum.add(tau); - } - }; - - function areaRingStart() { - areaStream.point = areaPointFirst; - } - - function areaRingEnd() { - areaPoint(lambda00, phi00); - } - - function areaPointFirst(lambda, phi) { - areaStream.point = areaPoint; - lambda00 = lambda, phi00 = phi; - lambda *= radians, phi *= radians; - lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi); - } - - function areaPoint(lambda, phi) { - lambda *= radians, phi *= radians; - phi = phi / 2 + quarterPi; // half the angular distance from south pole - - // Spherical excess E for a spherical triangle with vertices: south pole, - // previous point, current point. Uses a formula derived from Cagnoli’s - // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). - var dLambda = lambda - lambda0, - sdLambda = dLambda >= 0 ? 1 : -1, - adLambda = sdLambda * dLambda, - cosPhi = cos(phi), - sinPhi = sin(phi), - k = sinPhi0 * sinPhi, - u = cosPhi0 * cosPhi + k * cos(adLambda), - v = k * sdLambda * sin(adLambda); - areaRingSum.add(atan2(v, u)); - - // Advance the previous points. - lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi; - } - - function d3_geoArea(object) { - areaSum.reset(); - d3_geoStream(object, areaStream); - return areaSum * 2; - } - - function spherical(cartesian) { - return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])]; - } - - function cartesian(spherical) { - var lambda = spherical[0], phi = spherical[1], cosPhi = cos(phi); - return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)]; - } - - function cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - - function cartesianCross(a, b) { - return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; - } - - // TODO return a - function cartesianAddInPlace(a, b) { - a[0] += b[0], a[1] += b[1], a[2] += b[2]; - } - - function cartesianScale(vector, k) { - return [vector[0] * k, vector[1] * k, vector[2] * k]; - } - - // TODO return d - function cartesianNormalizeInPlace(d) { - var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l, d[1] /= l, d[2] /= l; - } - - var lambda0$1, phi0, lambda1, phi1, // bounds - lambda2, // previous lambda-coordinate - lambda00$1, phi00$1, // first point - p0, // previous 3D point - deltaSum = adder(), - ranges, - range; - - var boundsStream = { - point: boundsPoint, - lineStart: boundsLineStart, - lineEnd: boundsLineEnd, - polygonStart: function() { - boundsStream.point = boundsRingPoint; - boundsStream.lineStart = boundsRingStart; - boundsStream.lineEnd = boundsRingEnd; - deltaSum.reset(); - areaStream.polygonStart(); - }, - polygonEnd: function() { - areaStream.polygonEnd(); - boundsStream.point = boundsPoint; - boundsStream.lineStart = boundsLineStart; - boundsStream.lineEnd = boundsLineEnd; - if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); - else if (deltaSum > epsilon) phi1 = 90; - else if (deltaSum < -epsilon) phi0 = -90; - range[0] = lambda0$1, range[1] = lambda1; - }, - sphere: function() { - lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); - } - }; - - function boundsPoint(lambda, phi) { - ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); - if (phi < phi0) phi0 = phi; - if (phi > phi1) phi1 = phi; - } - - function linePoint(lambda, phi) { - var p = cartesian([lambda * radians, phi * radians]); - if (p0) { - var normal = cartesianCross(p0, p), - equatorial = [normal[1], -normal[0], 0], - inflection = cartesianCross(equatorial, normal); - cartesianNormalizeInPlace(inflection); - inflection = spherical(inflection); - var delta = lambda - lambda2, - sign = delta > 0 ? 1 : -1, - lambdai = inflection[0] * degrees * sign, - phii, - antimeridian = abs$2(delta) > 180; - if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) { - phii = inflection[1] * degrees; - if (phii > phi1) phi1 = phii; - } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) { - phii = -inflection[1] * degrees; - if (phii < phi0) phi0 = phii; - } else { - if (phi < phi0) phi0 = phi; - if (phi > phi1) phi1 = phi; - } - if (antimeridian) { - if (lambda < lambda2) { - if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; - } else { - if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; - } - } else { - if (lambda1 >= lambda0$1) { - if (lambda < lambda0$1) lambda0$1 = lambda; - if (lambda > lambda1) lambda1 = lambda; - } else { - if (lambda > lambda2) { - if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; - } else { - if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; - } - } - } - } else { - ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); - } - if (phi < phi0) phi0 = phi; - if (phi > phi1) phi1 = phi; - p0 = p, lambda2 = lambda; - } - - function boundsLineStart() { - boundsStream.point = linePoint; - } - - function boundsLineEnd() { - range[0] = lambda0$1, range[1] = lambda1; - boundsStream.point = boundsPoint; - p0 = null; - } - - function boundsRingPoint(lambda, phi) { - if (p0) { - var delta = lambda - lambda2; - deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta); - } else { - lambda00$1 = lambda, phi00$1 = phi; - } - areaStream.point(lambda, phi); - linePoint(lambda, phi); - } - - function boundsRingStart() { - areaStream.lineStart(); - } - - function boundsRingEnd() { - boundsRingPoint(lambda00$1, phi00$1); - areaStream.lineEnd(); - if (abs$2(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180); - range[0] = lambda0$1, range[1] = lambda1; - p0 = null; - } - - // Finds the left-right distance between two longitudes. - // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want - // the distance between ±180° to be 360°. - function angle(lambda0, lambda1) { - return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1; - } - - function rangeCompare(a, b) { - return a[0] - b[0]; - } - - function rangeContains(range, x) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; - } - - function d3_geoBounds(feature) { - var i, n, a, b, merged, deltaMax, delta; - - phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity); - ranges = []; - d3_geoStream(feature, boundsStream); - - // First, sort ranges by their minimum longitudes. - if (n = ranges.length) { - ranges.sort(rangeCompare); - - // Then, merge any ranges that overlap. - for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) { - b = ranges[i]; - if (rangeContains(a, b[0]) || rangeContains(a, b[1])) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - - // Finally, find the largest gap between the merged ranges. - // The final bounding box will be the inverse of this gap. - for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) { - b = merged[i]; - if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; - } - } - - ranges = range = null; - - return lambda0$1 === Infinity || phi0 === Infinity - ? [[NaN, NaN], [NaN, NaN]] - : [[lambda0$1, phi0], [lambda1, phi1]]; - } - - var W0, W1, - X0, Y0, Z0, - X1, Y1, Z1, - X2, Y2, Z2, - lambda00$2, phi00$2, // first point - x0, y0, z0; // previous point - - var centroidStream = { - sphere: noop$2, - point: centroidPoint, - lineStart: centroidLineStart, - lineEnd: centroidLineEnd, - polygonStart: function() { - centroidStream.lineStart = centroidRingStart; - centroidStream.lineEnd = centroidRingEnd; - }, - polygonEnd: function() { - centroidStream.lineStart = centroidLineStart; - centroidStream.lineEnd = centroidLineEnd; - } - }; - - // Arithmetic mean of Cartesian vectors. - function centroidPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos(phi); - centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)); - } - - function centroidPointCartesian(x, y, z) { - ++W0; - X0 += (x - X0) / W0; - Y0 += (y - Y0) / W0; - Z0 += (z - Z0) / W0; - } - - function centroidLineStart() { - centroidStream.point = centroidLinePointFirst; - } - - function centroidLinePointFirst(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos(phi); - x0 = cosPhi * cos(lambda); - y0 = cosPhi * sin(lambda); - z0 = sin(phi); - centroidStream.point = centroidLinePoint; - centroidPointCartesian(x0, y0, z0); - } - - function centroidLinePoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos(phi), - x = cosPhi * cos(lambda), - y = cosPhi * sin(lambda), - z = sin(phi), - w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); - W1 += w; - X1 += w * (x0 + (x0 = x)); - Y1 += w * (y0 + (y0 = y)); - Z1 += w * (z0 + (z0 = z)); - centroidPointCartesian(x0, y0, z0); - } - - function centroidLineEnd() { - centroidStream.point = centroidPoint; - } - - // See J. E. Brock, The Inertia Tensor for a Spherical Triangle, - // J. Applied Mechanics 42, 239 (1975). - function centroidRingStart() { - centroidStream.point = centroidRingPointFirst; - } - - function centroidRingEnd() { - centroidRingPoint(lambda00$2, phi00$2); - centroidStream.point = centroidPoint; - } - - function centroidRingPointFirst(lambda, phi) { - lambda00$2 = lambda, phi00$2 = phi; - lambda *= radians, phi *= radians; - centroidStream.point = centroidRingPoint; - var cosPhi = cos(phi); - x0 = cosPhi * cos(lambda); - y0 = cosPhi * sin(lambda); - z0 = sin(phi); - centroidPointCartesian(x0, y0, z0); - } - - function centroidRingPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var cosPhi = cos(phi), - x = cosPhi * cos(lambda), - y = cosPhi * sin(lambda), - z = sin(phi), - cx = y0 * z - z0 * y, - cy = z0 * x - x0 * z, - cz = x0 * y - y0 * x, - m = sqrt(cx * cx + cy * cy + cz * cz), - w = asin(m), // line weight = angle - v = m && -w / m; // area weight multiplier - X2 += v * cx; - Y2 += v * cy; - Z2 += v * cz; - W1 += w; - X1 += w * (x0 + (x0 = x)); - Y1 += w * (y0 + (y0 = y)); - Z1 += w * (z0 + (z0 = z)); - centroidPointCartesian(x0, y0, z0); - } - - function d3_geoCentroid(object) { - W0 = W1 = - X0 = Y0 = Z0 = - X1 = Y1 = Z1 = - X2 = Y2 = Z2 = 0; - d3_geoStream(object, centroidStream); - - var x = X2, - y = Y2, - z = Z2, - m = x * x + y * y + z * z; - - // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid. - if (m < epsilon2) { - x = X1, y = Y1, z = Z1; - // If the feature has zero length, fall back to arithmetic mean of point vectors. - if (W1 < epsilon) x = X0, y = Y0, z = Z0; - m = x * x + y * y + z * z; - // If the feature still has an undefined ccentroid, then return. - if (m < epsilon2) return [NaN, NaN]; - } - - return [atan2(y, x) * degrees, asin(z / sqrt(m)) * degrees]; - } - - function compose(a, b) { - - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - - return compose; - } - - function rotationIdentity(lambda, phi) { - return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi]; - } - - rotationIdentity.invert = rotationIdentity; - - function rotateRadians(deltaLambda, deltaPhi, deltaGamma) { - return (deltaLambda %= tau) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) - : rotationLambda(deltaLambda)) - : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) - : rotationIdentity); - } - - function forwardRotationLambda(deltaLambda) { - return function(lambda, phi) { - return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi]; - }; - } - - function rotationLambda(deltaLambda) { - var rotation = forwardRotationLambda(deltaLambda); - rotation.invert = forwardRotationLambda(-deltaLambda); - return rotation; - } - - function rotationPhiGamma(deltaPhi, deltaGamma) { - var cosDeltaPhi = cos(deltaPhi), - sinDeltaPhi = sin(deltaPhi), - cosDeltaGamma = cos(deltaGamma), - sinDeltaGamma = sin(deltaGamma); - - function rotation(lambda, phi) { - var cosPhi = cos(phi), - x = cos(lambda) * cosPhi, - y = sin(lambda) * cosPhi, - z = sin(phi), - k = z * cosDeltaPhi + x * sinDeltaPhi; - return [ - atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), - asin(k * cosDeltaGamma + y * sinDeltaGamma) - ]; - } - - rotation.invert = function(lambda, phi) { - var cosPhi = cos(phi), - x = cos(lambda) * cosPhi, - y = sin(lambda) * cosPhi, - z = sin(phi), - k = z * cosDeltaGamma - y * sinDeltaGamma; - return [ - atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), - asin(k * cosDeltaPhi - x * sinDeltaPhi) - ]; - }; - - return rotation; - } - - function rotation(rotate) { - rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0); - - function forward(coordinates) { - coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians); - return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates; - } - - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians); - return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates; - }; - - return forward; - } - - // Generates a circle centered at [0°, 0°], with a given radius and precision. - function circleStream(stream, radius, delta, direction, t0, t1) { - if (!delta) return; - var cosRadius = cos(radius), - sinRadius = sin(radius), - step = direction * delta; - if (t0 == null) { - t0 = radius + direction * tau; - t1 = radius - step / 2; - } else { - t0 = circleRadius(cosRadius, t0); - t1 = circleRadius(cosRadius, t1); - if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau; - } - for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) { - point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]); - stream.point(point[0], point[1]); - } - } - - // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0]. - function circleRadius(cosRadius, point) { - point = cartesian(point), point[0] -= cosRadius; - cartesianNormalizeInPlace(point); - var radius = acos(-point[1]); - return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau; - } - - function clipBuffer() { - var lines = [], - line; - return { - point: function(x, y, m) { - line.push([x, y, m]); - }, - lineStart: function() { - lines.push(line = []); - }, - lineEnd: noop$2, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - }, - result: function() { - var result = lines; - lines = []; - line = null; - return result; - } - }; - } - - function pointEqual(a, b) { - return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon; - } - - function Intersection(point, points, other, entry) { - this.x = point; - this.z = points; - this.o = other; // another intersection - this.e = entry; // is an entry? - this.v = false; // visited - this.n = this.p = null; // next & previous - } - - // A generalized polygon clipping algorithm: given a polygon that has been cut - // into its visible line segments, and rejoins the segments by interpolating - // along the clip edge. - function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) { - var subject = [], - clip = [], - i, - n; - - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n], x; - - if (pointEqual(p0, p1)) { - if (!p0[2] && !p1[2]) { - stream.lineStart(); - for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]); - stream.lineEnd(); - return; - } - // handle degenerate cases by moving the point - p1[0] += 2 * epsilon; - } - - subject.push(x = new Intersection(p0, segment, null, true)); - clip.push(x.o = new Intersection(p0, null, x, false)); - subject.push(x = new Intersection(p1, segment, null, false)); - clip.push(x.o = new Intersection(p1, null, x, true)); - }); - - if (!subject.length) return; - - clip.sort(compareIntersection); - link(subject); - link(clip); - - for (i = 0, n = clip.length; i < n; ++i) { - clip[i].e = startInside = !startInside; - } - - var start = subject[0], - points, - point; - - while (1) { - // Find first unvisited intersection. - var current = start, - isSubject = true; - while (current.v) if ((current = current.n) === start) return; - points = current.z; - stream.lineStart(); - do { - current.v = current.o.v = true; - if (current.e) { - if (isSubject) { - for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.n.x, 1, stream); - } - current = current.n; - } else { - if (isSubject) { - points = current.p.z; - for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.p.x, -1, stream); - } - current = current.p; - } - current = current.o; - points = current.z; - isSubject = !isSubject; - } while (!current.v); - stream.lineEnd(); - } - } - - function link(array) { - if (!(n = array.length)) return; - var n, - i = 0, - a = array[0], - b; - while (++i < n) { - a.n = b = array[i]; - b.p = a; - a = b; - } - a.n = b = array[0]; - b.p = a; - } - - var sum = adder(); - - function longitude(point) { - if (abs$2(point[0]) <= pi) - return point[0]; - else - return sign$2(point[0]) * ((abs$2(point[0]) + pi) % tau - pi); - } - - function polygonContains(polygon, point) { - var lambda = longitude(point), - phi = point[1], - sinPhi = sin(phi), - normal = [sin(lambda), -cos(lambda), 0], - angle = 0, - winding = 0; - - sum.reset(); - - if (sinPhi === 1) phi = halfPi + epsilon; - else if (sinPhi === -1) phi = -halfPi - epsilon; - - for (var i = 0, n = polygon.length; i < n; ++i) { - if (!(m = (ring = polygon[i]).length)) continue; - var ring, - m, - point0 = ring[m - 1], - lambda0 = longitude(point0), - phi0 = point0[1] / 2 + quarterPi, - sinPhi0 = sin(phi0), - cosPhi0 = cos(phi0); - - for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) { - var point1 = ring[j], - lambda1 = longitude(point1), - phi1 = point1[1] / 2 + quarterPi, - sinPhi1 = sin(phi1), - cosPhi1 = cos(phi1), - delta = lambda1 - lambda0, - sign = delta >= 0 ? 1 : -1, - absDelta = sign * delta, - antimeridian = absDelta > pi, - k = sinPhi0 * sinPhi1; - - sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta))); - angle += antimeridian ? delta + sign * tau : delta; - - // Are the longitudes either side of the point’s meridian (lambda), - // and are the latitudes smaller than the parallel (phi)? - if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) { - var arc = cartesianCross(cartesian(point0), cartesian(point1)); - cartesianNormalizeInPlace(arc); - var intersection = cartesianCross(normal, arc); - cartesianNormalizeInPlace(intersection); - var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]); - if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) { - winding += antimeridian ^ delta >= 0 ? 1 : -1; - } - } - } - } - - // First, determine whether the South pole is inside or outside: - // - // It is inside if: - // * the polygon winds around it in a clockwise direction. - // * the polygon does not (cumulatively) wind around it, but has a negative - // (counter-clockwise) area. - // - // Second, count the (signed) number of times a segment crosses a lambda - // from the point to the South pole. If it is zero, then the point is the - // same side as the South pole. - - return (angle < -epsilon || angle < epsilon && sum < -epsilon) ^ (winding & 1); - } - - function d3_ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - - function d3_bisector(compare) { - if (compare.length === 1) compare = ascendingComparator(compare); - return { - left: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; - else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; - else lo = mid + 1; - } - return lo; - } - }; - } - - function ascendingComparator(f) { - return function(d, x) { - return d3_ascending(f(d), x); - }; - } - - var ascendingBisect = d3_bisector(d3_ascending); - var bisectRight = ascendingBisect.right; - - function d3_descending(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; - } - - function number(x) { - return x === null ? NaN : +x; - } - - function range$1(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; - } - - var e10 = Math.sqrt(50), - e5 = Math.sqrt(10), - e2 = Math.sqrt(2); - - function ticks(start, stop, count) { - var reverse, - i = -1, - n, - ticks, - step; - - stop = +stop, start = +start, count = +count; - if (start === stop && count > 0) return [start]; - if (reverse = stop < start) n = start, start = stop, stop = n; - if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; - - if (step > 0) { - start = Math.ceil(start / step); - stop = Math.floor(stop / step); - ticks = new Array(n = Math.ceil(stop - start + 1)); - while (++i < n) ticks[i] = (start + i) * step; - } else { - start = Math.floor(start * step); - stop = Math.ceil(stop * step); - ticks = new Array(n = Math.ceil(start - stop + 1)); - while (++i < n) ticks[i] = (start - i) / step; - } - - if (reverse) ticks.reverse(); - - return ticks; - } - - function tickIncrement(start, stop, count) { - var step = (stop - start) / Math.max(0, count), - power = Math.floor(Math.log(step) / Math.LN10), - error = step / Math.pow(10, power); - return power >= 0 - ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) - : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); - } - - function tickStep(start, stop, count) { - var step0 = Math.abs(stop - start) / Math.max(0, count), - step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), - error = step0 / step1; - if (error >= e10) step1 *= 10; - else if (error >= e5) step1 *= 5; - else if (error >= e2) step1 *= 2; - return stop < start ? -step1 : step1; - } - - function threshold(values, p, valueof) { - if (valueof == null) valueof = number; - if (!(n = values.length)) return; - if ((p = +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 d3_median(values, valueof) { - var n = values.length, - i = -1, - value, - numbers = []; - - if (valueof == null) { - while (++i < n) { - if (!isNaN(value = number(values[i]))) { - numbers.push(value); - } - } - } - - else { - while (++i < n) { - if (!isNaN(value = number(valueof(values[i], i, values)))) { - numbers.push(value); - } - } - } - - return threshold(numbers.sort(d3_ascending), 0.5); - } - - function merge(arrays) { - var n = arrays.length, - m, - i = -1, - j = 0, - merged, - array; - - while (++i < n) j += arrays[i].length; - merged = new Array(j); - - while (--n >= 0) { - array = arrays[n]; - m = array.length; - while (--m >= 0) { - merged[--j] = array[m]; - } - } - - return merged; - } - - function clip(pointVisible, clipLine, interpolate, start) { - return function(sink) { - var line = clipLine(sink), - ringBuffer = clipBuffer(), - ringSink = clipLine(ringBuffer), - polygonStarted = false, - polygon, - segments, - ring; - - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - segments = merge(segments); - var startInside = polygonContains(polygon, start); - if (segments.length) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - clipRejoin(segments, compareIntersection, startInside, interpolate, sink); - } else if (startInside) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - sink.lineStart(); - interpolate(null, null, 1, sink); - sink.lineEnd(); - } - if (polygonStarted) sink.polygonEnd(), polygonStarted = false; - segments = polygon = null; - }, - sphere: function() { - sink.polygonStart(); - sink.lineStart(); - interpolate(null, null, 1, sink); - sink.lineEnd(); - sink.polygonEnd(); - } - }; - - function point(lambda, phi) { - if (pointVisible(lambda, phi)) sink.point(lambda, phi); - } - - function pointLine(lambda, phi) { - line.point(lambda, phi); - } - - function lineStart() { - clip.point = pointLine; - line.lineStart(); - } - - function lineEnd() { - clip.point = point; - line.lineEnd(); - } - - function pointRing(lambda, phi) { - ring.push([lambda, phi]); - ringSink.point(lambda, phi); - } - - function ringStart() { - ringSink.lineStart(); - ring = []; - } - - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringSink.lineEnd(); - - var clean = ringSink.clean(), - ringSegments = ringBuffer.result(), - i, n = ringSegments.length, m, - segment, - point; - - ring.pop(); - polygon.push(ring); - ring = null; - - if (!n) return; - - // No intersections. - if (clean & 1) { - segment = ringSegments[0]; - if ((m = segment.length - 1) > 0) { - if (!polygonStarted) sink.polygonStart(), polygonStarted = true; - sink.lineStart(); - for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]); - sink.lineEnd(); - } - return; - } - - // Rejoin connected segments. - // TODO reuse ringBuffer.rejoin()? - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - - segments.push(ringSegments.filter(validSegment)); - } - - return clip; - }; - } - - function validSegment(segment) { - return segment.length > 1; - } - - // Intersections are sorted along the clip edge. For both antimeridian cutting - // and circle clipping, the same comparison is used. - function compareIntersection(a, b) { - return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1]) - - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]); - } - - var clipAntimeridian = clip( - function() { return true; }, - clipAntimeridianLine, - clipAntimeridianInterpolate, - [-pi, -halfPi] - ); - - // Takes a line and cuts into visible segments. Return values: 0 - there were - // intersections or the line was empty; 1 - no intersections; 2 - there were - // intersections, and the first and last segments should be rejoined. - function clipAntimeridianLine(stream) { - var lambda0 = NaN, - phi0 = NaN, - sign0 = NaN, - clean; // no intersections - - return { - lineStart: function() { - stream.lineStart(); - clean = 1; - }, - point: function(lambda1, phi1) { - var sign1 = lambda1 > 0 ? pi : -pi, - delta = abs$2(lambda1 - lambda0); - if (abs$2(delta - pi) < epsilon) { // line crosses a pole - stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi); - stream.point(sign0, phi0); - stream.lineEnd(); - stream.lineStart(); - stream.point(sign1, phi0); - stream.point(lambda1, phi0); - clean = 0; - } else if (sign0 !== sign1 && delta >= pi) { // line crosses antimeridian - if (abs$2(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies - if (abs$2(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon; - phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1); - stream.point(sign0, phi0); - stream.lineEnd(); - stream.lineStart(); - stream.point(sign1, phi0); - clean = 0; - } - stream.point(lambda0 = lambda1, phi0 = phi1); - sign0 = sign1; - }, - lineEnd: function() { - stream.lineEnd(); - lambda0 = phi0 = NaN; - }, - clean: function() { - return 2 - clean; // if intersections, rejoin first and last segments - } - }; - } - - function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) { - var cosPhi0, - cosPhi1, - sinLambda0Lambda1 = sin(lambda0 - lambda1); - return abs$2(sinLambda0Lambda1) > epsilon - ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) - / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) - : (phi0 + phi1) / 2; - } - - function clipAntimeridianInterpolate(from, to, direction, stream) { - var phi; - if (from == null) { - phi = direction * halfPi; - stream.point(-pi, phi); - stream.point(0, phi); - stream.point(pi, phi); - stream.point(pi, 0); - stream.point(pi, -phi); - stream.point(0, -phi); - stream.point(-pi, -phi); - stream.point(-pi, 0); - stream.point(-pi, phi); - } else if (abs$2(from[0] - to[0]) > epsilon) { - var lambda = from[0] < to[0] ? pi : -pi; - phi = direction * lambda / 2; - stream.point(-lambda, phi); - stream.point(0, phi); - stream.point(lambda, phi); - } else { - stream.point(to[0], to[1]); - } - } - - function clipCircle(radius) { - var cr = cos(radius), - delta = 6 * radians, - smallRadius = cr > 0, - notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case - - function interpolate(from, to, direction, stream) { - circleStream(stream, radius, delta, direction, from, to); - } - - function visible(lambda, phi) { - return cos(lambda) * cos(phi) > cr; - } - - // Takes a line and cuts into visible segments. Return values used for polygon - // clipping: 0 - there were intersections or the line was empty; 1 - no - // intersections 2 - there were intersections, and the first and last segments - // should be rejoined. - function clipLine(stream) { - var point0, // previous point - c0, // code for previous point - v0, // visibility of previous point - v00, // visibility of first point - clean; // no intersections - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(lambda, phi) { - var point1 = [lambda, phi], - point2, - v = visible(lambda, phi), - c = smallRadius - ? v ? 0 : code(lambda, phi) - : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0; - if (!point0 && (v00 = v0 = v)) stream.lineStart(); - if (v !== v0) { - point2 = intersect(point0, point1); - if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) - point1[2] = 1; - } - if (v !== v0) { - clean = 0; - if (v) { - // outside going in - stream.lineStart(); - point2 = intersect(point1, point0); - stream.point(point2[0], point2[1]); - } else { - // inside going out - point2 = intersect(point0, point1); - stream.point(point2[0], point2[1], 2); - stream.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - // If the codes for two points are different, or are both zero, - // and there this segment intersects with the small circle. - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - stream.lineStart(); - stream.point(t[0][0], t[0][1]); - stream.point(t[1][0], t[1][1]); - stream.lineEnd(); - } else { - stream.point(t[1][0], t[1][1]); - stream.lineEnd(); - stream.lineStart(); - stream.point(t[0][0], t[0][1], 3); - } - } - } - if (v && (!point0 || !pointEqual(point0, point1))) { - stream.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) stream.lineEnd(); - point0 = null; - }, - // Rejoin first and last segments if there were intersections and the first - // and last points were visible. - clean: function() { - return clean | ((v00 && v0) << 1); - } - }; - } - - // Intersects the great circle between a and b with the clip circle. - function intersect(a, b, two) { - var pa = cartesian(a), - pb = cartesian(b); - - // We have two planes, n1.p = d1 and n2.p = d2. - // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). - var n1 = [1, 0, 0], // normal - n2 = cartesianCross(pa, pb), - n2n2 = cartesianDot(n2, n2), - n1n2 = n2[0], // cartesianDot(n1, n2), - determinant = n2n2 - n1n2 * n1n2; - - // Two polar points. - if (!determinant) return !two && a; - - var c1 = cr * n2n2 / determinant, - c2 = -cr * n1n2 / determinant, - n1xn2 = cartesianCross(n1, n2), - A = cartesianScale(n1, c1), - B = cartesianScale(n2, c2); - cartesianAddInPlace(A, B); - - // Solve |p(t)|^2 = 1. - var u = n1xn2, - w = cartesianDot(A, u), - uu = cartesianDot(u, u), - t2 = w * w - uu * (cartesianDot(A, A) - 1); - - if (t2 < 0) return; - - var t = sqrt(t2), - q = cartesianScale(u, (-w - t) / uu); - cartesianAddInPlace(q, A); - q = spherical(q); - - if (!two) return q; - - // Two intersection points. - var lambda0 = a[0], - lambda1 = b[0], - phi0 = a[1], - phi1 = b[1], - z; - - if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z; - - var delta = lambda1 - lambda0, - polar = abs$2(delta - pi) < epsilon, - meridian = polar || delta < epsilon; - - if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; - - // Check that the first point is between a and b. - if (meridian - ? polar - ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon ? phi0 : phi1) - : phi0 <= q[1] && q[1] <= phi1 - : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) { - var q1 = cartesianScale(u, (-w + t) / uu); - cartesianAddInPlace(q1, A); - return [q, spherical(q1)]; - } - } - - // Generates a 4-bit vector representing the location of a point relative to - // the small circle's bounding box. - function code(lambda, phi) { - var r = smallRadius ? radius : pi - radius, - code = 0; - if (lambda < -r) code |= 1; // left - else if (lambda > r) code |= 2; // right - if (phi < -r) code |= 4; // below - else if (phi > r) code |= 8; // above - return code; - } - - return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]); - } - - function clipLine(a, b, x0, y0, x1, y1) { - var ax = a[0], - ay = a[1], - bx = b[0], - by = b[1], - t0 = 0, - t1 = 1, - dx = bx - ax, - dy = by - ay, - r; - - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - - if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; - if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; - return true; - } - - var clipMax = 1e9, clipMin = -clipMax; - - // TODO Use d3-polygon’s polygonContains here for the ring check? - // TODO Eliminate duplicate buffering in clipBuffer and polygon.push? - - function clipRectangle(x0, y0, x1, y1) { - - function visible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - - function interpolate(from, to, direction, stream) { - var a = 0, a1 = 0; - if (from == null - || (a = corner(from, direction)) !== (a1 = corner(to, direction)) - || comparePoint(from, to) < 0 ^ direction > 0) { - do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - while ((a = (a + direction + 4) % 4) !== a1); - } else { - stream.point(to[0], to[1]); - } - } - - function corner(p, direction) { - return abs$2(p[0] - x0) < epsilon ? direction > 0 ? 0 : 3 - : abs$2(p[0] - x1) < epsilon ? direction > 0 ? 2 : 1 - : abs$2(p[1] - y0) < epsilon ? direction > 0 ? 1 : 0 - : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon - } - - function compareIntersection(a, b) { - return comparePoint(a.x, b.x); - } - - function comparePoint(a, b) { - var ca = corner(a, 1), - cb = corner(b, 1); - return ca !== cb ? ca - cb - : ca === 0 ? b[1] - a[1] - : ca === 1 ? a[0] - b[0] - : ca === 2 ? a[1] - b[1] - : b[0] - a[0]; - } - - return function(stream) { - var activeStream = stream, - bufferStream = clipBuffer(), - segments, - polygon, - ring, - x__, y__, v__, // first point - x_, y_, v_, // previous point - first, - clean; - - var clipStream = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: polygonStart, - polygonEnd: polygonEnd - }; - - function point(x, y) { - if (visible(x, y)) activeStream.point(x, y); - } - - function polygonInside() { - var winding = 0; - - for (var i = 0, n = polygon.length; i < n; ++i) { - for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) { - a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1]; - if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; } - else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; } - } - } - - return winding; - } - - // Buffer geometry within a polygon and then clip it en masse. - function polygonStart() { - activeStream = bufferStream, segments = [], polygon = [], clean = true; - } - - function polygonEnd() { - var startInside = polygonInside(), - cleanInside = clean && startInside, - visible = (segments = merge(segments)).length; - if (cleanInside || visible) { - stream.polygonStart(); - if (cleanInside) { - stream.lineStart(); - interpolate(null, null, 1, stream); - stream.lineEnd(); - } - if (visible) { - clipRejoin(segments, compareIntersection, startInside, interpolate, stream); - } - stream.polygonEnd(); - } - activeStream = stream, segments = polygon = ring = null; - } - - function lineStart() { - clipStream.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - - // TODO rather than special-case polygons, simply handle them separately. - // Ideally, coincident intersection points should be jittered to avoid - // clipping issues. - function lineEnd() { - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferStream.rejoin(); - segments.push(bufferStream.result()); - } - clipStream.point = point; - if (v_) activeStream.lineEnd(); - } - - function linePoint(x, y) { - var v = visible(x, y); - if (polygon) ring.push([x, y]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - activeStream.lineStart(); - activeStream.point(x, y); - } - } else { - if (v && v_) activeStream.point(x, y); - else { - var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))], - b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))]; - if (clipLine(a, b, x0, y0, x1, y1)) { - if (!v_) { - activeStream.lineStart(); - activeStream.point(a[0], a[1]); - } - activeStream.point(b[0], b[1]); - if (!v) activeStream.lineEnd(); - clean = false; - } else if (v) { - activeStream.lineStart(); - activeStream.point(x, y); - clean = false; - } - } - } - x_ = x, y_ = y, v_ = v; - } - - return clipStream; - }; - } - - var lengthSum = adder(), - lambda0$2, - sinPhi0$1, - cosPhi0$1; - - var lengthStream = { - sphere: noop$2, - point: noop$2, - lineStart: lengthLineStart, - lineEnd: noop$2, - polygonStart: noop$2, - polygonEnd: noop$2 - }; - - function lengthLineStart() { - lengthStream.point = lengthPointFirst; - lengthStream.lineEnd = lengthLineEnd; - } - - function lengthLineEnd() { - lengthStream.point = lengthStream.lineEnd = noop$2; - } - - function lengthPointFirst(lambda, phi) { - lambda *= radians, phi *= radians; - lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi); - lengthStream.point = lengthPoint; - } - - function lengthPoint(lambda, phi) { - lambda *= radians, phi *= radians; - var sinPhi = sin(phi), - cosPhi = cos(phi), - delta = abs$2(lambda - lambda0$2), - cosDelta = cos(delta), - sinDelta = sin(delta), - x = cosPhi * sinDelta, - y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta, - z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta; - lengthSum.add(atan2(sqrt(x * x + y * y), z)); - lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi; - } - - function d3_geoLength(object) { - lengthSum.reset(); - d3_geoStream(object, lengthStream); - return +lengthSum; - } - - function identity(x) { - return x; - } - - var areaSum$1 = adder(), - areaRingSum$1 = adder(), - x00, - y00, - x0$1, - y0$1; - - var areaStream$1 = { - point: noop$2, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: function() { - areaStream$1.lineStart = areaRingStart$1; - areaStream$1.lineEnd = areaRingEnd$1; - }, - polygonEnd: function() { - areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2; - areaSum$1.add(abs$2(areaRingSum$1)); - areaRingSum$1.reset(); - }, - result: function() { - var area = areaSum$1 / 2; - areaSum$1.reset(); - return area; - } - }; - - function areaRingStart$1() { - areaStream$1.point = areaPointFirst$1; - } - - function areaPointFirst$1(x, y) { - areaStream$1.point = areaPoint$1; - x00 = x0$1 = x, y00 = y0$1 = y; - } - - function areaPoint$1(x, y) { - areaRingSum$1.add(y0$1 * x - x0$1 * y); - x0$1 = x, y0$1 = y; - } - - function areaRingEnd$1() { - areaPoint$1(x00, y00); - } - - var x0$2 = Infinity, - y0$2 = x0$2, - x1 = -x0$2, - y1 = x1; - - var boundsStream$1 = { - point: boundsPoint$1, - lineStart: noop$2, - lineEnd: noop$2, - polygonStart: noop$2, - polygonEnd: noop$2, - result: function() { - var bounds = [[x0$2, y0$2], [x1, y1]]; - x1 = y1 = -(y0$2 = x0$2 = Infinity); - return bounds; - } - }; - - function boundsPoint$1(x, y) { - if (x < x0$2) x0$2 = x; - if (x > x1) x1 = x; - if (y < y0$2) y0$2 = y; - if (y > y1) y1 = y; - } - - // TODO Enforce positive area for exterior, negative area for interior? - - var X0$1 = 0, - Y0$1 = 0, - Z0$1 = 0, - X1$1 = 0, - Y1$1 = 0, - Z1$1 = 0, - X2$1 = 0, - Y2$1 = 0, - Z2$1 = 0, - x00$1, - y00$1, - x0$3, - y0$3; - - var centroidStream$1 = { - point: centroidPoint$1, - lineStart: centroidLineStart$1, - lineEnd: centroidLineEnd$1, - polygonStart: function() { - centroidStream$1.lineStart = centroidRingStart$1; - centroidStream$1.lineEnd = centroidRingEnd$1; - }, - polygonEnd: function() { - centroidStream$1.point = centroidPoint$1; - centroidStream$1.lineStart = centroidLineStart$1; - centroidStream$1.lineEnd = centroidLineEnd$1; - }, - result: function() { - var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1] - : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1] - : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1] - : [NaN, NaN]; - X0$1 = Y0$1 = Z0$1 = - X1$1 = Y1$1 = Z1$1 = - X2$1 = Y2$1 = Z2$1 = 0; - return centroid; - } - }; - - function centroidPoint$1(x, y) { - X0$1 += x; - Y0$1 += y; - ++Z0$1; - } - - function centroidLineStart$1() { - centroidStream$1.point = centroidPointFirstLine; - } - - function centroidPointFirstLine(x, y) { - centroidStream$1.point = centroidPointLine; - centroidPoint$1(x0$3 = x, y0$3 = y); - } - - function centroidPointLine(x, y) { - var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy); - X1$1 += z * (x0$3 + x) / 2; - Y1$1 += z * (y0$3 + y) / 2; - Z1$1 += z; - centroidPoint$1(x0$3 = x, y0$3 = y); - } - - function centroidLineEnd$1() { - centroidStream$1.point = centroidPoint$1; - } - - function centroidRingStart$1() { - centroidStream$1.point = centroidPointFirstRing; - } - - function centroidRingEnd$1() { - centroidPointRing(x00$1, y00$1); - } - - function centroidPointFirstRing(x, y) { - centroidStream$1.point = centroidPointRing; - centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y); - } - - function centroidPointRing(x, y) { - var dx = x - x0$3, - dy = y - y0$3, - z = sqrt(dx * dx + dy * dy); - - X1$1 += z * (x0$3 + x) / 2; - Y1$1 += z * (y0$3 + y) / 2; - Z1$1 += z; - - z = y0$3 * x - x0$3 * y; - X2$1 += z * (x0$3 + x); - Y2$1 += z * (y0$3 + y); - Z2$1 += z * 3; - centroidPoint$1(x0$3 = x, y0$3 = y); - } - - function PathContext(context) { - this._context = context; - } - - PathContext.prototype = { - _radius: 4.5, - pointRadius: function(_) { - return this._radius = _, this; - }, - polygonStart: function() { - this._line = 0; - }, - polygonEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._line === 0) this._context.closePath(); - this._point = NaN; - }, - point: function(x, y) { - switch (this._point) { - case 0: { - this._context.moveTo(x, y); - this._point = 1; - break; - } - case 1: { - this._context.lineTo(x, y); - break; - } - default: { - this._context.moveTo(x + this._radius, y); - this._context.arc(x, y, this._radius, 0, tau); - break; - } - } - }, - result: noop$2 - }; - - var lengthSum$1 = adder(), - lengthRing, - x00$2, - y00$2, - x0$4, - y0$4; - - var lengthStream$1 = { - point: noop$2, - lineStart: function() { - lengthStream$1.point = lengthPointFirst$1; - }, - lineEnd: function() { - if (lengthRing) lengthPoint$1(x00$2, y00$2); - lengthStream$1.point = noop$2; - }, - polygonStart: function() { - lengthRing = true; - }, - polygonEnd: function() { - lengthRing = null; - }, - result: function() { - var length = +lengthSum$1; - lengthSum$1.reset(); - return length; - } - }; - - function lengthPointFirst$1(x, y) { - lengthStream$1.point = lengthPoint$1; - x00$2 = x0$4 = x, y00$2 = y0$4 = y; - } - - function lengthPoint$1(x, y) { - x0$4 -= x, y0$4 -= y; - lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4)); - x0$4 = x, y0$4 = y; - } - - function PathString() { - this._string = []; - } - - PathString.prototype = { - _radius: 4.5, - _circle: circle(4.5), - pointRadius: function(_) { - if ((_ = +_) !== this._radius) this._radius = _, this._circle = null; - return this; - }, - polygonStart: function() { - this._line = 0; - }, - polygonEnd: function() { - this._line = NaN; - }, - lineStart: function() { - this._point = 0; - }, - lineEnd: function() { - if (this._line === 0) this._string.push("Z"); - this._point = NaN; - }, - point: function(x, y) { - switch (this._point) { - case 0: { - this._string.push("M", x, ",", y); - this._point = 1; - break; - } - case 1: { - this._string.push("L", x, ",", y); - break; - } - default: { - if (this._circle == null) this._circle = circle(this._radius); - this._string.push("M", x, ",", y, this._circle); - break; - } - } - }, - result: function() { - if (this._string.length) { - var result = this._string.join(""); - this._string = []; - return result; - } else { - return null; - } - } - }; - - function circle(radius) { - return "m0," + radius - + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius - + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius - + "z"; - } - - function d3_geoPath(projection, context) { - var pointRadius = 4.5, - projectionStream, - contextStream; - - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - d3_geoStream(object, projectionStream(contextStream)); - } - return contextStream.result(); - } - - path.area = function(object) { - d3_geoStream(object, projectionStream(areaStream$1)); - return areaStream$1.result(); - }; - - path.measure = function(object) { - d3_geoStream(object, projectionStream(lengthStream$1)); - return lengthStream$1.result(); - }; - - path.bounds = function(object) { - d3_geoStream(object, projectionStream(boundsStream$1)); - return boundsStream$1.result(); - }; - - path.centroid = function(object) { - d3_geoStream(object, projectionStream(centroidStream$1)); - return centroidStream$1.result(); - }; - - path.projection = function(_) { - return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection; - }; - - path.context = function(_) { - if (!arguments.length) return context; - contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return path; - }; - - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - - return path.projection(projection).context(context); - } - - function d3_geoTransform(methods) { - return { - stream: transformer(methods) - }; - } - - function transformer(methods) { - return function(stream) { - var s = new TransformStream; - for (var key in methods) s[key] = methods[key]; - s.stream = stream; - return s; - }; - } - - function TransformStream() {} - - TransformStream.prototype = { - constructor: TransformStream, - point: function(x, y) { this.stream.point(x, y); }, - sphere: function() { this.stream.sphere(); }, - lineStart: function() { this.stream.lineStart(); }, - lineEnd: function() { this.stream.lineEnd(); }, - polygonStart: function() { this.stream.polygonStart(); }, - polygonEnd: function() { this.stream.polygonEnd(); } - }; - - function fit(projection, fitBounds, object) { - var clip = projection.clipExtent && projection.clipExtent(); - projection.scale(150).translate([0, 0]); - if (clip != null) projection.clipExtent(null); - d3_geoStream(object, projection.stream(boundsStream$1)); - fitBounds(boundsStream$1.result()); - if (clip != null) projection.clipExtent(clip); - return projection; - } - - function fitExtent(projection, extent, object) { - return fit(projection, function(b) { - var w = extent[1][0] - extent[0][0], - h = extent[1][1] - extent[0][1], - k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])), - x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2, - y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2; - projection.scale(150 * k).translate([x, y]); - }, object); - } - - function fitSize(projection, size, object) { - return fitExtent(projection, [[0, 0], size], object); - } - - function fitWidth(projection, width, object) { - return fit(projection, function(b) { - var w = +width, - k = w / (b[1][0] - b[0][0]), - x = (w - k * (b[1][0] + b[0][0])) / 2, - y = -k * b[0][1]; - projection.scale(150 * k).translate([x, y]); - }, object); - } - - function fitHeight(projection, height, object) { - return fit(projection, function(b) { - var h = +height, - k = h / (b[1][1] - b[0][1]), - x = -k * b[0][0], - y = (h - k * (b[1][1] + b[0][1])) / 2; - projection.scale(150 * k).translate([x, y]); - }, object); - } - - var maxDepth = 16, // maximum depth of subdivision - cosMinDistance = cos(30 * radians); // cos(minimum angular distance) - - function resample(project, delta2) { - return +delta2 ? resample$1(project, delta2) : resampleNone(project); - } - - function resampleNone(project) { - return transformer({ - point: function(x, y) { - x = project(x, y); - this.stream.point(x[0], x[1]); - } - }); - } - - function resample$1(project, delta2) { - - function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, - dy = y1 - y0, - d2 = dx * dx + dy * dy; - if (d2 > 4 * delta2 && depth--) { - var a = a0 + a1, - b = b0 + b1, - c = c0 + c1, - m = sqrt(a * a + b * b + c * c), - phi2 = asin(c /= m), - lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a), - p = project(lambda2, phi2), - x2 = p[0], - y2 = p[1], - dx2 = x2 - x0, - dy2 = y2 - y0, - dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > delta2 // perpendicular projected distance - || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end - || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream); - } - } - } - return function(stream) { - var lambda00, x00, y00, a00, b00, c00, // first point - lambda0, x0, y0, a0, b0, c0; // previous point - - var resampleStream = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; }, - polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; } - }; - - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - - function lineStart() { - x0 = NaN; - resampleStream.point = linePoint; - stream.lineStart(); - } - - function linePoint(lambda, phi) { - var c = cartesian([lambda, phi]), p = project(lambda, phi); - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - - function lineEnd() { - resampleStream.point = point; - stream.lineEnd(); - } - - function ringStart() { - lineStart(); - resampleStream.point = ringPoint; - resampleStream.lineEnd = ringEnd; - } - - function ringPoint(lambda, phi) { - linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resampleStream.point = linePoint; - } - - function ringEnd() { - resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream); - resampleStream.lineEnd = lineEnd; - lineEnd(); - } - - return resampleStream; - }; - } - - var transformRadians = transformer({ - point: function(x, y) { - this.stream.point(x * radians, y * radians); - } - }); - - function transformRotate(rotate) { - return transformer({ - point: function(x, y) { - var r = rotate(x, y); - return this.stream.point(r[0], r[1]); - } - }); - } - - function scaleTranslate(k, dx, dy, sx, sy) { - function transform(x, y) { - x *= sx; y *= sy; - return [dx + k * x, dy - k * y]; - } - transform.invert = function(x, y) { - return [(x - dx) / k * sx, (dy - y) / k * sy]; - }; - return transform; - } - - function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) { - var cosAlpha = cos(alpha), - sinAlpha = sin(alpha), - a = cosAlpha * k, - b = sinAlpha * k, - ai = cosAlpha / k, - bi = sinAlpha / k, - ci = (sinAlpha * dy - cosAlpha * dx) / k, - fi = (sinAlpha * dx + cosAlpha * dy) / k; - function transform(x, y) { - x *= sx; y *= sy; - return [a * x - b * y + dx, dy - b * x - a * y]; - } - transform.invert = function(x, y) { - return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)]; - }; - return transform; - } - - function projection(project) { - return projectionMutator(function() { return project; })(); - } - - function projectionMutator(projectAt) { - var project, - k = 150, // scale - x = 480, y = 250, // translate - lambda = 0, phi = 0, // center - deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate - alpha = 0, // post-rotate angle - sx = 1, // reflectX - sy = 1, // reflectX - theta = null, preclip = clipAntimeridian, // pre-clip angle - x0 = null, y0, x1, y1, postclip = identity, // post-clip extent - delta2 = 0.5, // precision - projectResample, - projectTransform, - projectRotateTransform, - cache, - cacheStream; - - function projection(point) { - return projectRotateTransform(point[0] * radians, point[1] * radians); - } - - function invert(point) { - point = projectRotateTransform.invert(point[0], point[1]); - return point && [point[0] * degrees, point[1] * degrees]; - } - - projection.stream = function(stream) { - return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream))))); - }; - - projection.preclip = function(_) { - return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip; - }; - - projection.postclip = function(_) { - return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; - }; - - projection.clipAngle = function(_) { - return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees; - }; - - projection.clipExtent = function(_) { - return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; - }; - - projection.scale = function(_) { - return arguments.length ? (k = +_, recenter()) : k; - }; - - projection.translate = function(_) { - return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y]; - }; - - projection.center = function(_) { - return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees]; - }; - - projection.rotate = function(_) { - return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees, deltaPhi * degrees, deltaGamma * degrees]; - }; - - projection.angle = function(_) { - return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees; - }; - - projection.reflectX = function(_) { - return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0; - }; - - projection.reflectY = function(_) { - return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0; - }; - - projection.precision = function(_) { - return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2); - }; - - projection.fitExtent = function(extent, object) { - return fitExtent(projection, extent, object); - }; - - projection.fitSize = function(size, object) { - return fitSize(projection, size, object); - }; - - projection.fitWidth = function(width, object) { - return fitWidth(projection, width, object); - }; - - projection.fitHeight = function(height, object) { - return fitHeight(projection, height, object); - }; - - function recenter() { - var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)), - transform = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], sx, sy, alpha); - rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma); - projectTransform = compose(project, transform); - projectRotateTransform = compose(rotate, projectTransform); - projectResample = resample(projectTransform, delta2); - return reset(); - } - - function reset() { - cache = cacheStream = null; - return projection; - } - - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return recenter(); - }; - } - - function mercatorRaw(lambda, phi) { - return [lambda, log(tan((halfPi + phi) / 2))]; - } - - mercatorRaw.invert = function(x, y) { - return [x, 2 * atan(exp(y)) - halfPi]; - }; - - function mercator() { - return mercatorProjection(mercatorRaw) - .scale(961 / tau); - } - - function mercatorProjection(project) { - var m = projection(project), - center = m.center, - scale = m.scale, - translate = m.translate, - clipExtent = m.clipExtent, - x0 = null, y0, x1, y1; // clip extent - - m.scale = function(_) { - return arguments.length ? (scale(_), reclip()) : scale(); - }; - - m.translate = function(_) { - return arguments.length ? (translate(_), reclip()) : translate(); - }; - - m.center = function(_) { - return arguments.length ? (center(_), reclip()) : center(); - }; - - m.clipExtent = function(_) { - return arguments.length ? ((_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1])), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]]; - }; - - function reclip() { - var k = pi * scale(), - t = m(rotation(m.rotate()).invert([0, 0])); - return clipExtent(x0 == null - ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw - ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] - : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]); - } - - return reclip(); - } - - function d3_geoIdentity() { - var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, // scale, translate and reflect - alpha = 0, ca, sa, // angle - x0 = null, y0, x1, y1, // clip extent - kx = 1, ky = 1, - transform = transformer({ - point: function(x, y) { - var p = projection([x, y]); - this.stream.point(p[0], p[1]); - } - }), - postclip = identity, - cache, - cacheStream; - - function reset() { - kx = k * sx; - ky = k * sy; - cache = cacheStream = null; - return projection; - } - - function projection (p) { - var x = p[0] * kx, y = p[1] * ky; - if (alpha) { - var t = y * ca - x * sa; - x = x * ca + y * sa; - y = t; - } - return [x + tx, y + ty]; - } - projection.invert = function(p) { - var x = p[0] - tx, y = p[1] - ty; - if (alpha) { - var t = y * ca + x * sa; - x = x * ca - y * sa; - y = t; - } - return [x / kx, y / ky]; - }; - projection.stream = function(stream) { - return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream)); - }; - projection.postclip = function(_) { - return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; - }; - projection.clipExtent = function(_) { - return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; - }; - projection.scale = function(_) { - return arguments.length ? (k = +_, reset()) : k; - }; - projection.translate = function(_) { - return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty]; - }; - projection.angle = function(_) { - return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees; - }; - projection.reflectX = function(_) { - return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0; - }; - projection.reflectY = function(_) { - return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0; - }; - projection.fitExtent = function(extent, object) { - return fitExtent(projection, extent, object); - }; - projection.fitSize = function(size, object) { - return fitSize(projection, size, object); - }; - projection.fitWidth = function(width, object) { - return fitWidth(projection, width, object); - }; - projection.fitHeight = function(height, object) { - return fitHeight(projection, height, object); - }; - - return projection; - } - - // constants - var TAU = 2 * Math.PI; - var EQUATORIAL_RADIUS = 6356752.314245179; - var POLAR_RADIUS = 6378137.0; - - - function geoLatToMeters(dLat) { - return dLat * (TAU * POLAR_RADIUS / 360); - } - - - function geoLonToMeters(dLon, atLat) { - return Math.abs(atLat) >= 90 ? 0 : - dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180))); - } - - - function geoMetersToLat(m) { - return m / (TAU * POLAR_RADIUS / 360); - } - - - function geoMetersToLon(m, atLat) { - return Math.abs(atLat) >= 90 ? 0 : - m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180))); - } - - - function geoMetersToOffset(meters, tileSize) { - tileSize = tileSize || 256; - return [ - meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), - -meters[1] * tileSize / (TAU * POLAR_RADIUS) - ]; - } - - - function geoOffsetToMeters(offset, tileSize) { - tileSize = tileSize || 256; - return [ - offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, - -offset[1] * TAU * POLAR_RADIUS / tileSize - ]; - } - - - // Equirectangular approximation of spherical distances on Earth - function geoSphericalDistance(a, b) { - var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2); - var y = geoLatToMeters(a[1] - b[1]); - return Math.sqrt((x * x) + (y * y)); - } - - - // scale to zoom - function geoScaleToZoom(k, tileSize) { - tileSize = tileSize || 256; - var log2ts = Math.log(tileSize) * Math.LOG2E; - return Math.log(k * TAU) / Math.LN2 - log2ts; - } - - - // zoom to scale - function geoZoomToScale(z, tileSize) { - tileSize = tileSize || 256; - return tileSize * Math.pow(2, z) / TAU; - } - - - // returns info about the node from `nodes` closest to the given `point` - function geoSphericalClosestNode(nodes, point) { - var minDistance = Infinity, distance; - var indexOfMin; - - for (var i in nodes) { - distance = geoSphericalDistance(nodes[i].loc, point); - if (distance < minDistance) { - minDistance = distance; - indexOfMin = i; - } - } - - if (indexOfMin !== undefined) { - return { index: indexOfMin, distance: minDistance, node: nodes[indexOfMin] }; - } else { - return null; - } - } - - function geoExtent(min, max) { - if (!(this instanceof geoExtent)) { - return new geoExtent(min, max); - } else if (min instanceof geoExtent) { - return min; - } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) { - this[0] = min[0]; - this[1] = min[1]; - } else { - this[0] = min || [ Infinity, Infinity]; - this[1] = max || min || [-Infinity, -Infinity]; - } - } - - geoExtent.prototype = new Array(2); - - Object.assign(geoExtent.prototype, { - - equals: function (obj) { - return this[0][0] === obj[0][0] && - this[0][1] === obj[0][1] && - this[1][0] === obj[1][0] && - this[1][1] === obj[1][1]; - }, - - - extend: function(obj) { - if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); - return geoExtent( - [Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])], - [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])] - ); - }, - - - _extend: function(extent) { - this[0][0] = Math.min(extent[0][0], this[0][0]); - this[0][1] = Math.min(extent[0][1], this[0][1]); - this[1][0] = Math.max(extent[1][0], this[1][0]); - this[1][1] = Math.max(extent[1][1], this[1][1]); - }, - - - area: function() { - return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1])); - }, - - - center: function() { - return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2]; - }, - - - rectangle: function() { - return [this[0][0], this[0][1], this[1][0], this[1][1]]; - }, - - - bbox: function() { - return { minX: this[0][0], minY: this[0][1], maxX: this[1][0], maxY: this[1][1] }; - }, - - - polygon: function() { - return [ - [this[0][0], this[0][1]], - [this[0][0], this[1][1]], - [this[1][0], this[1][1]], - [this[1][0], this[0][1]], - [this[0][0], this[0][1]] - ]; - }, - - - contains: function(obj) { - if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); - return obj[0][0] >= this[0][0] && - obj[0][1] >= this[0][1] && - obj[1][0] <= this[1][0] && - obj[1][1] <= this[1][1]; - }, - - - intersects: function(obj) { - if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); - return obj[0][0] <= this[1][0] && - obj[0][1] <= this[1][1] && - obj[1][0] >= this[0][0] && - obj[1][1] >= this[0][1]; - }, - - - intersection: function(obj) { - if (!this.intersects(obj)) return new geoExtent(); - return new geoExtent( - [Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])], - [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])] - ); - }, - - - percentContainedIn: function(obj) { - if (!(obj instanceof geoExtent)) obj = new geoExtent(obj); - var a1 = this.intersection(obj).area(); - var a2 = this.area(); - - if (a1 === Infinity || a2 === Infinity) { - return 0; - } else if (a1 === 0 || a2 === 0) { - if (obj.contains(this)) { - return 1; - } - return 0; - } else { - return a1 / a2; - } - }, - - - padByMeters: function(meters) { - var dLat = geoMetersToLat(meters); - var dLon = geoMetersToLon(meters, this.center()[1]); - return geoExtent( - [this[0][0] - dLon, this[0][1] - dLat], - [this[1][0] + dLon, this[1][1] + dLat] - ); - }, - - - toParam: function() { - return this.rectangle().join(','); - } - - }); - - function d3_polygonArea(polygon) { - var i = -1, - n = polygon.length, - a, - b = polygon[n - 1], - area = 0; - - while (++i < n) { - a = b; - b = polygon[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - - return area / 2; - } - - function d3_polygonCentroid(polygon) { - var i = -1, - n = polygon.length, - x = 0, - y = 0, - a, - b = polygon[n - 1], - c, - k = 0; - - while (++i < n) { - a = b; - b = polygon[i]; - k += c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - - return k *= 3, [x / k, y / k]; - } - - // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of - // the 3D cross product in a quadrant I Cartesian coordinate system (+x is - // right, +y is up). Returns a positive value if ABC is counter-clockwise, - // negative if clockwise, and zero if the points are collinear. - function cross(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); - } - - function lexicographicOrder(a, b) { - return a[0] - b[0] || a[1] - b[1]; - } - - // Computes the upper convex hull per the monotone chain algorithm. - // Assumes points.length >= 3, is sorted by x, unique in y. - // Returns an array of indices into points in left-to-right order. - function computeUpperHullIndexes(points) { - var n = points.length, - indexes = [0, 1], - size = 2; - - for (var i = 2; i < n; ++i) { - while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size; - indexes[size++] = i; - } - - return indexes.slice(0, size); // remove popped points - } - - function d3_polygonHull(points) { - if ((n = points.length) < 3) return null; - - var i, - n, - sortedPoints = new Array(n), - flippedPoints = new Array(n); - - for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i]; - sortedPoints.sort(lexicographicOrder); - for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; - - var upperIndexes = computeUpperHullIndexes(sortedPoints), - lowerIndexes = computeUpperHullIndexes(flippedPoints); - - // Construct the hull polygon, removing possible duplicate endpoints. - var skipLeft = lowerIndexes[0] === upperIndexes[0], - skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1], - hull = []; - - // Add upper hull in right-to-l order. - // Then add lower hull in left-to-right order. - for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]); - for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]); - - return hull; - } - - // vector equals - function geoVecEqual(a, b, epsilon) { - if (epsilon) { - return (Math.abs(a[0] - b[0]) <= epsilon) && (Math.abs(a[1] - b[1]) <= epsilon); - } else { - return (a[0] === b[0]) && (a[1] === b[1]); - } - } - - // vector addition - function geoVecAdd(a, b) { - return [ a[0] + b[0], a[1] + b[1] ]; - } - - // vector subtraction - function geoVecSubtract(a, b) { - return [ a[0] - b[0], a[1] - b[1] ]; - } - - // vector scaling - function geoVecScale(a, mag) { - return [ a[0] * mag, a[1] * mag ]; - } - - // vector rounding (was: geoRoundCoordinates) - function geoVecFloor(a) { - return [ Math.floor(a[0]), Math.floor(a[1]) ]; - } - - // linear interpolation - function geoVecInterp(a, b, t) { - return [ - a[0] + (b[0] - a[0]) * t, - a[1] + (b[1] - a[1]) * t - ]; - } - - // http://jsperf.com/id-dist-optimization - function geoVecLength(a, b) { - return Math.sqrt(geoVecLengthSquare(a,b)); - } - - // length of vector raised to the power two - function geoVecLengthSquare(a, b) { - b = b || [0, 0]; - var x = a[0] - b[0]; - var y = a[1] - b[1]; - return (x * x) + (y * y); - } - - // get a unit vector - function geoVecNormalize(a) { - var length = Math.sqrt((a[0] * a[0]) + (a[1] * a[1])); - if (length !== 0) { - return geoVecScale(a, 1 / length); - } - return [0, 0]; - } - - // Return the counterclockwise angle in the range (-pi, pi) - // between the positive X axis and the line intersecting a and b. - function geoVecAngle(a, b) { - return Math.atan2(b[1] - a[1], b[0] - a[0]); - } - - // dot product - function geoVecDot(a, b, origin) { - origin = origin || [0, 0]; - var p = geoVecSubtract(a, origin); - var q = geoVecSubtract(b, origin); - return (p[0]) * (q[0]) + (p[1]) * (q[1]); - } - - // normalized dot product - function geoVecNormalizedDot(a, b, origin) { - origin = origin || [0, 0]; - var p = geoVecNormalize(geoVecSubtract(a, origin)); - var q = geoVecNormalize(geoVecSubtract(b, origin)); - return geoVecDot(p, q); - } - - // 2D cross product of OA and OB vectors, returns magnitude of Z vector - // Returns a positive value, if OAB makes a counter-clockwise turn, - // negative for clockwise turn, and zero if the points are collinear. - function geoVecCross(a, b, origin) { - origin = origin || [0, 0]; - var p = geoVecSubtract(a, origin); - var q = geoVecSubtract(b, origin); - return (p[0]) * (q[1]) - (p[1]) * (q[0]); - } - - - // find closest orthogonal projection of point onto points array - function geoVecProject(a, points) { - var min = Infinity; - var idx; - var target; - - for (var i = 0; i < points.length - 1; i++) { - var o = points[i]; - var s = geoVecSubtract(points[i + 1], o); - var v = geoVecSubtract(a, o); - var proj = geoVecDot(v, s) / geoVecDot(s, s); - var p; - - if (proj < 0) { - p = o; - } else if (proj > 1) { - p = points[i + 1]; - } else { - p = [o[0] + proj * s[0], o[1] + proj * s[1]]; - } - - var dist = geoVecLength(p, a); - if (dist < min) { - min = dist; - idx = i + 1; - target = p; - } - } - - if (idx !== undefined) { - return { index: idx, distance: min, target: target }; - } else { - return null; - } - } - - // Return the counterclockwise angle in the range (-pi, pi) - // between the positive X axis and the line intersecting a and b. - function geoAngle(a, b, projection) { - return geoVecAngle(projection(a.loc), projection(b.loc)); - } - - - function geoEdgeEqual(a, b) { - return (a[0] === b[0] && a[1] === b[1]) || - (a[0] === b[1] && a[1] === b[0]); - } - - - // Rotate all points counterclockwise around a pivot point by given angle - function geoRotate(points, angle, around) { - return points.map(function(point) { - var radial = geoVecSubtract(point, around); - return [ - radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0], - radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1] - ]; - }); - } - - - // Choose the edge with the minimal distance from `point` to its orthogonal - // projection onto that edge, if such a projection exists, or the distance to - // the closest vertex on that edge. Returns an object with the `index` of the - // chosen edge, the chosen `loc` on that edge, and the `distance` to to it. - function geoChooseEdge(nodes, point, projection, activeID) { - var dist = geoVecLength; - var points = nodes.map(function(n) { return projection(n.loc); }); - var ids = nodes.map(function(n) { return n.id; }); - var min = Infinity; - var idx; - var loc; - - for (var i = 0; i < points.length - 1; i++) { - if (ids[i] === activeID || ids[i + 1] === activeID) continue; - - var o = points[i]; - var s = geoVecSubtract(points[i + 1], o); - var v = geoVecSubtract(point, o); - var proj = geoVecDot(v, s) / geoVecDot(s, s); - var p; - - if (proj < 0) { - p = o; - } else if (proj > 1) { - p = points[i + 1]; - } else { - p = [o[0] + proj * s[0], o[1] + proj * s[1]]; - } - - var d = dist(p, point); - if (d < min) { - min = d; - idx = i + 1; - loc = projection.invert(p); - } - } - - if (idx !== undefined) { - return { index: idx, distance: min, loc: loc }; - } else { - return null; - } - } - - - // Test active (dragged or drawing) segments against inactive segments - // This is used to test e.g. multipolygon rings that cross - // `activeNodes` is the ring containing the activeID being dragged. - // `inactiveNodes` is the other ring to test against - function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) { - var actives = []; - var inactives = []; - var j, k, n1, n2, segment; - - // gather active segments (only segments in activeNodes that contain the activeID) - for (j = 0; j < activeNodes.length - 1; j++) { - n1 = activeNodes[j]; - n2 = activeNodes[j+1]; - segment = [n1.loc, n2.loc]; - if (n1.id === activeID || n2.id === activeID) { - actives.push(segment); - } - } - - // gather inactive segments - for (j = 0; j < inactiveNodes.length - 1; j++) { - n1 = inactiveNodes[j]; - n2 = inactiveNodes[j+1]; - segment = [n1.loc, n2.loc]; - inactives.push(segment); - } - - // test - for (j = 0; j < actives.length; j++) { - for (k = 0; k < inactives.length; k++) { - var p = actives[j]; - var q = inactives[k]; - var hit = geoLineIntersection(p, q); - if (hit) { - return true; - } - } - } - - return false; - } - - - // Test active (dragged or drawing) segments against inactive segments - // This is used to test whether a way intersects with itself. - function geoHasSelfIntersections(nodes, activeID) { - var actives = []; - var inactives = []; - var j, k; - - // group active and passive segments along the nodes - for (j = 0; j < nodes.length - 1; j++) { - var n1 = nodes[j]; - var n2 = nodes[j+1]; - var segment = [n1.loc, n2.loc]; - if (n1.id === activeID || n2.id === activeID) { - actives.push(segment); - } else { - inactives.push(segment); - } - } - - // test - for (j = 0; j < actives.length; j++) { - for (k = 0; k < inactives.length; k++) { - var p = actives[j]; - var q = inactives[k]; - // skip if segments share an endpoint - if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || - geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1]) ) { - continue; - } - - var hit = geoLineIntersection(p, q); - if (hit) { - var epsilon = 1e-8; - // skip if the hit is at the segment's endpoint - if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || - geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon) ) { - continue; - } else { - return true; - } - } - } - } - - return false; - } - - - // Return the intersection point of 2 line segments. - // From https://github.com/pgkelley4/line-segments-intersect - // This uses the vector cross product approach described below: - // http://stackoverflow.com/a/565282/786339 - function geoLineIntersection(a, b) { - var p = [a[0][0], a[0][1]]; - var p2 = [a[1][0], a[1][1]]; - var q = [b[0][0], b[0][1]]; - var q2 = [b[1][0], b[1][1]]; - var r = geoVecSubtract(p2, p); - var s = geoVecSubtract(q2, q); - var uNumerator = geoVecCross(geoVecSubtract(q, p), r); - var denominator = geoVecCross(r, s); - - if (uNumerator && denominator) { - var u = uNumerator / denominator; - var t = geoVecCross(geoVecSubtract(q, p), s) / denominator; - - if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) { - return geoVecInterp(p, p2, t); - } - } - - return null; - } - - - function geoPathIntersections(path1, path2) { - var intersections = []; - for (var i = 0; i < path1.length - 1; i++) { - for (var j = 0; j < path2.length - 1; j++) { - var a = [ path1[i], path1[i+1] ]; - var b = [ path2[j], path2[j+1] ]; - var hit = geoLineIntersection(a, b); - if (hit) { - intersections.push(hit); - } - } - } - return intersections; - } - - function geoPathHasIntersections(path1, path2) { - for (var i = 0; i < path1.length - 1; i++) { - for (var j = 0; j < path2.length - 1; j++) { - var a = [ path1[i], path1[i+1] ]; - var b = [ path2[j], path2[j+1] ]; - var hit = geoLineIntersection(a, b); - if (hit) { - return true; - } - } - } - return false; - } - - - // Return whether point is contained in polygon. - // - // `point` should be a 2-item array of coordinates. - // `polygon` should be an array of 2-item arrays of coordinates. - // - // From https://github.com/substack/point-in-polygon. - // ray-casting algorithm based on - // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - // - function geoPointInPolygon(point, polygon) { - var x = point[0]; - var y = point[1]; - var inside = false; - - for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { - var xi = polygon[i][0]; - var yi = polygon[i][1]; - var xj = polygon[j][0]; - var yj = polygon[j][1]; - - var intersect = ((yi > y) !== (yj > y)) && - (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - if (intersect) inside = !inside; - } - - return inside; - } - - - function geoPolygonContainsPolygon(outer, inner) { - return inner.every(function(point) { - return geoPointInPolygon(point, outer); - }); - } - - - function geoPolygonIntersectsPolygon(outer, inner, checkSegments) { - function testPoints(outer, inner) { - return inner.some(function(point) { - return geoPointInPolygon(point, outer); - }); - } - - return testPoints(outer, inner) || (!!checkSegments && geoPathHasIntersections(outer, inner)); - } - - - // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points - // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756 - function geoGetSmallestSurroundingRectangle(points) { - var hull = d3_polygonHull(points); - var centroid = d3_polygonCentroid(hull); - var minArea = Infinity; - var ssrExtent = []; - var ssrAngle = 0; - var c1 = hull[0]; - - for (var i = 0; i <= hull.length - 1; i++) { - var c2 = (i === hull.length - 1) ? hull[0] : hull[i + 1]; - var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]); - var poly = geoRotate(hull, -angle, centroid); - var extent = poly.reduce(function(extent, point) { - return extent.extend(geoExtent(point)); - }, geoExtent()); - - var area = extent.area(); - if (area < minArea) { - minArea = area; - ssrExtent = extent; - ssrAngle = angle; - } - c1 = c2; - } - - return { - poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid), - angle: ssrAngle - }; - } - - - function geoPathLength(path) { - var length = 0; - for (var i = 0; i < path.length - 1; i++) { - length += geoVecLength(path[i], path[i + 1]); - } - return length; - } - - - // If the given point is at the edge of the padded viewport, - // return a vector that will nudge the viewport in that direction - function geoViewportEdge(point, dimensions) { - var pad = [80, 20, 50, 20]; // top, right, bottom, left - var x = 0; - var y = 0; - - if (point[0] > dimensions[0] - pad[1]) - x = -10; - if (point[0] < pad[3]) - x = 10; - if (point[1] > dimensions[1] - pad[2]) - y = -10; - if (point[1] < pad[0]) - y = 10; - - if (x || y) { - return [x, y]; - } else { - return null; - } - } - - var noop$3 = {value: function() {}}; - - function dispatch() { - for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { - if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t); - _[t] = []; - } - return new Dispatch(_); - } - - function Dispatch(_) { - this._ = _; - } - - function parseTypenames(typenames, types) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); - return {type: t, name: name}; - }); - } - - Dispatch.prototype = dispatch.prototype = { - constructor: Dispatch, - on: function(typename, callback) { - var _ = this._, - T = parseTypenames(typename + "", _), - t, - i = -1, - n = T.length; - - // If no callback was specified, return the callback of the given type and name. - if (arguments.length < 2) { - while (++i < n) if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) return t; - return; - } - - // If a type was specified, set the callback for the given type and name. - // Otherwise, if a null callback was specified, remove callbacks of the given name. - if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); - while (++i < n) { - if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); - else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); - } - - return this; - }, - copy: function() { - var copy = {}, _ = this._; - for (var t in _) copy[t] = _[t].slice(); - return new Dispatch(copy); - }, - call: function(type, that) { - if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - }, - apply: function(type, that, args) { - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - } - }; - - function get$1(type, name) { - for (var i = 0, n = type.length, c; i < n; ++i) { - if ((c = type[i]).name === name) { - return c.value; - } - } - } - - function set(type, name, callback) { - for (var i = 0, n = type.length; i < n; ++i) { - if (type[i].name === name) { - type[i] = noop$3, type = type.slice(0, i).concat(type.slice(i + 1)); - break; - } - } - if (callback != null) type.push({name: name, value: callback}); - return type; - } - - var xhtml = "http://www.w3.org/1999/xhtml"; - - var namespaces = { - svg: "http://www.w3.org/2000/svg", - xhtml: xhtml, - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" - }; - - function namespace(name) { - var prefix = name += "", i = prefix.indexOf(":"); - if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); - return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; - } - - function creatorInherit(name) { - return function() { - var document = this.ownerDocument, - uri = this.namespaceURI; - return uri === xhtml && document.documentElement.namespaceURI === xhtml - ? document.createElement(name) - : document.createElementNS(uri, name); - }; - } - - function creatorFixed(fullname) { - return function() { - return this.ownerDocument.createElementNS(fullname.space, fullname.local); - }; - } - - function creator(name) { - var fullname = namespace(name); - return (fullname.local - ? creatorFixed - : creatorInherit)(fullname); - } - - function none() {} - - function selector(selector) { - return selector == null ? none : function() { - return this.querySelector(selector); - }; - } - - function selection_select(select) { - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - } - } - } - - return new Selection(subgroups, this._parents); - } - - function empty() { - return []; - } - - function selectorAll(selector) { - return selector == null ? empty : function() { - return this.querySelectorAll(selector); - }; - } - - function selection_selectAll(select) { - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - subgroups.push(select.call(node, node.__data__, i, group)); - parents.push(node); - } - } - } - - return new Selection(subgroups, parents); - } - - function matcher(selector) { - return function() { - return this.matches(selector); - }; - } - - function selection_filter(match) { - if (typeof match !== "function") match = matcher(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Selection(subgroups, this._parents); - } - - function sparse(update) { - return new Array(update.length); - } - - function selection_enter() { - return new Selection(this._enter || this._groups.map(sparse), this._parents); - } - - function EnterNode(parent, datum) { - this.ownerDocument = parent.ownerDocument; - this.namespaceURI = parent.namespaceURI; - this._next = null; - this._parent = parent; - this.__data__ = datum; - } - - EnterNode.prototype = { - constructor: EnterNode, - appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, - insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, - querySelector: function(selector) { return this._parent.querySelector(selector); }, - querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } - }; - - function constant(x) { - return function() { - return x; - }; - } - - var keyPrefix = "$"; // Protect against keys like “__proto__”. - - function bindIndex(parent, group, enter, update, exit, data) { - var i = 0, - node, - groupLength = group.length, - dataLength = data.length; - - // Put any non-null nodes that fit into update. - // Put any null nodes into enter. - // Put any remaining data into enter. - for (; i < dataLength; ++i) { - if (node = group[i]) { - node.__data__ = data[i]; - update[i] = node; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Put any non-null nodes that don’t fit into exit. - for (; i < groupLength; ++i) { - if (node = group[i]) { - exit[i] = node; - } - } - } - - function bindKey(parent, group, enter, update, exit, data, key) { - var i, - node, - nodeByKeyValue = {}, - groupLength = group.length, - dataLength = data.length, - keyValues = new Array(groupLength), - keyValue; - - // Compute the key for each node. - // If multiple nodes have the same key, the duplicates are added to exit. - for (i = 0; i < groupLength; ++i) { - if (node = group[i]) { - keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); - if (keyValue in nodeByKeyValue) { - exit[i] = node; - } else { - nodeByKeyValue[keyValue] = node; - } - } - } - - // Compute the key for each datum. - // If there a node associated with this key, join and add it to update. - // If there is not (or the key is a duplicate), add it to enter. - for (i = 0; i < dataLength; ++i) { - keyValue = keyPrefix + key.call(parent, data[i], i, data); - if (node = nodeByKeyValue[keyValue]) { - update[i] = node; - node.__data__ = data[i]; - nodeByKeyValue[keyValue] = null; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Add any remaining nodes that were not bound to data to exit. - for (i = 0; i < groupLength; ++i) { - if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { - exit[i] = node; - } - } - } - - function selection_data(value, key) { - if (!value) { - data = new Array(this.size()), j = -1; - this.each(function(d) { data[++j] = d; }); - return data; - } - - var bind = key ? bindKey : bindIndex, - parents = this._parents, - groups = this._groups; - - if (typeof value !== "function") value = constant(value); - - for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { - var parent = parents[j], - group = groups[j], - groupLength = group.length, - data = value.call(parent, parent && parent.__data__, j, parents), - dataLength = data.length, - enterGroup = enter[j] = new Array(dataLength), - updateGroup = update[j] = new Array(dataLength), - exitGroup = exit[j] = new Array(groupLength); - - bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); - - // Now connect the enter nodes to their following update node, such that - // appendChild can insert the materialized enter node before this node, - // rather than at the end of the parent node. - for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { - if (previous = enterGroup[i0]) { - if (i0 >= i1) i1 = i0 + 1; - while (!(next = updateGroup[i1]) && ++i1 < dataLength); - previous._next = next || null; - } - } - } - - update = new Selection(update, parents); - update._enter = enter; - update._exit = exit; - return update; - } - - function selection_exit() { - return new Selection(this._exit || this._groups.map(sparse), this._parents); - } - - function selection_join(onenter, onupdate, onexit) { - var enter = this.enter(), update = this, exit = this.exit(); - enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + ""); - if (onupdate != null) update = onupdate(update); - if (onexit == null) exit.remove(); else onexit(exit); - return enter && update ? enter.merge(update).order() : update; - } - - function selection_merge(selection) { - - for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Selection(merges, this._parents); - } - - function selection_order() { - - for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { - for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { - if (node = group[i]) { - if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - - return this; - } - - function selection_sort(compare) { - if (!compare) compare = ascending; - - function compareNode(a, b) { - return a && b ? compare(a.__data__, b.__data__) : !a - !b; - } - - for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group[i]) { - sortgroup[i] = node; - } - } - sortgroup.sort(compareNode); - } - - return new Selection(sortgroups, this._parents).order(); - } - - function ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - - function selection_call() { - var callback = arguments[0]; - arguments[0] = this; - callback.apply(null, arguments); - return this; - } - - function selection_nodes() { - var nodes = new Array(this.size()), i = -1; - this.each(function() { nodes[++i] = this; }); - return nodes; - } - - function selection_node() { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { - var node = group[i]; - if (node) return node; - } - } - - return null; - } - - function selection_size() { - var size = 0; - this.each(function() { ++size; }); - return size; - } - - function selection_empty() { - return !this.node(); - } - - function selection_each(callback) { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { - if (node = group[i]) callback.call(node, node.__data__, i, group); - } - } - - return this; - } - - function attrRemove(name) { - return function() { - this.removeAttribute(name); - }; - } - - function attrRemoveNS(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; - } - - function attrConstant(name, value) { - return function() { - this.setAttribute(name, value); - }; - } - - function attrConstantNS(fullname, value) { - return function() { - this.setAttributeNS(fullname.space, fullname.local, value); - }; - } - - function attrFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttribute(name); - else this.setAttribute(name, v); - }; - } - - function attrFunctionNS(fullname, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttributeNS(fullname.space, fullname.local); - else this.setAttributeNS(fullname.space, fullname.local, v); - }; - } - - function selection_attr(name, value) { - var fullname = namespace(name); - - if (arguments.length < 2) { - var node = this.node(); - return fullname.local - ? node.getAttributeNS(fullname.space, fullname.local) - : node.getAttribute(fullname); - } - - return this.each((value == null - ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" - ? (fullname.local ? attrFunctionNS : attrFunction) - : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); - } - - function defaultView(node) { - return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node - || (node.document && node) // node is a Window - || node.defaultView; // node is a Document - } - - function styleRemove(name) { - return function() { - this.style.removeProperty(name); - }; - } - - function styleConstant(name, value, priority) { - return function() { - this.style.setProperty(name, value, priority); - }; - } - - function styleFunction(name, value, priority) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.style.removeProperty(name); - else this.style.setProperty(name, v, priority); - }; - } - - function selection_style(name, value, priority) { - return arguments.length > 1 - ? this.each((value == null - ? styleRemove : typeof value === "function" - ? styleFunction - : styleConstant)(name, value, priority == null ? "" : priority)) - : styleValue(this.node(), name); - } - - function styleValue(node, name) { - return node.style.getPropertyValue(name) - || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); - } - - function propertyRemove(name) { - return function() { - delete this[name]; - }; - } - - function propertyConstant(name, value) { - return function() { - this[name] = value; - }; - } - - function propertyFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) delete this[name]; - else this[name] = v; - }; - } - - function selection_property(name, value) { - return arguments.length > 1 - ? this.each((value == null - ? propertyRemove : typeof value === "function" - ? propertyFunction - : propertyConstant)(name, value)) - : this.node()[name]; - } - - function classArray(string) { - return string.trim().split(/^|\s+/); - } - - function classList(node) { - return node.classList || new ClassList(node); - } - - function ClassList(node) { - this._node = node; - this._names = classArray(node.getAttribute("class") || ""); - } - - ClassList.prototype = { - add: function(name) { - var i = this._names.indexOf(name); - if (i < 0) { - this._names.push(name); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - remove: function(name) { - var i = this._names.indexOf(name); - if (i >= 0) { - this._names.splice(i, 1); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - contains: function(name) { - return this._names.indexOf(name) >= 0; - } - }; - - function classedAdd(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.add(names[i]); - } - - function classedRemove(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.remove(names[i]); - } - - function classedTrue(names) { - return function() { - classedAdd(this, names); - }; - } - - function classedFalse(names) { - return function() { - classedRemove(this, names); - }; - } - - function classedFunction(names, value) { - return function() { - (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); - }; - } - - function selection_classed(name, value) { - var names = classArray(name + ""); - - if (arguments.length < 2) { - var list = classList(this.node()), i = -1, n = names.length; - while (++i < n) if (!list.contains(names[i])) return false; - return true; - } - - return this.each((typeof value === "function" - ? classedFunction : value - ? classedTrue - : classedFalse)(names, value)); - } - - function textRemove() { - this.textContent = ""; - } - - function textConstant(value) { - return function() { - this.textContent = value; - }; - } - - function textFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - }; - } - - function selection_text(value) { - return arguments.length - ? this.each(value == null - ? textRemove : (typeof value === "function" - ? textFunction - : textConstant)(value)) - : this.node().textContent; - } - - function htmlRemove() { - this.innerHTML = ""; - } - - function htmlConstant(value) { - return function() { - this.innerHTML = value; - }; - } - - function htmlFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - }; - } - - function selection_html(value) { - return arguments.length - ? this.each(value == null - ? htmlRemove : (typeof value === "function" - ? htmlFunction - : htmlConstant)(value)) - : this.node().innerHTML; - } - - function raise() { - if (this.nextSibling) this.parentNode.appendChild(this); - } - - function selection_raise() { - return this.each(raise); - } - - function lower() { - if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); - } - - function selection_lower() { - return this.each(lower); - } - - function selection_append(name) { - var create = typeof name === "function" ? name : creator(name); - return this.select(function() { - return this.appendChild(create.apply(this, arguments)); - }); - } - - function constantNull() { - return null; - } - - function selection_insert(name, before) { - var create = typeof name === "function" ? name : creator(name), - select = before == null ? constantNull : typeof before === "function" ? before : selector(before); - return this.select(function() { - return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); - }); - } - - function remove() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - } - - function selection_remove() { - return this.each(remove); - } - - function selection_cloneShallow() { - var clone = this.cloneNode(false), parent = this.parentNode; - return parent ? parent.insertBefore(clone, this.nextSibling) : clone; - } - - function selection_cloneDeep() { - var clone = this.cloneNode(true), parent = this.parentNode; - return parent ? parent.insertBefore(clone, this.nextSibling) : clone; - } - - function selection_clone(deep) { - return this.select(deep ? selection_cloneDeep : selection_cloneShallow); - } - - function selection_datum(value) { - return arguments.length - ? this.property("__data__", value) - : this.node().__data__; - } - - var filterEvents = {}; - - var event = null; - - if (typeof document !== "undefined") { - var element = document.documentElement; - if (!("onmouseenter" in element)) { - filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; - } - } - - function filterContextListener(listener, index, group) { - listener = contextListener(listener, index, group); - return function(event) { - var related = event.relatedTarget; - if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { - listener.call(this, event); - } - }; - } - - function contextListener(listener, index, group) { - return function(event1) { - var event0 = event; // Events can be reentrant (e.g., focus). - event = event1; - try { - listener.call(this, this.__data__, index, group); - } finally { - event = event0; - } - }; - } - - function parseTypenames$1(typenames) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - return {type: t, name: name}; - }); - } - - function onRemove(typename) { - return function() { - var on = this.__on; - if (!on) return; - for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { - if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - } else { - on[++i] = o; - } - } - if (++i) on.length = i; - else delete this.__on; - }; - } - - function onAdd(typename, value, capture) { - var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; - return function(d, i, group) { - var on = this.__on, o, listener = wrap(value, i, group); - if (on) for (var j = 0, m = on.length; j < m; ++j) { - if ((o = on[j]).type === typename.type && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - this.addEventListener(o.type, o.listener = listener, o.capture = capture); - o.value = value; - return; - } - } - this.addEventListener(typename.type, listener, capture); - o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; - if (!on) this.__on = [o]; - else on.push(o); - }; - } - - function selection_on(typename, value, capture) { - var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t; - - if (arguments.length < 2) { - var on = this.node().__on; - if (on) for (var j = 0, m = on.length, o; j < m; ++j) { - for (i = 0, o = on[j]; i < n; ++i) { - if ((t = typenames[i]).type === o.type && t.name === o.name) { - return o.value; - } - } - } - return; - } - - on = value ? onAdd : onRemove; - if (capture == null) capture = false; - for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); - return this; - } - - function customEvent(event1, listener, that, args) { - var event0 = event; - event1.sourceEvent = event; - event = event1; - try { - return listener.apply(that, args); - } finally { - event = event0; - } - } - - function dispatchEvent(node, type, params) { - var window = defaultView(node), - event = window.CustomEvent; - - if (typeof event === "function") { - event = new event(type, params); - } else { - event = window.document.createEvent("Event"); - if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; - else event.initEvent(type, false, false); - } - - node.dispatchEvent(event); - } - - function dispatchConstant(type, params) { - return function() { - return dispatchEvent(this, type, params); - }; - } - - function dispatchFunction(type, params) { - return function() { - return dispatchEvent(this, type, params.apply(this, arguments)); - }; - } - - function selection_dispatch(type, params) { - return this.each((typeof params === "function" - ? dispatchFunction - : dispatchConstant)(type, params)); - } - - var root$1 = [null]; - - function Selection(groups, parents) { - this._groups = groups; - this._parents = parents; - } - - function selection() { - return new Selection([[document.documentElement]], root$1); - } - - Selection.prototype = selection.prototype = { - constructor: Selection, - select: selection_select, - selectAll: selection_selectAll, - filter: selection_filter, - data: selection_data, - enter: selection_enter, - exit: selection_exit, - join: selection_join, - merge: selection_merge, - order: selection_order, - sort: selection_sort, - call: selection_call, - nodes: selection_nodes, - node: selection_node, - size: selection_size, - empty: selection_empty, - each: selection_each, - attr: selection_attr, - style: selection_style, - property: selection_property, - classed: selection_classed, - text: selection_text, - html: selection_html, - raise: selection_raise, - lower: selection_lower, - append: selection_append, - insert: selection_insert, - remove: selection_remove, - clone: selection_clone, - datum: selection_datum, - on: selection_on, - dispatch: selection_dispatch - }; - - function select(selector) { - return typeof selector === "string" - ? new Selection([[document.querySelector(selector)]], [document.documentElement]) - : new Selection([[selector]], root$1); - } - - function sourceEvent() { - var current = event, source; - while (source = current.sourceEvent) current = source; - return current; - } - - function point(node, event) { - var svg = node.ownerSVGElement || node; - - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - point.x = event.clientX, point.y = event.clientY; - point = point.matrixTransform(node.getScreenCTM().inverse()); - return [point.x, point.y]; - } - - var rect = node.getBoundingClientRect(); - return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; - } - - function mouse(node) { - var event = sourceEvent(); - if (event.changedTouches) event = event.changedTouches[0]; - return point(node, event); - } - - function selectAll(selector) { - return typeof selector === "string" - ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) - : new Selection([selector == null ? [] : selector], root$1); - } - - function touch(node, touches, identifier) { - if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches; - - for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) { - if ((touch = touches[i]).identifier === identifier) { - return point(node, touch); - } - } - - return null; - } - - function nopropagation() { - event.stopImmediatePropagation(); - } - - function noevent() { - event.preventDefault(); - event.stopImmediatePropagation(); - } - - function dragDisable(view) { - var root = view.document.documentElement, - selection = select(view).on("dragstart.drag", noevent, true); - if ("onselectstart" in root) { - selection.on("selectstart.drag", noevent, true); - } else { - root.__noselect = root.style.MozUserSelect; - root.style.MozUserSelect = "none"; - } - } - - function yesdrag(view, noclick) { - var root = view.document.documentElement, - selection = select(view).on("dragstart.drag", null); - if (noclick) { - selection.on("click.drag", noevent, true); - setTimeout(function() { selection.on("click.drag", null); }, 0); - } - if ("onselectstart" in root) { - selection.on("selectstart.drag", null); - } else { - root.style.MozUserSelect = root.__noselect; - delete root.__noselect; - } - } - - function constant$1(x) { - return function() { - return x; - }; - } - - function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) { - this.target = target; - this.type = type; - this.subject = subject; - this.identifier = id; - this.active = active; - this.x = x; - this.y = y; - this.dx = dx; - this.dy = dy; - this._ = dispatch; - } - - DragEvent.prototype.on = function() { - var value = this._.on.apply(this._, arguments); - return value === this._ ? this : value; - }; - - // Ignore right-click, since that should open the context menu. - function defaultFilter() { - return !event.ctrlKey && !event.button; - } - - function defaultContainer() { - return this.parentNode; - } - - function defaultSubject(d) { - return d == null ? {x: event.x, y: event.y} : d; - } - - function defaultTouchable() { - return navigator.maxTouchPoints || ("ontouchstart" in this); - } - - function d3_drag() { - var filter = defaultFilter, - container = defaultContainer, - subject = defaultSubject, - touchable = defaultTouchable, - gestures = {}, - listeners = dispatch("start", "drag", "end"), - active = 0, - mousedownx, - mousedowny, - mousemoving, - touchending, - clickDistance2 = 0; - - function drag(selection) { - selection - .on("mousedown.drag", mousedowned) - .filter(touchable) - .on("touchstart.drag", touchstarted) - .on("touchmove.drag", touchmoved) - .on("touchend.drag touchcancel.drag", touchended) - .style("touch-action", "none") - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); - } - - function mousedowned() { - if (touchending || !filter.apply(this, arguments)) return; - var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments); - if (!gesture) return; - select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true); - dragDisable(event.view); - nopropagation(); - mousemoving = false; - mousedownx = event.clientX; - mousedowny = event.clientY; - gesture("start"); - } - - function mousemoved() { - noevent(); - if (!mousemoving) { - var dx = event.clientX - mousedownx, dy = event.clientY - mousedowny; - mousemoving = dx * dx + dy * dy > clickDistance2; - } - gestures.mouse("drag"); - } - - function mouseupped() { - select(event.view).on("mousemove.drag mouseup.drag", null); - yesdrag(event.view, mousemoving); - noevent(); - gestures.mouse("end"); - } - - function touchstarted() { - if (!filter.apply(this, arguments)) return; - var touches = event.changedTouches, - c = container.apply(this, arguments), - n = touches.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = beforestart(touches[i].identifier, c, touch, this, arguments)) { - nopropagation(); - gesture("start"); - } - } - } - - function touchmoved() { - var touches = event.changedTouches, - n = touches.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches[i].identifier]) { - noevent(); - gesture("drag"); - } - } - } - - function touchended() { - var touches = event.changedTouches, - n = touches.length, i, gesture; - - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches[i].identifier]) { - nopropagation(); - gesture("end"); - } - } - } - - function beforestart(id, container, point, that, args) { - var p = point(container, id), s, dx, dy, - sublisteners = listeners.copy(); - - if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() { - if ((event.subject = s = subject.apply(that, args)) == null) return false; - dx = s.x - p[0] || 0; - dy = s.y - p[1] || 0; - return true; - })) return; - - return function gesture(type) { - var p0 = p, n; - switch (type) { - case "start": gestures[id] = gesture, n = active++; break; - case "end": delete gestures[id], --active; // nobreak - case "drag": p = point(container, id), n = active; break; - } - customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]); - }; - } - - drag.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter; - }; - - drag.container = function(_) { - return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container; - }; - - drag.subject = function(_) { - return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject; - }; - - drag.touchable = function(_) { - return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable; - }; - - drag.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? drag : value; - }; - - drag.clickDistance = function(_) { - return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2); - }; - - return drag; - } - - function define$1(constructor, factory, prototype) { - constructor.prototype = factory.prototype = prototype; - prototype.constructor = constructor; - } - - function extend(parent, definition) { - var prototype = Object.create(parent.prototype); - for (var key in definition) prototype[key] = definition[key]; - return prototype; - } - - function Color() {} - - var darker = 0.7; - var brighter = 1 / darker; - - var reI = "\\s*([+-]?\\d+)\\s*", - reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*", - reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*", - reHex = /^#([0-9a-f]{3,8})$/, - reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"), - reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"), - reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"), - reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"), - reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"), - reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); - - var named = { - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - grey: 0x808080, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - rebeccapurple: 0x663399, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 - }; - - define$1(Color, color, { - copy: function(channels) { - return Object.assign(new this.constructor, this, channels); - }, - displayable: function() { - return this.rgb().displayable(); - }, - hex: color_formatHex, // Deprecated! Use color.formatHex. - formatHex: color_formatHex, - formatHsl: color_formatHsl, - formatRgb: color_formatRgb, - toString: color_formatRgb - }); - - function color_formatHex() { - return this.rgb().formatHex(); - } - - function color_formatHsl() { - return hslConvert(this).formatHsl(); - } - - function color_formatRgb() { - return this.rgb().formatRgb(); - } - - function color(format) { - var m, l; - format = (format + "").trim().toLowerCase(); - return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000 - : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00 - : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000 - : l === 4 ? rgba((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000 - : null) // invalid hex - : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) - : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) - : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) - : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) - : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) - : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) - : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins - : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) - : null; - } - - function rgbn(n) { - return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); - } - - function rgba(r, g, b, a) { - if (a <= 0) r = g = b = NaN; - return new Rgb(r, g, b, a); - } - - function rgbConvert(o) { - if (!(o instanceof Color)) o = color(o); - if (!o) return new Rgb; - o = o.rgb(); - return new Rgb(o.r, o.g, o.b, o.opacity); - } - - function rgb(r, g, b, opacity) { - return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); - } - - function Rgb(r, g, b, opacity) { - this.r = +r; - this.g = +g; - this.b = +b; - this.opacity = +opacity; - } - - define$1(Rgb, rgb, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - rgb: function() { - return this; - }, - displayable: function() { - return (-0.5 <= this.r && this.r < 255.5) - && (-0.5 <= this.g && this.g < 255.5) - && (-0.5 <= this.b && this.b < 255.5) - && (0 <= this.opacity && this.opacity <= 1); - }, - hex: rgb_formatHex, // Deprecated! Use color.formatHex. - formatHex: rgb_formatHex, - formatRgb: rgb_formatRgb, - toString: rgb_formatRgb - })); - - function rgb_formatHex() { - return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b); - } - - function rgb_formatRgb() { - var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); - return (a === 1 ? "rgb(" : "rgba(") - + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.b) || 0)) - + (a === 1 ? ")" : ", " + a + ")"); - } - - function hex$1(value) { - value = Math.max(0, Math.min(255, Math.round(value) || 0)); - return (value < 16 ? "0" : "") + value.toString(16); - } - - function hsla(h, s, l, a) { - if (a <= 0) h = s = l = NaN; - else if (l <= 0 || l >= 1) h = s = NaN; - else if (s <= 0) h = NaN; - return new Hsl(h, s, l, a); - } - - function hslConvert(o) { - if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Color)) o = color(o); - if (!o) return new Hsl; - if (o instanceof Hsl) return o; - o = o.rgb(); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - h = NaN, - s = max - min, - l = (max + min) / 2; - if (s) { - if (r === max) h = (g - b) / s + (g < b) * 6; - else if (g === max) h = (b - r) / s + 2; - else h = (r - g) / s + 4; - s /= l < 0.5 ? max + min : 2 - max - min; - h *= 60; - } else { - s = l > 0 && l < 1 ? 0 : h; - } - return new Hsl(h, s, l, o.opacity); - } - - function hsl(h, s, l, opacity) { - return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); - } - - function Hsl(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; - } - - define$1(Hsl, hsl, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - rgb: function() { - var h = this.h % 360 + (this.h < 0) * 360, - s = isNaN(h) || isNaN(this.s) ? 0 : this.s, - l = this.l, - m2 = l + (l < 0.5 ? l : 1 - l) * s, - m1 = 2 * l - m2; - return new Rgb( - hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), - hsl2rgb(h, m1, m2), - hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), - this.opacity - ); - }, - displayable: function() { - return (0 <= this.s && this.s <= 1 || isNaN(this.s)) - && (0 <= this.l && this.l <= 1) - && (0 <= this.opacity && this.opacity <= 1); - }, - formatHsl: function() { - var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); - return (a === 1 ? "hsl(" : "hsla(") - + (this.h || 0) + ", " - + (this.s || 0) * 100 + "%, " - + (this.l || 0) * 100 + "%" - + (a === 1 ? ")" : ", " + a + ")"); - } - })); - - /* From FvD 13.37, CSS Color Module Level 3 */ - function hsl2rgb(h, m1, m2) { - return (h < 60 ? m1 + (m2 - m1) * h / 60 - : h < 180 ? m2 - : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 - : m1) * 255; - } - - function constant$2(x) { - return function() { - return x; - }; - } - - function linear(a, d) { - return function(t) { - return a + t * d; - }; - } - - function exponential(a, b, y) { - return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { - return Math.pow(a + t * b, y); - }; - } - - function gamma(y) { - return (y = +y) === 1 ? nogamma : function(a, b) { - return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a); - }; - } - - function nogamma(a, b) { - var d = b - a; - return d ? linear(a, d) : constant$2(isNaN(a) ? b : a); - } - - var d3_interpolateRgb = (function rgbGamma(y) { - var color = gamma(y); - - function rgb$1(start, end) { - var r = color((start = rgb(start)).r, (end = rgb(end)).r), - g = color(start.g, end.g), - b = color(start.b, end.b), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.r = r(t); - start.g = g(t); - start.b = b(t); - start.opacity = opacity(t); - return start + ""; - }; - } - - rgb$1.gamma = rgbGamma; - - return rgb$1; - })(1); - - function numberArray(a, b) { - if (!b) b = []; - var n = a ? Math.min(b.length, a.length) : 0, - c = b.slice(), - i; - return function(t) { - for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t; - return c; - }; - } - - function isNumberArray(x) { - return ArrayBuffer.isView(x) && !(x instanceof DataView); - } - - function genericArray(a, b) { - var nb = b ? b.length : 0, - na = a ? Math.min(nb, a.length) : 0, - x = new Array(na), - c = new Array(nb), - i; - - for (i = 0; i < na; ++i) x[i] = interpolate(a[i], b[i]); - for (; i < nb; ++i) c[i] = b[i]; - - return function(t) { - for (i = 0; i < na; ++i) c[i] = x[i](t); - return c; - }; - } - - function date(a, b) { - var d = new Date; - return a = +a, b = +b, function(t) { - return d.setTime(a * (1 - t) + b * t), d; - }; - } - - function d3_interpolateNumber(a, b) { - return a = +a, b = +b, function(t) { - return a * (1 - t) + b * t; - }; - } - - function object(a, b) { - var i = {}, - c = {}, - k; - - if (a === null || typeof a !== "object") a = {}; - if (b === null || typeof b !== "object") b = {}; - - for (k in b) { - if (k in a) { - i[k] = interpolate(a[k], b[k]); - } else { - c[k] = b[k]; - } - } - - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; - } - - var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, - reB = new RegExp(reA.source, "g"); - - function zero(b) { - return function() { - return b; - }; - } - - function one(b) { - return function(t) { - return b(t) + ""; - }; - } - - function interpolateString(a, b) { - var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b - am, // current match in a - bm, // current match in b - bs, // string preceding current number in b, if any - i = -1, // index in s - s = [], // string constants and placeholders - q = []; // number interpolators - - // Coerce inputs to strings. - a = a + "", b = b + ""; - - // Interpolate pairs of numbers in a & b. - while ((am = reA.exec(a)) - && (bm = reB.exec(b))) { - if ((bs = bm.index) > bi) { // a string precedes the next number in b - bs = b.slice(bi, bs); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match - if (s[i]) s[i] += bm; // coalesce with previous string - else s[++i] = bm; - } else { // interpolate non-matching numbers - s[++i] = null; - q.push({i: i, x: d3_interpolateNumber(am, bm)}); - } - bi = reB.lastIndex; - } - - // Add remains of b. - if (bi < b.length) { - bs = b.slice(bi); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - - // Special optimization for only a single match. - // Otherwise, interpolate each of the numbers and rejoin the string. - return s.length < 2 ? (q[0] - ? one(q[0].x) - : zero(b)) - : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); - } - - function interpolate(a, b) { - var t = typeof b, c; - return b == null || t === "boolean" ? constant$2(b) - : (t === "number" ? d3_interpolateNumber - : t === "string" ? ((c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString) - : b instanceof color ? d3_interpolateRgb - : b instanceof Date ? date - : isNumberArray(b) ? numberArray - : Array.isArray(b) ? genericArray - : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object - : d3_interpolateNumber)(a, b); - } - - function interpolateRound(a, b) { - return a = +a, b = +b, function(t) { - return Math.round(a * (1 - t) + b * t); - }; - } - - var degrees$1 = 180 / Math.PI; - - var identity$1 = { - translateX: 0, - translateY: 0, - rotate: 0, - skewX: 0, - scaleX: 1, - scaleY: 1 - }; - - function decompose(a, b, c, d, e, f) { - var scaleX, scaleY, skewX; - if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; - if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; - if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; - if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; - return { - translateX: e, - translateY: f, - rotate: Math.atan2(b, a) * degrees$1, - skewX: Math.atan(skewX) * degrees$1, - scaleX: scaleX, - scaleY: scaleY - }; - } - - var cssNode, - cssRoot, - cssView, - svgNode; - - function parseCss(value) { - if (value === "none") return identity$1; - if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; - cssNode.style.transform = value; - value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); - cssRoot.removeChild(cssNode); - value = value.slice(7, -1).split(","); - return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); - } - - function parseSvg(value) { - if (value == null) return identity$1; - if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); - svgNode.setAttribute("transform", value); - if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1; - value = value.matrix; - return decompose(value.a, value.b, value.c, value.d, value.e, value.f); - } - - function interpolateTransform(parse, pxComma, pxParen, degParen) { - - function pop(s) { - return s.length ? s.pop() + " " : ""; - } - - function translate(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push("translate(", null, pxComma, null, pxParen); - q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)}); - } else if (xb || yb) { - s.push("translate(" + xb + pxComma + yb + pxParen); - } - } - - function rotate(a, b, s, q) { - if (a !== b) { - if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path - q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: d3_interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "rotate(" + b + degParen); - } - } - - function skewX(a, b, s, q) { - if (a !== b) { - q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: d3_interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "skewX(" + b + degParen); - } - } - - function scale(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push(pop(s) + "scale(", null, ",", null, ")"); - q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)}); - } else if (xb !== 1 || yb !== 1) { - s.push(pop(s) + "scale(" + xb + "," + yb + ")"); - } - } - - return function(a, b) { - var s = [], // string constants and placeholders - q = []; // number interpolators - a = parse(a), b = parse(b); - translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); - rotate(a.rotate, b.rotate, s, q); - skewX(a.skewX, b.skewX, s, q); - scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); - a = b = null; // gc - return function(t) { - var i = -1, n = q.length, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - }; - } - - var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); - var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); - - var rho = Math.SQRT2, - rho2 = 2, - rho4 = 4, - epsilon2$1 = 1e-12; - - function cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; - } - - function sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; - } - - function tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); - } - - // p0 = [ux0, uy0, w0] - // p1 = [ux1, uy1, w1] - function interpolateZoom(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], - ux1 = p1[0], uy1 = p1[1], w1 = p1[2], - dx = ux1 - ux0, - dy = uy1 - uy0, - d2 = dx * dx + dy * dy, - i, - S; - - // Special case for u0 ≅ u1. - if (d2 < epsilon2$1) { - S = Math.log(w1 / w0) / rho; - i = function(t) { - return [ - ux0 + t * dx, - uy0 + t * dy, - w0 * Math.exp(rho * t * S) - ]; - }; - } - - // General case. - else { - var d1 = Math.sqrt(d2), - b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), - b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), - r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), - r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); - S = (r1 - r0) / rho; - i = function(t) { - var s = t * S, - coshr0 = cosh(r0), - u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); - return [ - ux0 + u * dx, - uy0 + u * dy, - w0 * coshr0 / cosh(rho * s + r0) - ]; - }; - } - - i.duration = S * 1000; - - return i; - } - - function d3_quantize(interpolator, n) { - var samples = new Array(n); - for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1)); - return samples; - } - - var frame = 0, // is an animation frame pending? - timeout = 0, // is a timeout pending? - interval = 0, // are any timers active? - pokeDelay = 1000, // how frequently we check for clock skew - taskHead, - taskTail, - clockLast = 0, - clockNow = 0, - clockSkew = 0, - clock = typeof performance === "object" && performance.now ? performance : Date, - setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; - - function now() { - return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); - } - - function clearNow() { - clockNow = 0; - } - - function Timer() { - this._call = - this._time = - this._next = null; - } - - Timer.prototype = timer.prototype = { - constructor: Timer, - restart: function(callback, delay, time) { - if (typeof callback !== "function") throw new TypeError("callback is not a function"); - time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); - if (!this._next && taskTail !== this) { - if (taskTail) taskTail._next = this; - else taskHead = this; - taskTail = this; - } - this._call = callback; - this._time = time; - sleep(); - }, - stop: function() { - if (this._call) { - this._call = null; - this._time = Infinity; - sleep(); - } - } - }; - - function timer(callback, delay, time) { - var t = new Timer; - t.restart(callback, delay, time); - return t; - } - - function timerFlush() { - now(); // Get the current time, if not already set. - ++frame; // Pretend we’ve set an alarm, if we haven’t already. - var t = taskHead, e; - while (t) { - if ((e = clockNow - t._time) >= 0) t._call.call(null, e); - t = t._next; - } - --frame; - } - - function wake() { - clockNow = (clockLast = clock.now()) + clockSkew; - frame = timeout = 0; - try { - timerFlush(); - } finally { - frame = 0; - nap(); - clockNow = 0; - } - } - - function poke() { - var now = clock.now(), delay = now - clockLast; - if (delay > pokeDelay) clockSkew -= delay, clockLast = now; - } - - function nap() { - var t0, t1 = taskHead, t2, time = Infinity; - while (t1) { - if (t1._call) { - if (time > t1._time) time = t1._time; - t0 = t1, t1 = t1._next; - } else { - t2 = t1._next, t1._next = null; - t1 = t0 ? t0._next = t2 : taskHead = t2; - } - } - taskTail = t0; - sleep(time); - } - - function sleep(time) { - if (frame) return; // Soonest alarm already set, or will be. - if (timeout) timeout = clearTimeout(timeout); - var delay = time - clockNow; // Strictly less than if we recomputed clockNow. - if (delay > 24) { - if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); - if (interval) interval = clearInterval(interval); - } else { - if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); - frame = 1, setFrame(wake); - } - } - - function d3_timeout(callback, delay, time) { - var t = new Timer; - delay = delay == null ? 0 : +delay; - t.restart(function(elapsed) { - t.stop(); - callback(elapsed + delay); - }, delay, time); - return t; - } - - var emptyOn = dispatch("start", "end", "cancel", "interrupt"); - var emptyTween = []; - - var CREATED = 0; - var SCHEDULED = 1; - var STARTING = 2; - var STARTED = 3; - var RUNNING = 4; - var ENDING = 5; - var ENDED = 6; - - function schedule(node, name, id, index, group, timing) { - var schedules = node.__transition; - if (!schedules) node.__transition = {}; - else if (id in schedules) return; - create$7(node, id, { - name: name, - index: index, // For context during callback. - group: group, // For context during callback. - on: emptyOn, - tween: emptyTween, - time: timing.time, - delay: timing.delay, - duration: timing.duration, - ease: timing.ease, - timer: null, - state: CREATED - }); - } - - function init(node, id) { - var schedule = get$2(node, id); - if (schedule.state > CREATED) throw new Error("too late; already scheduled"); - return schedule; - } - - function set$1(node, id) { - var schedule = get$2(node, id); - if (schedule.state > STARTED) throw new Error("too late; already running"); - return schedule; - } - - function get$2(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); - return schedule; - } - - function create$7(node, id, self) { - var schedules = node.__transition, - tween; - - // Initialize the self timer when the transition is created. - // Note the actual delay is not known until the first callback! - schedules[id] = self; - self.timer = timer(schedule, 0, self.time); - - function schedule(elapsed) { - self.state = SCHEDULED; - self.timer.restart(start, self.delay, self.time); - - // If the elapsed delay is less than our first sleep, start immediately. - if (self.delay <= elapsed) start(elapsed - self.delay); - } - - function start(elapsed) { - var i, j, n, o; - - // If the state is not SCHEDULED, then we previously errored on start. - if (self.state !== SCHEDULED) return stop(); - - for (i in schedules) { - o = schedules[i]; - if (o.name !== self.name) continue; - - // While this element already has a starting transition during this frame, - // defer starting an interrupting transition until that transition has a - // chance to tick (and possibly end); see d3/d3-transition#54! - if (o.state === STARTED) return d3_timeout(start); - - // Interrupt the active transition, if any. - if (o.state === RUNNING) { - o.state = ENDED; - o.timer.stop(); - o.on.call("interrupt", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - - // Cancel any pre-empted transitions. - else if (+i < id) { - o.state = ENDED; - o.timer.stop(); - o.on.call("cancel", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - } - - // Defer the first tick to end of the current frame; see d3/d3#1576. - // Note the transition may be canceled after start and before the first tick! - // Note this must be scheduled before the start event; see d3/d3-transition#16! - // Assuming this is successful, subsequent callbacks go straight to tick. - d3_timeout(function() { - if (self.state === STARTED) { - self.state = RUNNING; - self.timer.restart(tick, self.delay, self.time); - tick(elapsed); - } - }); - - // Dispatch the start event. - // Note this must be done before the tween are initialized. - self.state = STARTING; - self.on.call("start", node, node.__data__, self.index, self.group); - if (self.state !== STARTING) return; // interrupted - self.state = STARTED; - - // Initialize the tween, deleting null tween. - tween = new Array(n = self.tween.length); - for (i = 0, j = -1; i < n; ++i) { - if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { - tween[++j] = o; - } - } - tween.length = j + 1; - } - - function tick(elapsed) { - var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), - i = -1, - n = tween.length; - - while (++i < n) { - tween[i].call(node, t); - } - - // Dispatch the end event. - if (self.state === ENDING) { - self.on.call("end", node, node.__data__, self.index, self.group); - stop(); - } - } - - function stop() { - self.state = ENDED; - self.timer.stop(); - delete schedules[id]; - for (var i in schedules) return; // eslint-disable-line no-unused-vars - delete node.__transition; - } - } - - function interrupt(node, name) { - var schedules = node.__transition, - schedule, - active, - empty = true, - i; - - if (!schedules) return; - - name = name == null ? null : name + ""; - - for (i in schedules) { - if ((schedule = schedules[i]).name !== name) { empty = false; continue; } - active = schedule.state > STARTING && schedule.state < ENDING; - schedule.state = ENDED; - schedule.timer.stop(); - schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group); - delete schedules[i]; - } - - if (empty) delete node.__transition; - } - - function selection_interrupt(name) { - return this.each(function() { - interrupt(this, name); - }); - } - - function tweenRemove(id, name) { - var tween0, tween1; - return function() { - var schedule = set$1(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = tween0 = tween; - for (var i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1 = tween1.slice(); - tween1.splice(i, 1); - break; - } - } - } - - schedule.tween = tween1; - }; - } - - function tweenFunction(id, name, value) { - var tween0, tween1; - if (typeof value !== "function") throw new Error; - return function() { - var schedule = set$1(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = (tween0 = tween).slice(); - for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1[i] = t; - break; - } - } - if (i === n) tween1.push(t); - } - - schedule.tween = tween1; - }; - } - - function transition_tween(name, value) { - var id = this._id; - - name += ""; - - if (arguments.length < 2) { - var tween = get$2(this.node(), id).tween; - for (var i = 0, n = tween.length, t; i < n; ++i) { - if ((t = tween[i]).name === name) { - return t.value; - } - } - return null; - } - - return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); - } - - function tweenValue(transition, name, value) { - var id = transition._id; - - transition.each(function() { - var schedule = set$1(this, id); - (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments); - }); - - return function(node) { - return get$2(node, id).value[name]; - }; - } - - function interpolate$1(a, b) { - var c; - return (typeof b === "number" ? d3_interpolateNumber - : b instanceof color ? d3_interpolateRgb - : (c = color(b)) ? (b = c, d3_interpolateRgb) - : interpolateString)(a, b); - } - - function attrRemove$1(name) { - return function() { - this.removeAttribute(name); - }; - } - - function attrRemoveNS$1(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; - } - - function attrConstant$1(name, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = this.getAttribute(name); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function attrConstantNS$1(fullname, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = this.getAttributeNS(fullname.space, fullname.local); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function attrFunction$1(name, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0, value1 = value(this), string1; - if (value1 == null) return void this.removeAttribute(name); - string0 = this.getAttribute(name); - string1 = value1 + ""; - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function attrFunctionNS$1(fullname, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0, value1 = value(this), string1; - if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); - string0 = this.getAttributeNS(fullname.space, fullname.local); - string1 = value1 + ""; - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function transition_attr(name, value) { - var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1; - return this.attrTween(name, typeof value === "function" - ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) - : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) - : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value)); - } - - function attrInterpolate(name, i) { - return function(t) { - this.setAttribute(name, i.call(this, t)); - }; - } - - function attrInterpolateNS(fullname, i) { - return function(t) { - this.setAttributeNS(fullname.space, fullname.local, i.call(this, t)); - }; - } - - function attrTweenNS(fullname, value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i); - return t0; - } - tween._value = value; - return tween; - } - - function attrTween(name, value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i); - return t0; - } - tween._value = value; - return tween; - } - - function transition_attrTween(name, value) { - var key = "attr." + name; - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - var fullname = namespace(name); - return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); - } - - function delayFunction(id, value) { - return function() { - init(this, id).delay = +value.apply(this, arguments); - }; - } - - function delayConstant(id, value) { - return value = +value, function() { - init(this, id).delay = value; - }; - } - - function transition_delay(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? delayFunction - : delayConstant)(id, value)) - : get$2(this.node(), id).delay; - } - - function durationFunction(id, value) { - return function() { - set$1(this, id).duration = +value.apply(this, arguments); - }; - } - - function durationConstant(id, value) { - return value = +value, function() { - set$1(this, id).duration = value; - }; - } - - function transition_duration(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? durationFunction - : durationConstant)(id, value)) - : get$2(this.node(), id).duration; - } - - function easeConstant(id, value) { - if (typeof value !== "function") throw new Error; - return function() { - set$1(this, id).ease = value; - }; - } - - function transition_ease(value) { - var id = this._id; - - return arguments.length - ? this.each(easeConstant(id, value)) - : get$2(this.node(), id).ease; - } - - function transition_filter(match) { - if (typeof match !== "function") match = matcher(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Transition(subgroups, this._parents, this._name, this._id); - } - - function transition_merge(transition) { - if (transition._id !== this._id) throw new Error; - - for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Transition(merges, this._parents, this._name, this._id); - } - - function start(name) { - return (name + "").trim().split(/^|\s+/).every(function(t) { - var i = t.indexOf("."); - if (i >= 0) t = t.slice(0, i); - return !t || t === "start"; - }); - } - - function onFunction(id, name, listener) { - var on0, on1, sit = start(name) ? init : set$1; - return function() { - var schedule = sit(this, id), - on = schedule.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); - - schedule.on = on1; - }; - } - - function transition_on(name, listener) { - var id = this._id; - - return arguments.length < 2 - ? get$2(this.node(), id).on.on(name) - : this.each(onFunction(id, name, listener)); - } - - function removeFunction(id) { - return function() { - var parent = this.parentNode; - for (var i in this.__transition) if (+i !== id) return; - if (parent) parent.removeChild(this); - }; - } - - function transition_remove() { - return this.on("end.remove", removeFunction(this._id)); - } - - function transition_select(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - schedule(subgroup[i], name, id, i, subgroup, get$2(node, id)); - } - } - } - - return new Transition(subgroups, this._parents, name, id); - } - - function transition_selectAll(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - for (var children = select.call(node, node.__data__, i, group), child, inherit = get$2(node, id), k = 0, l = children.length; k < l; ++k) { - if (child = children[k]) { - schedule(child, name, id, k, children, inherit); - } - } - subgroups.push(children); - parents.push(node); - } - } - } - - return new Transition(subgroups, parents, name, id); - } - - var Selection$1 = selection.prototype.constructor; - - function transition_selection() { - return new Selection$1(this._groups, this._parents); - } - - function styleNull(name, interpolate) { - var string00, - string10, - interpolate0; - return function() { - var string0 = styleValue(this, name), - string1 = (this.style.removeProperty(name), styleValue(this, name)); - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, string10 = string1); - }; - } - - function styleRemove$1(name) { - return function() { - this.style.removeProperty(name); - }; - } - - function styleConstant$1(name, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = styleValue(this, name); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function styleFunction$1(name, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0 = styleValue(this, name), - value1 = value(this), - string1 = value1 + ""; - if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name)); - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function styleMaybeRemove(id, name) { - var on0, on1, listener0, key = "style." + name, event = "end." + key, remove; - return function() { - var schedule = set$1(this, id), - on = schedule.on, - listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener); - - schedule.on = on1; - }; - } - - function transition_style(name, value, priority) { - var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1; - return value == null ? this - .styleTween(name, styleNull(name, i)) - .on("end.style." + name, styleRemove$1(name)) - : typeof value === "function" ? this - .styleTween(name, styleFunction$1(name, i, tweenValue(this, "style." + name, value))) - .each(styleMaybeRemove(this._id, name)) - : this - .styleTween(name, styleConstant$1(name, i, value), priority) - .on("end.style." + name, null); - } - - function styleInterpolate(name, i, priority) { - return function(t) { - this.style.setProperty(name, i.call(this, t), priority); - }; - } - - function styleTween(name, value, priority) { - var t, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority); - return t; - } - tween._value = value; - return tween; - } - - function transition_styleTween(name, value, priority) { - var key = "style." + (name += ""); - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); - } - - function textConstant$1(value) { - return function() { - this.textContent = value; - }; - } - - function textFunction$1(value) { - return function() { - var value1 = value(this); - this.textContent = value1 == null ? "" : value1; - }; - } - - function transition_text(value) { - return this.tween("text", typeof value === "function" - ? textFunction$1(tweenValue(this, "text", value)) - : textConstant$1(value == null ? "" : value + "")); - } - - function textInterpolate(i) { - return function(t) { - this.textContent = i.call(this, t); - }; - } - - function textTween(value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && textInterpolate(i); - return t0; - } - tween._value = value; - return tween; - } - - function transition_textTween(value) { - var key = "text"; - if (arguments.length < 1) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, textTween(value)); - } - - function transition_transition() { - var name = this._name, - id0 = this._id, - id1 = newId(); - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - var inherit = get$2(node, id0); - schedule(node, name, id1, i, group, { - time: inherit.time + inherit.delay + inherit.duration, - delay: 0, - duration: inherit.duration, - ease: inherit.ease - }); - } - } - } - - return new Transition(groups, this._parents, name, id1); - } - - function transition_end() { - var on0, on1, that = this, id = that._id, size = that.size(); - return new Promise(function(resolve, reject) { - var cancel = {value: reject}, - end = {value: function() { if (--size === 0) resolve(); }}; - - that.each(function() { - var schedule = set$1(this, id), - on = schedule.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) { - on1 = (on0 = on).copy(); - on1._.cancel.push(cancel); - on1._.interrupt.push(cancel); - on1._.end.push(end); - } - - schedule.on = on1; - }); - }); - } - - var id$3 = 0; - - function Transition(groups, parents, name, id) { - this._groups = groups; - this._parents = parents; - this._name = name; - this._id = id; - } - - function transition(name) { - return selection().transition(name); - } - - function newId() { - return ++id$3; - } - - var selection_prototype = selection.prototype; - - Transition.prototype = transition.prototype = { - constructor: Transition, - select: transition_select, - selectAll: transition_selectAll, - filter: transition_filter, - merge: transition_merge, - selection: transition_selection, - transition: transition_transition, - call: selection_prototype.call, - nodes: selection_prototype.nodes, - node: selection_prototype.node, - size: selection_prototype.size, - empty: selection_prototype.empty, - each: selection_prototype.each, - on: transition_on, - attr: transition_attr, - attrTween: transition_attrTween, - style: transition_style, - styleTween: transition_styleTween, - text: transition_text, - textTween: transition_textTween, - remove: transition_remove, - tween: transition_tween, - delay: transition_delay, - duration: transition_duration, - ease: transition_ease, - end: transition_end - }; - - function linear$1(t) { - return +t; - } - - function cubicInOut(t) { - return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; - } - - var defaultTiming = { - time: null, // Set on use. - delay: 0, - duration: 250, - ease: cubicInOut - }; - - function inherit(node, id) { - var timing; - while (!(timing = node.__transition) || !(timing = timing[id])) { - if (!(node = node.parentNode)) { - return defaultTiming.time = now(), defaultTiming; - } - } - return timing; - } - - function selection_transition(name) { - var id, - timing; - - if (name instanceof Transition) { - id = name._id, name = name._name; - } else { - id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; - } - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - schedule(node, name, id, i, group, timing || inherit(node, id)); - } - } - } - - return new Transition(groups, this._parents, name, id); - } - - selection.prototype.interrupt = selection_interrupt; - selection.prototype.transition = selection_transition; - - function constant$3(x) { - return function() { - return x; - }; - } - - function ZoomEvent(target, type, transform) { - this.target = target; - this.type = type; - this.transform = transform; - } - - function Transform(k, x, y) { - this.k = k; - this.x = x; - this.y = y; - } - - Transform.prototype = { - constructor: Transform, - scale: function(k) { - return k === 1 ? this : new Transform(this.k * k, this.x, this.y); - }, - translate: function(x, y) { - return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); - }, - apply: function(point) { - return [point[0] * this.k + this.x, point[1] * this.k + this.y]; - }, - applyX: function(x) { - return x * this.k + this.x; - }, - applyY: function(y) { - return y * this.k + this.y; - }, - invert: function(location) { - return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; - }, - invertX: function(x) { - return (x - this.x) / this.k; - }, - invertY: function(y) { - return (y - this.y) / this.k; - }, - rescaleX: function(x) { - return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); - }, - rescaleY: function(y) { - return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); - }, - toString: function() { - return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; - } - }; - - var identity$2 = new Transform(1, 0, 0); - - function nopropagation$1() { - event.stopImmediatePropagation(); - } - - function noevent$1() { - event.preventDefault(); - event.stopImmediatePropagation(); - } - - // Ignore right-click, since that should open the context menu. - function defaultFilter$1() { - return !event.ctrlKey && !event.button; - } - - function defaultExtent() { - var e = this; - if (e instanceof SVGElement) { - e = e.ownerSVGElement || e; - if (e.hasAttribute("viewBox")) { - e = e.viewBox.baseVal; - return [[e.x, e.y], [e.x + e.width, e.y + e.height]]; - } - return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]; - } - return [[0, 0], [e.clientWidth, e.clientHeight]]; - } - - function defaultTransform() { - return this.__zoom || identity$2; - } - - function defaultWheelDelta() { - return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002); - } - - function defaultTouchable$1() { - return navigator.maxTouchPoints || ("ontouchstart" in this); - } - - function defaultConstrain(transform, extent, translateExtent) { - var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], - dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], - dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], - dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; - return transform.translate( - dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), - dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1) - ); - } - - function d3_zoom() { - var filter = defaultFilter$1, - extent = defaultExtent, - constrain = defaultConstrain, - wheelDelta = defaultWheelDelta, - touchable = defaultTouchable$1, - scaleExtent = [0, Infinity], - translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], - duration = 250, - interpolate = interpolateZoom, - listeners = dispatch("start", "zoom", "end"), - touchstarting, - touchending, - touchDelay = 500, - wheelDelay = 150, - clickDistance2 = 0; - - function zoom(selection) { - selection - .property("__zoom", defaultTransform) - .on("wheel.zoom", wheeled) - .on("mousedown.zoom", mousedowned) - .on("dblclick.zoom", dblclicked) - .filter(touchable) - .on("touchstart.zoom", touchstarted) - .on("touchmove.zoom", touchmoved) - .on("touchend.zoom touchcancel.zoom", touchended) - .style("touch-action", "none") - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); - } - - zoom.transform = function(collection, transform, point) { - var selection = collection.selection ? collection.selection() : collection; - selection.property("__zoom", defaultTransform); - if (collection !== selection) { - schedule(collection, transform, point); - } else { - selection.interrupt().each(function() { - gesture(this, arguments) - .start() - .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform) - .end(); - }); - } - }; - - zoom.scaleBy = function(selection, k, p) { - zoom.scaleTo(selection, function() { - var k0 = this.__zoom.k, - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return k0 * k1; - }, p); - }; - - zoom.scaleTo = function(selection, k, p) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t0 = this.__zoom, - p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, - p1 = t0.invert(p0), - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); - }, p); - }; - - zoom.translateBy = function(selection, x, y) { - zoom.transform(selection, function() { - return constrain(this.__zoom.translate( - typeof x === "function" ? x.apply(this, arguments) : x, - typeof y === "function" ? y.apply(this, arguments) : y - ), extent.apply(this, arguments), translateExtent); - }); - }; - - zoom.translateTo = function(selection, x, y, p) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t = this.__zoom, - p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p; - return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate( - typeof x === "function" ? -x.apply(this, arguments) : -x, - typeof y === "function" ? -y.apply(this, arguments) : -y - ), e, translateExtent); - }, p); - }; - - function scale(transform, k) { - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); - return k === transform.k ? transform : new Transform(k, transform.x, transform.y); - } - - function translate(transform, p0, p1) { - var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k; - return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); - } - - function centroid(extent) { - return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; - } - - function schedule(transition, transform, point) { - transition - .on("start.zoom", function() { gesture(this, arguments).start(); }) - .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); }) - .tween("zoom", function() { - var that = this, - args = arguments, - g = gesture(that, args), - e = extent.apply(that, args), - p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, - w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), - a = that.__zoom, - b = typeof transform === "function" ? transform.apply(that, args) : transform, - i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); - return function(t) { - if (t === 1) t = b; // Avoid rounding error on end. - else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } - g.zoom(null, t); - }; - }); - } - - function gesture(that, args, clean) { - return (!clean && that.__zooming) || new Gesture(that, args); - } - - function Gesture(that, args) { - this.that = that; - this.args = args; - this.active = 0; - this.extent = extent.apply(that, args); - this.taps = 0; - } - - Gesture.prototype = { - start: function() { - if (++this.active === 1) { - this.that.__zooming = this; - this.emit("start"); - } - return this; - }, - zoom: function(key, transform) { - if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]); - if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]); - if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]); - this.that.__zoom = transform; - this.emit("zoom"); - return this; - }, - end: function() { - if (--this.active === 0) { - delete this.that.__zooming; - this.emit("end"); - } - return this; - }, - emit: function(type) { - customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]); - } - }; - - function wheeled() { - if (!filter.apply(this, arguments)) return; - var g = gesture(this, arguments), - t = this.__zoom, - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), - p = mouse(this); - - // If the mouse is in the same location as before, reuse it. - // If there were recent wheel events, reset the wheel idle timeout. - if (g.wheel) { - if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { - g.mouse[1] = t.invert(g.mouse[0] = p); - } - clearTimeout(g.wheel); - } - - // If this wheel event won’t trigger a transform change, ignore it. - else if (t.k === k) return; - - // Otherwise, capture the mouse point and location at the start. - else { - g.mouse = [p, t.invert(p)]; - interrupt(this); - g.start(); - } - - noevent$1(); - g.wheel = setTimeout(wheelidled, wheelDelay); - g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); - - function wheelidled() { - g.wheel = null; - g.end(); - } - } - - function mousedowned() { - if (touchending || !filter.apply(this, arguments)) return; - var g = gesture(this, arguments, true), - v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), - p = mouse(this), - x0 = event.clientX, - y0 = event.clientY; - - dragDisable(event.view); - nopropagation$1(); - g.mouse = [p, this.__zoom.invert(p)]; - interrupt(this); - g.start(); - - function mousemoved() { - noevent$1(); - if (!g.moved) { - var dx = event.clientX - x0, dy = event.clientY - y0; - g.moved = dx * dx + dy * dy > clickDistance2; - } - g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent)); - } - - function mouseupped() { - v.on("mousemove.zoom mouseup.zoom", null); - yesdrag(event.view, g.moved); - noevent$1(); - g.end(); - } - } - - function dblclicked() { - if (!filter.apply(this, arguments)) return; - var t0 = this.__zoom, - p0 = mouse(this), - p1 = t0.invert(p0), - k1 = t0.k * (event.shiftKey ? 0.5 : 2), - t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent); - - noevent$1(); - if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0); - else select(this).call(zoom.transform, t1); - } - - function touchstarted() { - if (!filter.apply(this, arguments)) return; - var touches = event.touches, - n = touches.length, - g = gesture(this, arguments, event.changedTouches.length === n), - started, i, t, p; - - nopropagation$1(); - for (i = 0; i < n; ++i) { - t = touches[i], p = touch(this, touches, t.identifier); - p = [p, this.__zoom.invert(p), t.identifier]; - if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting; - else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0; - } - - if (touchstarting) touchstarting = clearTimeout(touchstarting); - - if (started) { - if (g.taps < 2) touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); - interrupt(this); - g.start(); - } - } - - function touchmoved() { - if (!this.__zooming) return; - var g = gesture(this, arguments), - touches = event.changedTouches, - n = touches.length, i, t, p, l; - - noevent$1(); - if (touchstarting) touchstarting = clearTimeout(touchstarting); - g.taps = 0; - for (i = 0; i < n; ++i) { - t = touches[i], p = touch(this, touches, t.identifier); - if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; - else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; - } - t = g.that.__zoom; - if (g.touch1) { - var p0 = g.touch0[0], l0 = g.touch0[1], - p1 = g.touch1[0], l1 = g.touch1[1], - dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, - dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; - t = scale(t, Math.sqrt(dp / dl)); - p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - } - else if (g.touch0) p = g.touch0[0], l = g.touch0[1]; - else return; - g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent)); - } - - function touchended() { - if (!this.__zooming) return; - var g = gesture(this, arguments), - touches = event.changedTouches, - n = touches.length, i, t; - - nopropagation$1(); - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, touchDelay); - for (i = 0; i < n; ++i) { - t = touches[i]; - if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0; - else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; - } - if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; - if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]); - else { - g.end(); - // If this was a dbltap, reroute to the (optional) dblclick.zoom handler. - if (g.taps === 2) { - var p = select(this).on("dblclick.zoom"); - if (p) p.apply(this, arguments); - } - } - } - - zoom.wheelDelta = function(_) { - return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta; - }; - - zoom.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter; - }; - - zoom.touchable = function(_) { - return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable; - }; - - zoom.extent = function(_) { - return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; - }; - - zoom.scaleExtent = function(_) { - return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; - }; - - zoom.translateExtent = function(_) { - return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; - }; - - zoom.constrain = function(_) { - return arguments.length ? (constrain = _, zoom) : constrain; - }; - - zoom.duration = function(_) { - return arguments.length ? (duration = +_, zoom) : duration; - }; - - zoom.interpolate = function(_) { - return arguments.length ? (interpolate = _, zoom) : interpolate; - }; - - zoom.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? zoom : value; - }; - - zoom.clickDistance = function(_) { - return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2); - }; - - return zoom; - } - - /* - Bypasses features of D3's default projection stream pipeline that are unnecessary: - * Antimeridian clipping - * Spherical rotation - * Resampling - */ - function geoRawMercator() { - var project = mercatorRaw; - var k = 512 / Math.PI; // scale - var x = 0; - var y = 0; // translate - var clipExtent = [[0, 0], [0, 0]]; - - - function projection(point) { - point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180); - return [point[0] * k + x, y - point[1] * k]; - } - - - projection.invert = function(point) { - point = project.invert((point[0] - x) / k, (y - point[1]) / k); - return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI]; - }; - - - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return projection; - }; - - - projection.translate = function(_) { - if (!arguments.length) return [x, y]; - x = +_[0]; - y = +_[1]; - return projection; - }; - - - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - return projection; - }; - - - projection.transform = function(obj) { - if (!arguments.length) return identity$2.translate(x, y).scale(k); - x = +obj.x; - y = +obj.y; - k = +obj.k; - return projection; - }; - - - projection.stream = d3_geoTransform({ - point: function(x, y) { - var vec = projection([x, y]); - this.stream.point(vec[0], vec[1]); - } - }).stream; - - - return projection; - } - - function geoOrthoNormalizedDotProduct(a, b, origin) { - if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) { - return 1; // coincident points, treat as straight and try to remove - } - return geoVecNormalizedDot(a, b, origin); - } - - - function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) { - var val = Math.abs(dotp); - if (val < epsilon) { - return 0; // already orthogonal - } else if (allowStraightAngles && Math.abs(val-1) < epsilon) { - return 0; // straight angle, which is okay in this case - } else if (val < lowerThreshold || val > upperThreshold) { - return dotp; // can be adjusted - } else { - return null; // ignore vertex - } - } - - - function geoOrthoCalcScore(points, isClosed, epsilon, threshold) { - var score = 0; - var first = isClosed ? 0 : 1; - var last = isClosed ? points.length : points.length - 1; - var coords = points.map(function(p) { return p.coord; }); - - var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); - var upperThreshold = Math.cos(threshold * Math.PI / 180); - - for (var i = first; i < last; i++) { - var a = coords[(i - 1 + coords.length) % coords.length]; - var origin = coords[i]; - var b = coords[(i + 1) % coords.length]; - - var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold); - if (dotp === null) continue; // ignore vertex - score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1))); - } - - return score; - } - - // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner - function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) { - var max = -Infinity; - - var first = isClosed ? 0 : 1; - var last = isClosed ? coords.length : coords.length - 1; - - for (var i = first; i < last; i++) { - var a = coords[(i - 1 + coords.length) % coords.length]; - var origin = coords[i]; - var b = coords[(i + 1) % coords.length]; - var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin); - - var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI; - - if (angle > 45) angle = 90 - angle; - - if (angle >= lessThan) continue; - - if (angle > max) max = angle; - } - - if (max === -Infinity) return null; - - return max; - } - - - // similar to geoOrthoCalcScore, but returns quickly if there is something to do - function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) { - var score = null; - var first = isClosed ? 0 : 1; - var last = isClosed ? coords.length : coords.length - 1; - - var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); - var upperThreshold = Math.cos(threshold * Math.PI / 180); - - for (var i = first; i < last; i++) { - var a = coords[(i - 1 + coords.length) % coords.length]; - var origin = coords[i]; - var b = coords[(i + 1) % coords.length]; - - var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles); - if (dotp === null) continue; // ignore vertex - if (Math.abs(dotp) > 0) return 1; // something to do - score = 0; // already square - } - - return score; - } - - // Returns true if a and b have the same elements at the same indices. - function utilArrayIdentical(a, b) { - // an array is always identical to itself - if (a === b) return true; - - var i = a.length; - if (i !== b.length) return false; - while (i--) { - if (a[i] !== b[i]) return false; - } - return true; - } - - // http://2ality.com/2015/01/es6-set-operations.html - - // Difference (a \ b): create a set that contains those elements of set a that are not in set b. - // This operation is also sometimes called minus (-). - // var a = [1,2,3]; - // var b = [4,3,2]; - // utilArrayDifference(a, b) - // [1] - // utilArrayDifference(b, a) - // [4] - function utilArrayDifference(a, b) { - var other = new Set(b); - return Array.from(new Set(a)) - .filter(function(v) { return !other.has(v); }); - } - - // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b. - // var a = [1,2,3]; - // var b = [4,3,2]; - // utilArrayIntersection(a, b) - // [2,3] - function utilArrayIntersection(a, b) { - var other = new Set(b); - return Array.from(new Set(a)) - .filter(function(v) { return other.has(v); }); - } - - // Union (a ∪ b): create a set that contains the elements of both set a and set b. - // var a = [1,2,3]; - // var b = [4,3,2]; - // utilArrayUnion(a, b) - // [1,2,3,4] - function utilArrayUnion(a, b) { - var result = new Set(a); - b.forEach(function(v) { result.add(v); }); - return Array.from(result); - } - - // Returns an Array with all the duplicates removed - // var a = [1,1,2,3,3]; - // utilArrayUniq(a) - // [1,2,3] - function utilArrayUniq(a) { - return Array.from(new Set(a)); - } - - - // Splits array into chunks of given chunk size - // var a = [1,2,3,4,5,6,7]; - // utilArrayChunk(a, 3); - // [[1,2,3],[4,5,6],[7]]; - function utilArrayChunk(a, chunkSize) { - if (!chunkSize || chunkSize < 0) return [a.slice()]; - - var result = new Array(Math.ceil(a.length / chunkSize)); - return Array.from(result, function(item, i) { - return a.slice(i * chunkSize, i * chunkSize + chunkSize); - }); - } - - - // Flattens two level array into a single level - // var a = [[1,2,3],[4,5,6],[7]]; - // utilArrayFlatten(a); - // [1,2,3,4,5,6,7]; - function utilArrayFlatten(a) { - return a.reduce(function(acc, val) { - return acc.concat(val); - }, []); - } - - - // Groups the items of the Array according to the given key - // `key` can be passed as a property or as a key function - // - // var pets = [ - // { type: 'Dog', name: 'Spot' }, - // { type: 'Cat', name: 'Tiger' }, - // { type: 'Dog', name: 'Rover' }, - // { type: 'Cat', name: 'Leo' } - // ]; - // - // utilArrayGroupBy(pets, 'type') - // { - // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}], - // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}] - // } - // - // utilArrayGroupBy(pets, function(item) { return item.name.length; }) - // { - // 3: [{type: 'Cat', name: 'Leo'}], - // 4: [{type: 'Dog', name: 'Spot'}], - // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}] - // } - function utilArrayGroupBy(a, key) { - return a.reduce(function(acc, item) { - var group = (typeof key === 'function') ? key(item) : item[key]; - (acc[group] = acc[group] || []).push(item); - return acc; - }, {}); - } - - - // Returns an Array with all the duplicates removed - // where uniqueness determined by the given key - // `key` can be passed as a property or as a key function - // - // var pets = [ - // { type: 'Dog', name: 'Spot' }, - // { type: 'Cat', name: 'Tiger' }, - // { type: 'Dog', name: 'Rover' }, - // { type: 'Cat', name: 'Leo' } - // ]; - // - // utilArrayUniqBy(pets, 'type') - // [ - // { type: 'Dog', name: 'Spot' }, - // { type: 'Cat', name: 'Tiger' } - // ] - // - // utilArrayUniqBy(pets, function(item) { return item.name.length; }) - // [ - // { type: 'Dog', name: 'Spot' }, - // { type: 'Cat', name: 'Tiger' }, - // { type: 'Cat', name: 'Leo' } - // } - function utilArrayUniqBy(a, key) { - var seen = new Set(); - return a.reduce(function(acc, item) { - var val = (typeof key === 'function') ? key(item) : item[key]; - if (val && !seen.has(val)) { - seen.add(val); - acc.push(item); - } - return acc; - }, []); - } - - var remove$1 = removeDiacritics; - - var replacementList = [ - { - base: ' ', - chars: "\u00A0", - }, { - base: '0', - chars: "\u07C0", - }, { - base: 'A', - chars: "\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F", - }, { - base: 'AA', - chars: "\uA732", - }, { - base: 'AE', - chars: "\u00C6\u01FC\u01E2", - }, { - base: 'AO', - chars: "\uA734", - }, { - base: 'AU', - chars: "\uA736", - }, { - base: 'AV', - chars: "\uA738\uA73A", - }, { - base: 'AY', - chars: "\uA73C", - }, { - base: 'B', - chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181", - }, { - base: 'C', - chars: "\u24b8\uff23\uA73E\u1E08\u0106\u0043\u0108\u010A\u010C\u00C7\u0187\u023B", - }, { - base: 'D', - chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779", - }, { - base: 'Dh', - chars: "\u00D0", - }, { - base: 'DZ', - chars: "\u01F1\u01C4", - }, { - base: 'Dz', - chars: "\u01F2\u01C5", - }, { - base: 'E', - chars: "\u025B\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07", - }, { - base: 'F', - chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B", - }, { - base: 'G', - chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262", - }, { - base: 'H', - chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D", - }, { - base: 'I', - chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197", - }, { - base: 'J', - chars: "\u24BF\uFF2A\u0134\u0248\u0237", - }, { - base: 'K', - chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2", - }, { - base: 'L', - chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780", - }, { - base: 'LJ', - chars: "\u01C7", - }, { - base: 'Lj', - chars: "\u01C8", - }, { - base: 'M', - chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB", - }, { - base: 'N', - chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E", - }, { - base: 'NJ', - chars: "\u01CA", - }, { - base: 'Nj', - chars: "\u01CB", - }, { - base: 'O', - chars: "\u24C4\uFF2F\xD2\xD3\xD4\u1ED2\u1ED0\u1ED6\u1ED4\xD5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\xD6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\xD8\u01FE\u0186\u019F\uA74A\uA74C", - }, { - base: 'OE', - chars: "\u0152", - }, { - base: 'OI', - chars: "\u01A2", - }, { - base: 'OO', - chars: "\uA74E", - }, { - base: 'OU', - chars: "\u0222", - }, { - base: 'P', - chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754", - }, { - base: 'Q', - chars: "\u24C6\uFF31\uA756\uA758\u024A", - }, { - base: 'R', - chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782", - }, { - base: 'S', - chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784", - }, { - base: 'T', - chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786", - }, { - base: 'Th', - chars: "\u00DE", - }, { - base: 'TZ', - chars: "\uA728", - }, { - base: 'U', - chars: "\u24CA\uFF35\xD9\xDA\xDB\u0168\u1E78\u016A\u1E7A\u016C\xDC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244", - }, { - base: 'V', - chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245", - }, { - base: 'VY', - chars: "\uA760", - }, { - base: 'W', - chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72", - }, { - base: 'X', - chars: "\u24CD\uFF38\u1E8A\u1E8C", - }, { - base: 'Y', - chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE", - }, { - base: 'Z', - chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762", - }, { - base: 'a', - chars: "\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251", - }, { - base: 'aa', - chars: "\uA733", - }, { - base: 'ae', - chars: "\u00E6\u01FD\u01E3", - }, { - base: 'ao', - chars: "\uA735", - }, { - base: 'au', - chars: "\uA737", - }, { - base: 'av', - chars: "\uA739\uA73B", - }, { - base: 'ay', - chars: "\uA73D", - }, { - base: 'b', - chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182", - }, { - base: 'c', - chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184", - }, { - base: 'd', - chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA", - }, { - base: 'dh', - chars: "\u00F0", - }, { - base: 'dz', - chars: "\u01F3\u01C6", - }, { - base: 'e', - chars: "\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD", - }, { - base: 'f', - chars: "\u24D5\uFF46\u1E1F\u0192", - }, { - base: 'ff', - chars: "\uFB00", - }, { - base: 'fi', - chars: "\uFB01", - }, { - base: 'fl', - chars: "\uFB02", - }, { - base: 'ffi', - chars: "\uFB03", - }, { - base: 'ffl', - chars: "\uFB04", - }, { - base: 'g', - chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79", - }, { - base: 'h', - chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265", - }, { - base: 'hv', - chars: "\u0195", - }, { - base: 'i', - chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131", - }, { - base: 'j', - chars: "\u24D9\uFF4A\u0135\u01F0\u0249", - }, { - base: 'k', - chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3", - }, { - base: 'l', - chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D", - }, { - base: 'lj', - chars: "\u01C9", - }, { - base: 'm', - chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F", - }, { - base: 'n', - chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509", - }, { - base: 'nj', - chars: "\u01CC", - }, { - base: 'o', - chars: "\u24DE\uFF4F\xF2\xF3\xF4\u1ED3\u1ED1\u1ED7\u1ED5\xF5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\xF6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\xF8\u01FF\uA74B\uA74D\u0275\u0254\u1D11", - }, { - base: 'oe', - chars: "\u0153", - }, { - base: 'oi', - chars: "\u01A3", - }, { - base: 'oo', - chars: "\uA74F", - }, { - base: 'ou', - chars: "\u0223", - }, { - base: 'p', - chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1", - }, { - base: 'q', - chars: "\u24E0\uFF51\u024B\uA757\uA759", - }, { - base: 'r', - chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783", - }, { - base: 's', - chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282", - }, { - base: 'ss', - chars: "\xDF", - }, { - base: 't', - chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787", - }, { - base: 'th', - chars: "\u00FE", - }, { - base: 'tz', - chars: "\uA729", - }, { - base: 'u', - chars: "\u24E4\uFF55\xF9\xFA\xFB\u0169\u1E79\u016B\u1E7B\u016D\xFC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289", - }, { - base: 'v', - chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C", - }, { - base: 'vy', - chars: "\uA761", - }, { - base: 'w', - chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73", - }, { - base: 'x', - chars: "\u24E7\uFF58\u1E8B\u1E8D", - }, { - base: 'y', - chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF", - }, { - base: 'z', - chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763", - } - ]; - - var diacriticsMap = {}; - for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) { - var chars = replacementList[i$1].chars; - for (var j = 0; j < chars.length; j += 1) { - diacriticsMap[chars[j]] = replacementList[i$1].base; - } - } - - function removeDiacritics(str) { - return str.replace(/[^\u0000-\u007e]/g, function(c) { - return diacriticsMap[c] || c; - }); - } - - var replacementList_1 = replacementList; - var diacriticsMap_1 = diacriticsMap; - - var diacritics = { - remove: remove$1, - replacementList: replacementList_1, - diacriticsMap: diacriticsMap_1 - }; - - var isArabic_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - const arabicBlocks = [ - [0x0600, 0x06FF], - [0x0750, 0x077F], - [0x08A0, 0x08FF], - [0xFB50, 0xFDFF], - [0xFE70, 0xFEFF], - [0x10E60, 0x10E7F], - [0x1EC70, 0x1ECBF], - [0x1EE00, 0x1EEFF] // Mathematical Alphabetic symbols https://www.unicode.org/charts/PDF/U1EE00.pdf - ]; - function isArabic(char) { - if (char.length > 1) { - // allow the newer chars? - throw new Error('isArabic works on only one-character strings'); - } - let code = char.charCodeAt(0); - for (let i = 0; i < arabicBlocks.length; i++) { - let block = arabicBlocks[i]; - if (code >= block[0] && code <= block[1]) { - return true; - } - } - return false; - } - exports.isArabic = isArabic; - function isMath(char) { - if (char.length > 2) { - // allow the newer chars? - throw new Error('isMath works on only one-character strings'); - } - let code = char.charCodeAt(0); - return ((code >= 0x660 && code <= 0x66C) || (code >= 0x6F0 && code <= 0x6F9)); - } - exports.isMath = isMath; - }); - - var unicodeArabic = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - const arabicReference = { - "alef": { - "normal": [ - "\u0627" - ], - "madda_above": { - "normal": [ - "\u0627\u0653", - "\u0622" - ], - "isolated": "\uFE81", - "final": "\uFE82" - }, - "hamza_above": { - "normal": [ - "\u0627\u0654", - "\u0623" - ], - "isolated": "\uFE83", - "final": "\uFE84" - }, - "hamza_below": { - "normal": [ - "\u0627\u0655", - "\u0625" - ], - "isolated": "\uFE87", - "final": "\uFE88" - }, - "wasla": { - "normal": "\u0671", - "isolated": "\uFB50", - "final": "\uFB51" - }, - "wavy_hamza_above": [ - "\u0672" - ], - "wavy_hamza_below": [ - "\u0627\u065F", - "\u0673" - ], - "high_hamza": [ - "\u0675", - "\u0627\u0674" - ], - "indic_two_above": [ - "\u0773" - ], - "indic_three_above": [ - "\u0774" - ], - "fathatan": { - "normal": [ - "\u0627\u064B" - ], - "final": "\uFD3C", - "isolated": "\uFD3D" - }, - "isolated": "\uFE8D", - "final": "\uFE8E" - }, - "beh": { - "normal": [ - "\u0628" - ], - "dotless": [ - "\u066E" - ], - "three_dots_horizontally_below": [ - "\u0750" - ], - "dot_below_three_dots_above": [ - "\u0751" - ], - "three_dots_pointing_upwards_below": [ - "\u0752" - ], - "three_dots_pointing_upwards_below_two_dots_above": [ - "\u0753" - ], - "two_dots_below_dot_above": [ - "\u0754" - ], - "inverted_small_v_below": [ - "\u0755" - ], - "small_v": [ - "\u0756" - ], - "small_v_below": [ - "\u08A0" - ], - "hamza_above": [ - "\u08A1" - ], - "small_meem_above": [ - "\u08B6" - ], - "isolated": "\uFE8F", - "final": "\uFE90", - "initial": "\uFE91", - "medial": "\uFE92" - }, - "teh marbuta": { - "normal": [ - "\u0629" - ], - "isolated": "\uFE93", - "final": "\uFE94" - }, - "teh": { - "normal": [ - "\u062A" - ], - "ring": [ - "\u067C" - ], - "three_dots_above_downwards": [ - "\u067D" - ], - "small_teh_above": [ - "\u08B8" - ], - "isolated": "\uFE95", - "final": "\uFE96", - "initial": "\uFE97", - "medial": "\uFE98" - }, - "theh": { - "normal": [ - "\u062B" - ], - "isolated": "\uFE99", - "final": "\uFE9A", - "initial": "\uFE9B", - "medial": "\uFE9C" - }, - "jeem": { - "normal": [ - "\u062C" - ], - "two_dots_above": [ - "\u08A2" - ], - "isolated": "\uFE9D", - "final": "\uFE9E", - "initial": "\uFE9F", - "medial": "\uFEA0" - }, - "hah": { - "normal": [ - "\u062D" - ], - "hamza_above": [ - "\u0681" - ], - "two_dots_vertical_above": [ - "\u0682" - ], - "three_dots_above": [ - "\u0685" - ], - "two_dots_above": [ - "\u0757" - ], - "three_dots_pointing_upwards_below": [ - "\u0758" - ], - "small_tah_below": [ - "\u076E" - ], - "small_tah_two_dots": [ - "\u076F" - ], - "small_tah_above": [ - "\u0772" - ], - "indic_four_below": [ - "\u077C" - ], - "isolated": "\uFEA1", - "final": "\uFEA2", - "initial": "\uFEA3", - "medial": "\uFEA4" - }, - "khah": { - "normal": [ - "\u062E" - ], - "isolated": "\uFEA5", - "final": "\uFEA6", - "initial": "\uFEA7", - "medial": "\uFEA8" - }, - "dal": { - "normal": [ - "\u062F" - ], - "ring": [ - "\u0689" - ], - "dot_below": [ - "\u068A" - ], - "dot_below_small_tah": [ - "\u068B" - ], - "three_dots_above_downwards": [ - "\u068F" - ], - "four_dots_above": [ - "\u0690" - ], - "inverted_v": [ - "\u06EE" - ], - "two_dots_vertically_below_small_tah": [ - "\u0759" - ], - "inverted_small_v_below": [ - "\u075A" - ], - "three_dots_below": [ - "\u08AE" - ], - "isolated": "\uFEA9", - "final": "\uFEAA" - }, - "thal": { - "normal": [ - "\u0630" - ], - "isolated": "\uFEAB", - "final": "\uFEAC" - }, - "reh": { - "normal": [ - "\u0631" - ], - "small_v": [ - "\u0692" - ], - "ring": [ - "\u0693" - ], - "dot_below": [ - "\u0694" - ], - "small_v_below": [ - "\u0695" - ], - "dot_below_dot_above": [ - "\u0696" - ], - "two_dots_above": [ - "\u0697" - ], - "four_dots_above": [ - "\u0699" - ], - "inverted_v": [ - "\u06EF" - ], - "stroke": [ - "\u075B" - ], - "two_dots_vertically_above": [ - "\u076B" - ], - "hamza_above": [ - "\u076C" - ], - "small_tah_two_dots": [ - "\u0771" - ], - "loop": [ - "\u08AA" - ], - "small_noon_above": [ - "\u08B9" - ], - "isolated": "\uFEAD", - "final": "\uFEAE" - }, - "zain": { - "normal": [ - "\u0632" - ], - "inverted_v_above": [ - "\u08B2" - ], - "isolated": "\uFEAF", - "final": "\uFEB0" - }, - "seen": { - "normal": [ - "\u0633" - ], - "dot_below_dot_above": [ - "\u069A" - ], - "three_dots_below": [ - "\u069B" - ], - "three_dots_below_three_dots_above": [ - "\u069C" - ], - "four_dots_above": [ - "\u075C" - ], - "two_dots_vertically_above": [ - "\u076D" - ], - "small_tah_two_dots": [ - "\u0770" - ], - "indic_four_above": [ - "\u077D" - ], - "inverted_v": [ - "\u077E" - ], - "isolated": "\uFEB1", - "final": "\uFEB2", - "initial": "\uFEB3", - "medial": "\uFEB4" - }, - "sheen": { - "normal": [ - "\u0634" - ], - "dot_below": [ - "\u06FA" - ], - "isolated": "\uFEB5", - "final": "\uFEB6", - "initial": "\uFEB7", - "medial": "\uFEB8" - }, - "sad": { - "normal": [ - "\u0635" - ], - "two_dots_below": [ - "\u069D" - ], - "three_dots_above": [ - "\u069E" - ], - "three_dots_below": [ - "\u08AF" - ], - "isolated": "\uFEB9", - "final": "\uFEBA", - "initial": "\uFEBB", - "medial": "\uFEBC" - }, - "dad": { - "normal": [ - "\u0636" - ], - "dot_below": [ - "\u06FB" - ], - "isolated": "\uFEBD", - "final": "\uFEBE", - "initial": "\uFEBF", - "medial": "\uFEC0" - }, - "tah": { - "normal": [ - "\u0637" - ], - "three_dots_above": [ - "\u069F" - ], - "two_dots_above": [ - "\u08A3" - ], - "isolated": "\uFEC1", - "final": "\uFEC2", - "initial": "\uFEC3", - "medial": "\uFEC4" - }, - "zah": { - "normal": [ - "\u0638" - ], - "isolated": "\uFEC5", - "final": "\uFEC6", - "initial": "\uFEC7", - "medial": "\uFEC8" - }, - "ain": { - "normal": [ - "\u0639" - ], - "three_dots_above": [ - "\u06A0" - ], - "two_dots_above": [ - "\u075D" - ], - "three_dots_pointing_downwards_above": [ - "\u075E" - ], - "two_dots_vertically_above": [ - "\u075F" - ], - "three_dots_below": [ - "\u08B3" - ], - "isolated": "\uFEC9", - "final": "\uFECA", - "initial": "\uFECB", - "medial": "\uFECC" - }, - "ghain": { - "normal": [ - "\u063A" - ], - "dot_below": [ - "\u06FC" - ], - "isolated": "\uFECD", - "final": "\uFECE", - "initial": "\uFECF", - "medial": "\uFED0" - }, - "feh": { - "normal": [ - "\u0641" - ], - "dotless": [ - "\u06A1" - ], - "dot_moved_below": [ - "\u06A2" - ], - "dot_below": [ - "\u06A3" - ], - "three_dots_below": [ - "\u06A5" - ], - "two_dots_below": [ - "\u0760" - ], - "three_dots_pointing_upwards_below": [ - "\u0761" - ], - "dot_below_three_dots_above": [ - "\u08A4" - ], - "isolated": "\uFED1", - "final": "\uFED2", - "initial": "\uFED3", - "medial": "\uFED4" - }, - "qaf": { - "normal": [ - "\u0642" - ], - "dotless": [ - "\u066F" - ], - "dot_above": [ - "\u06A7" - ], - "three_dots_above": [ - "\u06A8" - ], - "dot_below": [ - "\u08A5" - ], - "isolated": "\uFED5", - "final": "\uFED6", - "initial": "\uFED7", - "medial": "\uFED8" - }, - "kaf": { - "normal": [ - "\u0643" - ], - "swash": [ - "\u06AA" - ], - "ring": [ - "\u06AB" - ], - "dot_above": [ - "\u06AC" - ], - "three_dots_below": [ - "\u06AE" - ], - "two_dots_above": [ - "\u077F" - ], - "dot_below": [ - "\u08B4" - ], - "isolated": "\uFED9", - "final": "\uFEDA", - "initial": "\uFEDB", - "medial": "\uFEDC" - }, - "lam": { - "normal": [ - "\u0644" - ], - "small_v": [ - "\u06B5" - ], - "dot_above": [ - "\u06B6" - ], - "three_dots_above": [ - "\u06B7" - ], - "three_dots_below": [ - "\u06B8" - ], - "bar": [ - "\u076A" - ], - "double_bar": [ - "\u08A6" - ], - "isolated": "\uFEDD", - "final": "\uFEDE", - "initial": "\uFEDF", - "medial": "\uFEE0" - }, - "meem": { - "normal": [ - "\u0645" - ], - "dot_above": [ - "\u0765" - ], - "dot_below": [ - "\u0766" - ], - "three_dots_above": [ - "\u08A7" - ], - "isolated": "\uFEE1", - "final": "\uFEE2", - "initial": "\uFEE3", - "medial": "\uFEE4" - }, - "noon": { - "normal": [ - "\u0646" - ], - "dot_below": [ - "\u06B9" - ], - "ring": [ - "\u06BC" - ], - "three_dots_above": [ - "\u06BD" - ], - "two_dots_below": [ - "\u0767" - ], - "small_tah": [ - "\u0768" - ], - "small_v": [ - "\u0769" - ], - "isolated": "\uFEE5", - "final": "\uFEE6", - "initial": "\uFEE7", - "medial": "\uFEE8" - }, - "heh": { - "normal": [ - "\u0647" - ], - "isolated": "\uFEE9", - "final": "\uFEEA", - "initial": "\uFEEB", - "medial": "\uFEEC" - }, - "waw": { - "normal": [ - "\u0648" - ], - "hamza_above": { - "normal": [ - "\u0624", - "\u0648\u0654" - ], - "isolated": "\uFE85", - "final": "\uFE86" - }, - "high_hamza": [ - "\u0676", - "\u0648\u0674" - ], - "ring": [ - "\u06C4" - ], - "two_dots_above": [ - "\u06CA" - ], - "dot_above": [ - "\u06CF" - ], - "indic_two_above": [ - "\u0778" - ], - "indic_three_above": [ - "\u0779" - ], - "dot_within": [ - "\u08AB" - ], - "isolated": "\uFEED", - "final": "\uFEEE" - }, - "alef_maksura": { - "normal": [ - "\u0649" - ], - "hamza_above": [ - "\u0626", - "\u064A\u0654" - ], - "initial": "\uFBE8", - "medial": "\uFBE9", - "isolated": "\uFEEF", - "final": "\uFEF0" - }, - "yeh": { - "normal": [ - "\u064A" - ], - "hamza_above": { - "normal": [ - "\u0626", - "\u0649\u0654" - ], - "isolated": "\uFE89", - "final": "\uFE8A", - "initial": "\uFE8B", - "medial": "\uFE8C" - }, - "two_dots_below_hamza_above": [ - "\u08A8" - ], - "high_hamza": [ - "\u0678", - "\u064A\u0674" - ], - "tail": [ - "\u06CD" - ], - "small_v": [ - "\u06CE" - ], - "three_dots_below": [ - "\u06D1" - ], - "two_dots_below_dot_above": [ - "\u08A9" - ], - "two_dots_below_small_noon_above": [ - "\u08BA" - ], - "isolated": "\uFEF1", - "final": "\uFEF2", - "initial": "\uFEF3", - "medial": "\uFEF4" - }, - "tteh": { - "normal": [ - "\u0679" - ], - "isolated": "\uFB66", - "final": "\uFB67", - "initial": "\uFB68", - "medial": "\uFB69" - }, - "tteheh": { - "normal": [ - "\u067A" - ], - "isolated": "\uFB5E", - "final": "\uFB5F", - "initial": "\uFB60", - "medial": "\uFB61" - }, - "beeh": { - "normal": [ - "\u067B" - ], - "isolated": "\uFB52", - "final": "\uFB53", - "initial": "\uFB54", - "medial": "\uFB55" - }, - "peh": { - "normal": [ - "\u067E" - ], - "small_meem_above": [ - "\u08B7" - ], - "isolated": "\uFB56", - "final": "\uFB57", - "initial": "\uFB58", - "medial": "\uFB59" - }, - "teheh": { - "normal": [ - "\u067F" - ], - "isolated": "\uFB62", - "final": "\uFB63", - "initial": "\uFB64", - "medial": "\uFB65" - }, - "beheh": { - "normal": [ - "\u0680" - ], - "isolated": "\uFB5A", - "final": "\uFB5B", - "initial": "\uFB5C", - "medial": "\uFB5D" - }, - "nyeh": { - "normal": [ - "\u0683" - ], - "isolated": "\uFB76", - "final": "\uFB77", - "initial": "\uFB78", - "medial": "\uFB79" - }, - "dyeh": { - "normal": [ - "\u0684" - ], - "isolated": "\uFB72", - "final": "\uFB73", - "initial": "\uFB74", - "medial": "\uFB75" - }, - "tcheh": { - "normal": [ - "\u0686" - ], - "dot_above": [ - "\u06BF" - ], - "isolated": "\uFB7A", - "final": "\uFB7B", - "initial": "\uFB7C", - "medial": "\uFB7D" - }, - "tcheheh": { - "normal": [ - "\u0687" - ], - "isolated": "\uFB7E", - "final": "\uFB7F", - "initial": "\uFB80", - "medial": "\uFB81" - }, - "ddal": { - "normal": [ - "\u0688" - ], - "isolated": "\uFB88", - "final": "\uFB89" - }, - "dahal": { - "normal": [ - "\u068C" - ], - "isolated": "\uFB84", - "final": "\uFB85" - }, - "ddahal": { - "normal": [ - "\u068D" - ], - "isolated": "\uFB82", - "final": "\uFB83" - }, - "dul": { - "normal": [ - "\u068F", - "\u068E" - ], - "isolated": "\uFB86", - "final": "\uFB87" - }, - "rreh": { - "normal": [ - "\u0691" - ], - "isolated": "\uFB8C", - "final": "\uFB8D" - }, - "jeh": { - "normal": [ - "\u0698" - ], - "isolated": "\uFB8A", - "final": "\uFB8B" - }, - "veh": { - "normal": [ - "\u06A4" - ], - "isolated": "\uFB6A", - "final": "\uFB6B", - "initial": "\uFB6C", - "medial": "\uFB6D" - }, - "peheh": { - "normal": [ - "\u06A6" - ], - "isolated": "\uFB6E", - "final": "\uFB6F", - "initial": "\uFB70", - "medial": "\uFB71" - }, - "keheh": { - "normal": [ - "\u06A9" - ], - "dot_above": [ - "\u0762" - ], - "three_dots_above": [ - "\u0763" - ], - "three_dots_pointing_upwards_below": [ - "\u0764" - ], - "isolated": "\uFB8E", - "final": "\uFB8F", - "initial": "\uFB90", - "medial": "\uFB91" - }, - "ng": { - "normal": [ - "\u06AD" - ], - "isolated": "\uFBD3", - "final": "\uFBD4", - "initial": "\uFBD5", - "medial": "\uFBD6" - }, - "gaf": { - "normal": [ - "\u06AF" - ], - "ring": [ - "\u06B0" - ], - "two_dots_below": [ - "\u06B2" - ], - "three_dots_above": [ - "\u06B4" - ], - "inverted_stroke": [ - "\u08B0" - ], - "isolated": "\uFB92", - "final": "\uFB93", - "initial": "\uFB94", - "medial": "\uFB95" - }, - "ngoeh": { - "normal": [ - "\u06B1" - ], - "isolated": "\uFB9A", - "final": "\uFB9B", - "initial": "\uFB9C", - "medial": "\uFB9D" - }, - "gueh": { - "normal": [ - "\u06B3" - ], - "isolated": "\uFB96", - "final": "\uFB97", - "initial": "\uFB98", - "medial": "\uFB99" - }, - "noon ghunna": { - "normal": [ - "\u06BA" - ], - "isolated": "\uFB9E", - "final": "\uFB9F" - }, - "rnoon": { - "normal": [ - "\u06BB" - ], - "isolated": "\uFBA0", - "final": "\uFBA1", - "initial": "\uFBA2", - "medial": "\uFBA3" - }, - "heh doachashmee": { - "normal": [ - "\u06BE" - ], - "isolated": "\uFBAA", - "final": "\uFBAB", - "initial": "\uFBAC", - "medial": "\uFBAD" - }, - "heh goal": { - "normal": [ - "\u06C1" - ], - "hamza_above": [ - "\u06C1\u0654", - "\u06C2" - ], - "isolated": "\uFBA6", - "final": "\uFBA7", - "initial": "\uFBA8", - "medial": "\uFBA9" - }, - "teh marbuta goal": { - "normal": [ - "\u06C3" - ] - }, - "kirghiz oe": { - "normal": [ - "\u06C5" - ], - "isolated": "\uFBE0", - "final": "\uFBE1" - }, - "oe": { - "normal": [ - "\u06C6" - ], - "isolated": "\uFBD9", - "final": "\uFBDA" - }, - "u": { - "normal": [ - "\u06C7" - ], - "hamza_above": { - "normal": [ - "\u0677", - "\u06C7\u0674" - ], - "isolated": "\uFBDD" - }, - "isolated": "\uFBD7", - "final": "\uFBD8" - }, - "yu": { - "normal": [ - "\u06C8" - ], - "isolated": "\uFBDB", - "final": "\uFBDC" - }, - "kirghiz yu": { - "normal": [ - "\u06C9" - ], - "isolated": "\uFBE2", - "final": "\uFBE3" - }, - "ve": { - "normal": [ - "\u06CB" - ], - "isolated": "\uFBDE", - "final": "\uFBDF" - }, - "farsi yeh": { - "normal": [ - "\u06CC" - ], - "indic_two_above": [ - "\u0775" - ], - "indic_three_above": [ - "\u0776" - ], - "indic_four_above": [ - "\u0777" - ], - "isolated": "\uFBFC", - "final": "\uFBFD", - "initial": "\uFBFE", - "medial": "\uFBFF" - }, - "e": { - "normal": [ - "\u06D0" - ], - "isolated": "\uFBE4", - "final": "\uFBE5", - "initial": "\uFBE6", - "medial": "\uFBE7" - }, - "yeh barree": { - "normal": [ - "\u06D2" - ], - "hamza_above": { - "normal": [ - "\u06D2\u0654", - "\u06D3" - ], - "isolated": "\uFBB0", - "final": "\uFBB1" - }, - "indic_two_above": [ - "\u077A" - ], - "indic_three_above": [ - "\u077B" - ], - "isolated": "\uFBAE", - "final": "\uFBAF" - }, - "ae": { - "normal": [ - "\u06D5" - ], - "isolated": "\u06D5", - "final": "\uFEEA", - "yeh_above": { - "normal": [ - "\u06C0", - "\u06D5\u0654" - ], - "isolated": "\uFBA4", - "final": "\uFBA5" - } - }, - "rohingya yeh": { - "normal": [ - "\u08AC" - ] - }, - "low alef": { - "normal": [ - "\u08AD" - ] - }, - "straight waw": { - "normal": [ - "\u08B1" - ] - }, - "african feh": { - "normal": [ - "\u08BB" - ] - }, - "african qaf": { - "normal": [ - "\u08BC" - ] - }, - "african noon": { - "normal": [ - "\u08BD" - ] - } - }; - exports.default = arabicReference; - }); - - var unicodeLigatures = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - const ligatureReference = { - "\u0626\u0627": { - "isolated": "\uFBEA", - "final": "\uFBEB" - }, - "\u0626\u06D5": { - "isolated": "\uFBEC", - "final": "\uFBED" - }, - "\u0626\u0648": { - "isolated": "\uFBEE", - "final": "\uFBEF" - }, - "\u0626\u06C7": { - "isolated": "\uFBF0", - "final": "\uFBF1" - }, - "\u0626\u06C6": { - "isolated": "\uFBF2", - "final": "\uFBF3" - }, - "\u0626\u06C8": { - "isolated": "\uFBF4", - "final": "\uFBF5" - }, - "\u0626\u06D0": { - "isolated": "\uFBF6", - "final": "\uFBF7", - "initial": "\uFBF8" - }, - "\u0626\u0649": { - "uighur_kirghiz": { - "isolated": "\uFBF9", - "final": "\uFBFA", - "initial": "\uFBFB" - }, - "isolated": "\uFC03", - "final": "\uFC68" - }, - "\u0626\u062C": { - "isolated": "\uFC00", - "initial": "\uFC97" - }, - "\u0626\u062D": { - "isolated": "\uFC01", - "initial": "\uFC98" - }, - "\u0626\u0645": { - "isolated": "\uFC02", - "final": "\uFC66", - "initial": "\uFC9A", - "medial": "\uFCDF" - }, - "\u0626\u064A": { - "isolated": "\uFC04", - "final": "\uFC69" - }, - "\u0628\u062C": { - "isolated": "\uFC05", - "initial": "\uFC9C" - }, - "\u0628\u062D": { - "isolated": "\uFC06", - "initial": "\uFC9D" - }, - "\u0628\u062E": { - "isolated": "\uFC07", - "initial": "\uFC9E" - }, - "\u0628\u0645": { - "isolated": "\uFC08", - "final": "\uFC6C", - "initial": "\uFC9F", - "medial": "\uFCE1" - }, - "\u0628\u0649": { - "isolated": "\uFC09", - "final": "\uFC6E" - }, - "\u0628\u064A": { - "isolated": "\uFC0A", - "final": "\uFC6F" - }, - "\u062A\u062C": { - "isolated": "\uFC0B", - "initial": "\uFCA1" - }, - "\u062A\u062D": { - "isolated": "\uFC0C", - "initial": "\uFCA2" - }, - "\u062A\u062E": { - "isolated": "\uFC0D", - "initial": "\uFCA3" - }, - "\u062A\u0645": { - "isolated": "\uFC0E", - "final": "\uFC72", - "initial": "\uFCA4", - "medial": "\uFCE3" - }, - "\u062A\u0649": { - "isolated": "\uFC0F", - "final": "\uFC74" - }, - "\u062A\u064A": { - "isolated": "\uFC10", - "final": "\uFC75" - }, - "\u062B\u062C": { - "isolated": "\uFC11" - }, - "\u062B\u0645": { - "isolated": "\uFC12", - "final": "\uFC78", - "initial": "\uFCA6", - "medial": "\uFCE5" - }, - "\u062B\u0649": { - "isolated": "\uFC13", - "final": "\uFC7A" - }, - "\u062B\u0648": { - "isolated": "\uFC14" - }, - "\u062C\u062D": { - "isolated": "\uFC15", - "initial": "\uFCA7" - }, - "\u062C\u0645": { - "isolated": "\uFC16", - "initial": "\uFCA8" - }, - "\u062D\u062C": { - "isolated": "\uFC17", - "initial": "\uFCA9" - }, - "\u062D\u0645": { - "isolated": "\uFC18", - "initial": "\uFCAA" - }, - "\u062E\u062C": { - "isolated": "\uFC19", - "initial": "\uFCAB" - }, - "\u062E\u062D": { - "isolated": "\uFC1A" - }, - "\u062E\u0645": { - "isolated": "\uFC1B", - "initial": "\uFCAC" - }, - "\u0633\u062C": { - "isolated": "\uFC1C", - "initial": "\uFCAD", - "medial": "\uFD34" - }, - "\u0633\u062D": { - "isolated": "\uFC1D", - "initial": "\uFCAE", - "medial": "\uFD35" - }, - "\u0633\u062E": { - "isolated": "\uFC1E", - "initial": "\uFCAF", - "medial": "\uFD36" - }, - "\u0633\u0645": { - "isolated": "\uFC1F", - "initial": "\uFCB0", - "medial": "\uFCE7" - }, - "\u0635\u062D": { - "isolated": "\uFC20", - "initial": "\uFCB1" - }, - "\u0635\u0645": { - "isolated": "\uFC21", - "initial": "\uFCB3" - }, - "\u0636\u062C": { - "isolated": "\uFC22", - "initial": "\uFCB4" - }, - "\u0636\u062D": { - "isolated": "\uFC23", - "initial": "\uFCB5" - }, - "\u0636\u062E": { - "isolated": "\uFC24", - "initial": "\uFCB6" - }, - "\u0636\u0645": { - "isolated": "\uFC25", - "initial": "\uFCB7" - }, - "\u0637\u062D": { - "isolated": "\uFC26", - "initial": "\uFCB8" - }, - "\u0637\u0645": { - "isolated": "\uFC27", - "initial": "\uFD33", - "medial": "\uFD3A" - }, - "\u0638\u0645": { - "isolated": "\uFC28", - "initial": "\uFCB9", - "medial": "\uFD3B" - }, - "\u0639\u062C": { - "isolated": "\uFC29", - "initial": "\uFCBA" - }, - "\u0639\u0645": { - "isolated": "\uFC2A", - "initial": "\uFCBB" - }, - "\u063A\u062C": { - "isolated": "\uFC2B", - "initial": "\uFCBC" - }, - "\u063A\u0645": { - "isolated": "\uFC2C", - "initial": "\uFCBD" - }, - "\u0641\u062C": { - "isolated": "\uFC2D", - "initial": "\uFCBE" - }, - "\u0641\u062D": { - "isolated": "\uFC2E", - "initial": "\uFCBF" - }, - "\u0641\u062E": { - "isolated": "\uFC2F", - "initial": "\uFCC0" - }, - "\u0641\u0645": { - "isolated": "\uFC30", - "initial": "\uFCC1" - }, - "\u0641\u0649": { - "isolated": "\uFC31", - "final": "\uFC7C" - }, - "\u0641\u064A": { - "isolated": "\uFC32", - "final": "\uFC7D" - }, - "\u0642\u062D": { - "isolated": "\uFC33", - "initial": "\uFCC2" - }, - "\u0642\u0645": { - "isolated": "\uFC34", - "initial": "\uFCC3" - }, - "\u0642\u0649": { - "isolated": "\uFC35", - "final": "\uFC7E" - }, - "\u0642\u064A": { - "isolated": "\uFC36", - "final": "\uFC7F" - }, - "\u0643\u0627": { - "isolated": "\uFC37", - "final": "\uFC80" - }, - "\u0643\u062C": { - "isolated": "\uFC38", - "initial": "\uFCC4" - }, - "\u0643\u062D": { - "isolated": "\uFC39", - "initial": "\uFCC5" - }, - "\u0643\u062E": { - "isolated": "\uFC3A", - "initial": "\uFCC6" - }, - "\u0643\u0644": { - "isolated": "\uFC3B", - "final": "\uFC81", - "initial": "\uFCC7", - "medial": "\uFCEB" - }, - "\u0643\u0645": { - "isolated": "\uFC3C", - "final": "\uFC82", - "initial": "\uFCC8", - "medial": "\uFCEC" - }, - "\u0643\u0649": { - "isolated": "\uFC3D", - "final": "\uFC83" - }, - "\u0643\u064A": { - "isolated": "\uFC3E", - "final": "\uFC84" - }, - "\u0644\u062C": { - "isolated": "\uFC3F", - "initial": "\uFCC9" - }, - "\u0644\u062D": { - "isolated": "\uFC40", - "initial": "\uFCCA" - }, - "\u0644\u062E": { - "isolated": "\uFC41", - "initial": "\uFCCB" - }, - "\u0644\u0645": { - "isolated": "\uFC42", - "final": "\uFC85", - "initial": "\uFCCC", - "medial": "\uFCED" - }, - "\u0644\u0649": { - "isolated": "\uFC43", - "final": "\uFC86" - }, - "\u0644\u064A": { - "isolated": "\uFC44", - "final": "\uFC87" - }, - "\u0645\u062C": { - "isolated": "\uFC45", - "initial": "\uFCCE" - }, - "\u0645\u062D": { - "isolated": "\uFC46", - "initial": "\uFCCF" - }, - "\u0645\u062E": { - "isolated": "\uFC47", - "initial": "\uFCD0" - }, - "\u0645\u0645": { - "isolated": "\uFC48", - "final": "\uFC89", - "initial": "\uFCD1" - }, - "\u0645\u0649": { - "isolated": "\uFC49" - }, - "\u0645\u064A": { - "isolated": "\uFC4A" - }, - "\u0646\u062C": { - "isolated": "\uFC4B", - "initial": "\uFCD2" - }, - "\u0646\u062D": { - "isolated": "\uFC4C", - "initial": "\uFCD3" - }, - "\u0646\u062E": { - "isolated": "\uFC4D", - "initial": "\uFCD4" - }, - "\u0646\u0645": { - "isolated": "\uFC4E", - "final": "\uFC8C", - "initial": "\uFCD5", - "medial": "\uFCEE" - }, - "\u0646\u0649": { - "isolated": "\uFC4F", - "final": "\uFC8E" - }, - "\u0646\u064A": { - "isolated": "\uFC50", - "final": "\uFC8F" - }, - "\u0647\u062C": { - "isolated": "\uFC51", - "initial": "\uFCD7" - }, - "\u0647\u0645": { - "isolated": "\uFC52", - "initial": "\uFCD8" - }, - "\u0647\u0649": { - "isolated": "\uFC53" - }, - "\u0647\u064A": { - "isolated": "\uFC54" - }, - "\u064A\u062C": { - "isolated": "\uFC55", - "initial": "\uFCDA" - }, - "\u064A\u062D": { - "isolated": "\uFC56", - "initial": "\uFCDB" - }, - "\u064A\u062E": { - "isolated": "\uFC57", - "initial": "\uFCDC" - }, - "\u064A\u0645": { - "isolated": "\uFC58", - "final": "\uFC93", - "initial": "\uFCDD", - "medial": "\uFCF0" - }, - "\u064A\u0649": { - "isolated": "\uFC59", - "final": "\uFC95" - }, - "\u064A\u064A": { - "isolated": "\uFC5A", - "final": "\uFC96" - }, - "\u0630\u0670": { - "isolated": "\uFC5B" - }, - "\u0631\u0670": { - "isolated": "\uFC5C" - }, - "\u0649\u0670": { - "isolated": "\uFC5D", - "final": "\uFC90" - }, - "\u064C\u0651": { - "isolated": "\uFC5E" - }, - "\u064D\u0651": { - "isolated": "\uFC5F" - }, - "\u064E\u0651": { - "isolated": "\uFC60" - }, - "\u064F\u0651": { - "isolated": "\uFC61" - }, - "\u0650\u0651": { - "isolated": "\uFC62" - }, - "\u0651\u0670": { - "isolated": "\uFC63" - }, - "\u0626\u0631": { - "final": "\uFC64" - }, - "\u0626\u0632": { - "final": "\uFC65" - }, - "\u0626\u0646": { - "final": "\uFC67" - }, - "\u0628\u0631": { - "final": "\uFC6A" - }, - "\u0628\u0632": { - "final": "\uFC6B" - }, - "\u0628\u0646": { - "final": "\uFC6D" - }, - "\u062A\u0631": { - "final": "\uFC70" - }, - "\u062A\u0632": { - "final": "\uFC71" - }, - "\u062A\u0646": { - "final": "\uFC73" - }, - "\u062B\u0631": { - "final": "\uFC76" - }, - "\u062B\u0632": { - "final": "\uFC77" - }, - "\u062B\u0646": { - "final": "\uFC79" - }, - "\u062B\u064A": { - "final": "\uFC7B" - }, - "\u0645\u0627": { - "final": "\uFC88" - }, - "\u0646\u0631": { - "final": "\uFC8A" - }, - "\u0646\u0632": { - "final": "\uFC8B" - }, - "\u0646\u0646": { - "final": "\uFC8D" - }, - "\u064A\u0631": { - "final": "\uFC91" - }, - "\u064A\u0632": { - "final": "\uFC92" - }, - "\u064A\u0646": { - "final": "\uFC94" - }, - "\u0626\u062E": { - "initial": "\uFC99" - }, - "\u0626\u0647": { - "initial": "\uFC9B", - "medial": "\uFCE0" - }, - "\u0628\u0647": { - "initial": "\uFCA0", - "medial": "\uFCE2" - }, - "\u062A\u0647": { - "initial": "\uFCA5", - "medial": "\uFCE4" - }, - "\u0635\u062E": { - "initial": "\uFCB2" - }, - "\u0644\u0647": { - "initial": "\uFCCD" - }, - "\u0646\u0647": { - "initial": "\uFCD6", - "medial": "\uFCEF" - }, - "\u0647\u0670": { - "initial": "\uFCD9" - }, - "\u064A\u0647": { - "initial": "\uFCDE", - "medial": "\uFCF1" - }, - "\u062B\u0647": { - "medial": "\uFCE6" - }, - "\u0633\u0647": { - "medial": "\uFCE8", - "initial": "\uFD31" - }, - "\u0634\u0645": { - "medial": "\uFCE9", - "isolated": "\uFD0C", - "final": "\uFD28", - "initial": "\uFD30" - }, - "\u0634\u0647": { - "medial": "\uFCEA", - "initial": "\uFD32" - }, - "\u0640\u064E\u0651": { - "medial": "\uFCF2" - }, - "\u0640\u064F\u0651": { - "medial": "\uFCF3" - }, - "\u0640\u0650\u0651": { - "medial": "\uFCF4" - }, - "\u0637\u0649": { - "isolated": "\uFCF5", - "final": "\uFD11" - }, - "\u0637\u064A": { - "isolated": "\uFCF6", - "final": "\uFD12" - }, - "\u0639\u0649": { - "isolated": "\uFCF7", - "final": "\uFD13" - }, - "\u0639\u064A": { - "isolated": "\uFCF8", - "final": "\uFD14" - }, - "\u063A\u0649": { - "isolated": "\uFCF9", - "final": "\uFD15" - }, - "\u063A\u064A": { - "isolated": "\uFCFA", - "final": "\uFD16" - }, - "\u0633\u0649": { - "isolated": "\uFCFB" - }, - "\u0633\u064A": { - "isolated": "\uFCFC", - "final": "\uFD18" - }, - "\u0634\u0649": { - "isolated": "\uFCFD", - "final": "\uFD19" - }, - "\u0634\u064A": { - "isolated": "\uFCFE", - "final": "\uFD1A" - }, - "\u062D\u0649": { - "isolated": "\uFCFF", - "final": "\uFD1B" - }, - "\u062D\u064A": { - "isolated": "\uFD00", - "final": "\uFD1C" - }, - "\u062C\u0649": { - "isolated": "\uFD01", - "final": "\uFD1D" - }, - "\u062C\u064A": { - "isolated": "\uFD02", - "final": "\uFD1E" - }, - "\u062E\u0649": { - "isolated": "\uFD03", - "final": "\uFD1F" - }, - "\u062E\u064A": { - "isolated": "\uFD04", - "final": "\uFD20" - }, - "\u0635\u0649": { - "isolated": "\uFD05", - "final": "\uFD21" - }, - "\u0635\u064A": { - "isolated": "\uFD06", - "final": "\uFD22" - }, - "\u0636\u0649": { - "isolated": "\uFD07", - "final": "\uFD23" - }, - "\u0636\u064A": { - "isolated": "\uFD08", - "final": "\uFD24" - }, - "\u0634\u062C": { - "isolated": "\uFD09", - "final": "\uFD25", - "initial": "\uFD2D", - "medial": "\uFD37" - }, - "\u0634\u062D": { - "isolated": "\uFD0A", - "final": "\uFD26", - "initial": "\uFD2E", - "medial": "\uFD38" - }, - "\u0634\u062E": { - "isolated": "\uFD0B", - "final": "\uFD27", - "initial": "\uFD2F", - "medial": "\uFD39" - }, - "\u0634\u0631": { - "isolated": "\uFD0D", - "final": "\uFD29" - }, - "\u0633\u0631": { - "isolated": "\uFD0E", - "final": "\uFD2A" - }, - "\u0635\u0631": { - "isolated": "\uFD0F", - "final": "\uFD2B" - }, - "\u0636\u0631": { - "isolated": "\uFD10", - "final": "\uFD2C" - }, - "\u0633\u0639": { - "final": "\uFD17" - }, - "\u062A\u062C\u0645": { - "initial": "\uFD50" - }, - "\u062A\u062D\u062C": { - "final": "\uFD51", - "initial": "\uFD52" - }, - "\u062A\u062D\u0645": { - "initial": "\uFD53" - }, - "\u062A\u062E\u0645": { - "initial": "\uFD54" - }, - "\u062A\u0645\u062C": { - "initial": "\uFD55" - }, - "\u062A\u0645\u062D": { - "initial": "\uFD56" - }, - "\u062A\u0645\u062E": { - "initial": "\uFD57" - }, - "\u062C\u0645\u062D": { - "final": "\uFD58", - "initial": "\uFD59" - }, - "\u062D\u0645\u064A": { - "final": "\uFD5A" - }, - "\u062D\u0645\u0649": { - "final": "\uFD5B" - }, - "\u0633\u062D\u062C": { - "initial": "\uFD5C" - }, - "\u0633\u062C\u062D": { - "initial": "\uFD5D" - }, - "\u0633\u062C\u0649": { - "final": "\uFD5E" - }, - "\u0633\u0645\u062D": { - "final": "\uFD5F", - "initial": "\uFD60" - }, - "\u0633\u0645\u062C": { - "initial": "\uFD61" - }, - "\u0633\u0645\u0645": { - "final": "\uFD62", - "initial": "\uFD63" - }, - "\u0635\u062D\u062D": { - "final": "\uFD64", - "initial": "\uFD65" - }, - "\u0635\u0645\u0645": { - "final": "\uFD66", - "initial": "\uFDC5" - }, - "\u0634\u062D\u0645": { - "final": "\uFD67", - "initial": "\uFD68" - }, - "\u0634\u062C\u064A": { - "final": "\uFD69" - }, - "\u0634\u0645\u062E": { - "final": "\uFD6A", - "initial": "\uFD6B" - }, - "\u0634\u0645\u0645": { - "final": "\uFD6C", - "initial": "\uFD6D" - }, - "\u0636\u062D\u0649": { - "final": "\uFD6E" - }, - "\u0636\u062E\u0645": { - "final": "\uFD6F", - "initial": "\uFD70" - }, - "\u0636\u0645\u062D": { - "final": "\uFD71" - }, - "\u0637\u0645\u062D": { - "initial": "\uFD72" - }, - "\u0637\u0645\u0645": { - "initial": "\uFD73" - }, - "\u0637\u0645\u064A": { - "final": "\uFD74" - }, - "\u0639\u062C\u0645": { - "final": "\uFD75", - "initial": "\uFDC4" - }, - "\u0639\u0645\u0645": { - "final": "\uFD76", - "initial": "\uFD77" - }, - "\u0639\u0645\u0649": { - "final": "\uFD78" - }, - "\u063A\u0645\u0645": { - "final": "\uFD79" - }, - "\u063A\u0645\u064A": { - "final": "\uFD7A" - }, - "\u063A\u0645\u0649": { - "final": "\uFD7B" - }, - "\u0641\u062E\u0645": { - "final": "\uFD7C", - "initial": "\uFD7D" - }, - "\u0642\u0645\u062D": { - "final": "\uFD7E", - "initial": "\uFDB4" - }, - "\u0642\u0645\u0645": { - "final": "\uFD7F" - }, - "\u0644\u062D\u0645": { - "final": "\uFD80", - "initial": "\uFDB5" - }, - "\u0644\u062D\u064A": { - "final": "\uFD81" - }, - "\u0644\u062D\u0649": { - "final": "\uFD82" - }, - "\u0644\u062C\u062C": { - "initial": "\uFD83", - "final": "\uFD84" - }, - "\u0644\u062E\u0645": { - "final": "\uFD85", - "initial": "\uFD86" - }, - "\u0644\u0645\u062D": { - "final": "\uFD87", - "initial": "\uFD88" - }, - "\u0645\u062D\u062C": { - "initial": "\uFD89" - }, - "\u0645\u062D\u0645": { - "initial": "\uFD8A" - }, - "\u0645\u062D\u064A": { - "final": "\uFD8B" - }, - "\u0645\u062C\u062D": { - "initial": "\uFD8C" - }, - "\u0645\u062C\u0645": { - "initial": "\uFD8D" - }, - "\u0645\u062E\u062C": { - "initial": "\uFD8E" - }, - "\u0645\u062E\u0645": { - "initial": "\uFD8F" - }, - "\u0645\u062C\u062E": { - "initial": "\uFD92" - }, - "\u0647\u0645\u062C": { - "initial": "\uFD93" - }, - "\u0647\u0645\u0645": { - "initial": "\uFD94" - }, - "\u0646\u062D\u0645": { - "initial": "\uFD95" - }, - "\u0646\u062D\u0649": { - "final": "\uFD96" - }, - "\u0646\u062C\u0645": { - "final": "\uFD97", - "initial": "\uFD98" - }, - "\u0646\u062C\u0649": { - "final": "\uFD99" - }, - "\u0646\u0645\u064A": { - "final": "\uFD9A" - }, - "\u0646\u0645\u0649": { - "final": "\uFD9B" - }, - "\u064A\u0645\u0645": { - "final": "\uFD9C", - "initial": "\uFD9D" - }, - "\u0628\u062E\u064A": { - "final": "\uFD9E" - }, - "\u062A\u062C\u064A": { - "final": "\uFD9F" - }, - "\u062A\u062C\u0649": { - "final": "\uFDA0" - }, - "\u062A\u062E\u064A": { - "final": "\uFDA1" - }, - "\u062A\u062E\u0649": { - "final": "\uFDA2" - }, - "\u062A\u0645\u064A": { - "final": "\uFDA3" - }, - "\u062A\u0645\u0649": { - "final": "\uFDA4" - }, - "\u062C\u0645\u064A": { - "final": "\uFDA5" - }, - "\u062C\u062D\u0649": { - "final": "\uFDA6" - }, - "\u062C\u0645\u0649": { - "final": "\uFDA7" - }, - "\u0633\u062E\u0649": { - "final": "\uFDA8" - }, - "\u0635\u062D\u064A": { - "final": "\uFDA9" - }, - "\u0634\u062D\u064A": { - "final": "\uFDAA" - }, - "\u0636\u062D\u064A": { - "final": "\uFDAB" - }, - "\u0644\u062C\u064A": { - "final": "\uFDAC" - }, - "\u0644\u0645\u064A": { - "final": "\uFDAD" - }, - "\u064A\u062D\u064A": { - "final": "\uFDAE" - }, - "\u064A\u062C\u064A": { - "final": "\uFDAF" - }, - "\u064A\u0645\u064A": { - "final": "\uFDB0" - }, - "\u0645\u0645\u064A": { - "final": "\uFDB1" - }, - "\u0642\u0645\u064A": { - "final": "\uFDB2" - }, - "\u0646\u062D\u064A": { - "final": "\uFDB3" - }, - "\u0639\u0645\u064A": { - "final": "\uFDB6" - }, - "\u0643\u0645\u064A": { - "final": "\uFDB7" - }, - "\u0646\u062C\u062D": { - "initial": "\uFDB8", - "final": "\uFDBD" - }, - "\u0645\u062E\u064A": { - "final": "\uFDB9" - }, - "\u0644\u062C\u0645": { - "initial": "\uFDBA", - "final": "\uFDBC" - }, - "\u0643\u0645\u0645": { - "final": "\uFDBB", - "initial": "\uFDC3" - }, - "\u062C\u062D\u064A": { - "final": "\uFDBE" - }, - "\u062D\u062C\u064A": { - "final": "\uFDBF" - }, - "\u0645\u062C\u064A": { - "final": "\uFDC0" - }, - "\u0641\u0645\u064A": { - "final": "\uFDC1" - }, - "\u0628\u062D\u064A": { - "final": "\uFDC2" - }, - "\u0633\u062E\u064A": { - "final": "\uFDC6" - }, - "\u0646\u062C\u064A": { - "final": "\uFDC7" - }, - "\u0644\u0622": { - "isolated": "\uFEF5", - "final": "\uFEF6" - }, - "\u0644\u0623": { - "isolated": "\uFEF7", - "final": "\uFEF8" - }, - "\u0644\u0625": { - "isolated": "\uFEF9", - "final": "\uFEFA" - }, - "\u0644\u0627": { - "isolated": "\uFEFB", - "final": "\uFEFC" - }, - "words": { - "\u0635\u0644\u06D2": "\uFDF0", - "\u0642\u0644\u06D2": "\uFDF1", - "\u0627\u0644\u0644\u0647": "\uFDF2", - "\u0627\u0643\u0628\u0631": "\uFDF3", - "\u0645\u062D\u0645\u062F": "\uFDF4", - "\u0635\u0644\u0639\u0645": "\uFDF5", - "\u0631\u0633\u0648\u0644": "\uFDF6", - "\u0639\u0644\u064A\u0647": "\uFDF7", - "\u0648\u0633\u0644\u0645": "\uFDF8", - "\u0635\u0644\u0649": "\uFDF9", - "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA", - "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB", - "\u0631\u06CC\u0627\u0644": "\uFDFC" - } - }; - exports.default = ligatureReference; - }); - - var reference = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - const letterList = Object.keys(unicodeArabic.default); - exports.letterList = letterList; - const ligatureList = Object.keys(unicodeLigatures.default); - exports.ligatureList = ligatureList; - const ligatureWordList = Object.keys(unicodeLigatures.default.words); - exports.ligatureWordList = ligatureWordList; - const lams = '\u0644\u06B5\u06B6\u06B7\u06B8'; - exports.lams = lams; - const alefs = '\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774'; - exports.alefs = alefs; - // for (var l = 1; l < lams.length; l++) { - // console.log('-'); - // for (var a = 0; a < alefs.length; a++) { - // console.log(a + ': ' + lams[l] + alefs[a]); - // } - // } - let tashkeel = '\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8'; - exports.tashkeel = tashkeel; - function addToTashkeel(start, finish) { - for (var i = start; i <= finish; i++) { - exports.tashkeel = tashkeel += String.fromCharCode(i); - } - } - addToTashkeel(0x0610, 0x061A); - addToTashkeel(0x064B, 0x065F); - addToTashkeel(0x06D6, 0x06DC); - addToTashkeel(0x06E0, 0x06E4); - addToTashkeel(0x06EA, 0x06ED); - addToTashkeel(0x08D3, 0x08E1); - addToTashkeel(0x08E3, 0x08FF); - addToTashkeel(0xFE70, 0xFE7F); - let lineBreakers = '\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9'; - exports.lineBreakers = lineBreakers; - function addToLineBreakers(start, finish) { - for (var i = start; i <= finish; i++) { - exports.lineBreakers = lineBreakers += String.fromCharCode(i); - } - } - addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored - addToLineBreakers(0x0621, 0x0625); - addToLineBreakers(0x062F, 0x0632); - addToLineBreakers(0x0660, 0x066D); // numerals, math - addToLineBreakers(0x0671, 0x0677); - addToLineBreakers(0x0688, 0x0699); - addToLineBreakers(0x06C3, 0x06CB); - addToLineBreakers(0x06D2, 0x06F9); - addToLineBreakers(0x0759, 0x075B); - addToLineBreakers(0x08AA, 0x08AE); - addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do - // Presentation Forms A includes diacritics but they are meant to stand alone - addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do - // numerals, math - addToLineBreakers(0x10E60, 0x10E7F); - addToLineBreakers(0x1EC70, 0x1ECBF); - addToLineBreakers(0x1EE00, 0x1EEFF); - }); - - var GlyphSplitter_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - function GlyphSplitter(word) { - let letters = []; - let lastLetter = ''; - word.split('').forEach((letter) => { - if (isArabic_1.isArabic(letter)) { - if (reference.tashkeel.indexOf(letter) > -1) { - letters[letters.length - 1] += letter; - } - else if (lastLetter.length && ((reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1) || (reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0))) { - // valid LA forms - letters[letters.length - 1] += letter; - } - else { - letters.push(letter); - } - } - else { - letters.push(letter); - } - if (reference.tashkeel.indexOf(letter) === -1) { - lastLetter = letter; - } - }); - return letters; - } - exports.GlyphSplitter = GlyphSplitter; - }); - - var BaselineSplitter_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - function BaselineSplitter(word) { - let letters = []; - let lastLetter = ''; - word.split('').forEach((letter) => { - if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) { - if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) { - letters[letters.length - 1] += letter; - } - else if (reference.lineBreakers.indexOf(lastLetter) > -1) { - letters.push(letter); - } - else { - letters[letters.length - 1] += letter; - } - } - else { - letters.push(letter); - } - if (reference.tashkeel.indexOf(letter) === -1) { - // don't allow tashkeel to hide line break - lastLetter = letter; - } - }); - return letters; - } - exports.BaselineSplitter = BaselineSplitter; - }); - - var Normalization = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - - - function Normal(word, breakPresentationForm) { - // default is to turn initial/isolated/medial/final presentation form to generic - if (typeof breakPresentationForm === 'undefined') { - breakPresentationForm = true; - } - let returnable = ''; - word.split('').forEach((letter) => { - if (!isArabic_1.isArabic(letter)) { - returnable += letter; - return; - } - for (let w = 0; w < reference.letterList.length; w++) { - // ok so we are checking this potential lettertron - let letterForms = unicodeArabic.default[reference.letterList[w]]; - let versions = Object.keys(letterForms); - for (let v = 0; v < versions.length; v++) { - let localVersion = letterForms[versions[v]]; - if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') { - // look at this embedded object - let embeddedForms = Object.keys(localVersion); - for (let ef = 0; ef < embeddedForms.length; ef++) { - let form = localVersion[embeddedForms[ef]]; - if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) { - // match - // console.log('embedded match'); - if (form === letter) { - // match exact - if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) { - // replace presentation form - // console.log('keeping normal form of the letter'); - if (typeof localVersion['normal'] === 'object') { - returnable += localVersion['normal'][0]; - } - else { - returnable += localVersion['normal']; - } - return; - } - // console.log('keeping this letter'); - returnable += letter; - return; - } - else if (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1) { - // match - returnable += form[0]; - // console.log('added the first letter from the same array'); - return; - } - } - } - } - else if (localVersion === letter) { - // match exact - if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) { - // replace presentation form - // console.log('keeping normal form of the letter'); - if (typeof letterForms['normal'] === 'object') { - returnable += letterForms['normal'][0]; - } - else { - returnable += letterForms['normal']; - } - return; - } - // console.log('keeping this letter'); - returnable += letter; - return; - } - else if (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) { - // match - returnable += localVersion[0]; - // console.log('added the first letter from the same array'); - return; - } - } - } - // try ligatures - for (let v2 = 0; v2 < reference.ligatureList.length; v2++) { - let normalForm = reference.ligatureList[v2]; - if (normalForm !== 'words') { - let ligForms = Object.keys(unicodeLigatures.default[normalForm]); - for (let f = 0; f < ligForms.length; f++) { - if (unicodeLigatures.default[normalForm][ligForms[f]] === letter) { - returnable += normalForm; - return; - } - } - } - } - // try words ligatures - for (let v3 = 0; v3 < reference.ligatureWordList.length; v3++) { - let normalForm = reference.ligatureWordList[v3]; - if (unicodeLigatures.default.words[normalForm] === letter) { - returnable += normalForm; - return; - } - } - returnable += letter; - // console.log('kept the letter') - }); - return returnable; - } - exports.Normal = Normal; - }); - - var CharShaper_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - - function CharShaper(letter, form) { - if (!isArabic_1.isArabic(letter)) { - // fail not Arabic - throw new Error('Not Arabic'); - } - if (letter === "\u0621") { - // hamza alone - return "\u0621"; - } - for (let w = 0; w < reference.letterList.length; w++) { - // ok so we are checking this potential lettertron - let letterForms = unicodeArabic.default[reference.letterList[w]]; - let versions = Object.keys(letterForms); - for (let v = 0; v < versions.length; v++) { - let localVersion = letterForms[versions[v]]; - if ((localVersion === letter) || - (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) { - if (versions.indexOf(form) > -1) { - return letterForms[form]; - } - } - else if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') { - // check embedded - let embeddedVersions = Object.keys(localVersion); - for (let ev = 0; ev < embeddedVersions.length; ev++) { - if ((localVersion[embeddedVersions[ev]] === letter) || - (typeof localVersion[embeddedVersions[ev]] === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1)) { - if (embeddedVersions.indexOf(form) > -1) { - return localVersion[form]; - } - } - } - } - } - } - } - exports.CharShaper = CharShaper; - }); - - var WordShaper_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - - - function WordShaper(word) { - let state = 'initial'; - let output = ''; - for (let w = 0; w < word.length; w++) { - let nextLetter = ' '; - for (let nxw = w + 1; nxw < word.length; nxw++) { - if (!isArabic_1.isArabic(word[nxw])) { - break; - } - if (reference.tashkeel.indexOf(word[nxw]) === -1) { - nextLetter = word[nxw]; - break; - } - } - if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) { - // space or other non-Arabic - output += word[w]; - state = 'initial'; - } - else if (reference.tashkeel.indexOf(word[w]) > -1) { - // tashkeel - add without changing state - output += word[w]; - } - else if ((nextLetter === ' ') // last Arabic letter in this word - || (reference.lineBreakers.indexOf(word[w]) > -1)) { // the current letter is known to break lines - output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final'); - state = 'initial'; - } - else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) { - // LA letters - advance an additional letter after this - output += unicodeLigatures.default[word[w] + nextLetter][(state === 'initial' ? 'isolated' : 'final')]; - while (word[w] !== nextLetter) { - w++; - } - state = 'initial'; - } - else { - output += CharShaper_1.CharShaper(word[w], state); - state = 'medial'; - } - } - return output; - } - exports.WordShaper = WordShaper; - }); - - var ParentLetter_1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - - - function ParentLetter(letter) { - if (!isArabic_1.isArabic(letter)) { - throw new Error('Not an Arabic letter'); - } - for (let w = 0; w < reference.letterList.length; w++) { - // ok so we are checking this potential lettertron - let letterForms = unicodeArabic.default[reference.letterList[w]]; - let versions = Object.keys(letterForms); - for (let v = 0; v < versions.length; v++) { - let localVersion = letterForms[versions[v]]; - if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') { - // look at this embedded object - let embeddedForms = Object.keys(localVersion); - for (let ef = 0; ef < embeddedForms.length; ef++) { - let form = localVersion[embeddedForms[ef]]; - if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) { - // match - return localVersion; - } - } - } - else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) { - // match - return letterForms; - } - } - return null; - } - } - exports.ParentLetter = ParentLetter; - function GrandparentLetter(letter) { - if (!isArabic_1.isArabic(letter)) { - throw new Error('Not an Arabic letter'); - } - for (let w = 0; w < reference.letterList.length; w++) { - // ok so we are checking this potential lettertron - let letterForms = unicodeArabic.default[reference.letterList[w]]; - let versions = Object.keys(letterForms); - for (let v = 0; v < versions.length; v++) { - let localVersion = letterForms[versions[v]]; - if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') { - // look at this embedded object - let embeddedForms = Object.keys(localVersion); - for (let ef = 0; ef < embeddedForms.length; ef++) { - let form = localVersion[embeddedForms[ef]]; - if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) { - // match - return letterForms; - } - } - } - else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) { - // match - return letterForms; - } - } - return null; - } - } - exports.GrandparentLetter = GrandparentLetter; - }); - - var lib$1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - exports.isArabic = isArabic_1.isArabic; - - exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter; - - exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter; - - exports.Normal = Normalization.Normal; - - exports.CharShaper = CharShaper_1.CharShaper; - - exports.WordShaper = WordShaper_1.WordShaper; - - exports.ParentLetter = ParentLetter_1.ParentLetter; - exports.GrandparentLetter = ParentLetter_1.GrandparentLetter; - }); - - // see https://github.com/openstreetmap/iD/pull/3707 - - var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/; - - function fixRTLTextForSvg(inputText) { - var ret = '', rtlBuffer = []; - var arabicRegex = /[\u0600-\u06FF]/g; - var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g; - var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g; - var thaanaVowel = /[\u07A6-\u07B0]/; - var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; - - // Arabic word shaping - if (arabicRegex.test(inputText)) { - inputText = lib$1.WordShaper(inputText); - } - - for (var n = 0; n < inputText.length; n++) { - var c = inputText[n]; - if (arabicMath.test(c)) { - // Arabic numbers go LTR - ret += rtlBuffer.reverse().join(''); - rtlBuffer = [c]; - } else { - if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) { - ret += rtlBuffer.reverse().join(''); - rtlBuffer = []; - } - if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) { - rtlBuffer[rtlBuffer.length - 1] += c; - } else if (rtlRegex.test(c) - // include Arabic presentation forms - || (c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023) - || (c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279)) { - rtlBuffer.push(c); - } else if (c === ' ' && rtlBuffer.length) { - // whitespace within RTL text - rtlBuffer = [rtlBuffer.reverse().join('') + ' ']; - } else { - // non-RTL character - ret += rtlBuffer.reverse().join('') + c; - rtlBuffer = []; - } - } - } - ret += rtlBuffer.reverse().join(''); - return ret; - } - - // https://github.com/openstreetmap/iD/issues/772 - // http://mathiasbynens.be/notes/localstorage-pattern#comment-9 - let _storage; - try { _storage = localStorage; } catch (e) {} // eslint-disable-line no-empty - _storage = _storage || (() => { - let s = {}; - return { - getItem: (k) => s[k], - setItem: (k, v) => s[k] = v, - removeItem: (k) => delete s[k] - }; - })(); - - // - // corePreferences is an interface for persisting basic key-value strings - // within and between iD sessions on the same site. - // - function corePreferences(k, v) { - - try { - if (arguments.length === 1) return _storage.getItem(k); - else if (v === null) _storage.removeItem(k); - else _storage.setItem(k, v); - } catch (e) { - /* eslint-disable no-console */ - if (typeof console !== 'undefined') { - console.error('localStorage quota exceeded'); - } - /* eslint-enable no-console */ - } - - } - - function responseText(response) { - if (!response.ok) throw new Error(response.status + " " + response.statusText); - return response.text(); - } - - function d3_text(input, init) { - return fetch(input, init).then(responseText); - } - - function responseJson(response) { - if (!response.ok) throw new Error(response.status + " " + response.statusText); - if (response.status === 204 || response.status === 205) return; - return response.json(); - } - - function d3_json(input, init) { - return fetch(input, init).then(responseJson); - } - - function parser(type) { - return function(input, init) { - return d3_text(input, init).then(function(text) { - return (new DOMParser).parseFromString(text, type); - }); - }; - } - - var d3_xml = parser("application/xml"); - - var svg = parser("image/svg+xml"); - - let _mainFileFetcher = coreFileFetcher(); // singleton - - // - // coreFileFetcher asynchronously fetches data from JSON files - // - function coreFileFetcher() { - let _this = {}; - let _inflight = {}; - let _fileMap = { - 'address_formats': 'data/address_formats.min.json', - 'deprecated': 'data/deprecated.min.json', - 'discarded': 'data/discarded.min.json', - 'imagery': 'data/imagery.min.json', - 'intro_graph': 'data/intro_graph.min.json', - 'keepRight': 'data/keepRight.min.json', - 'languages': 'data/languages.min.json', - 'locales': 'data/locales.min.json', - 'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json', - 'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json', - 'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json', - 'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json', - 'preset_categories': 'data/preset_categories.min.json', - 'preset_defaults': 'data/preset_defaults.min.json', - 'preset_fields': 'data/preset_fields.min.json', - 'preset_presets': 'data/preset_presets.min.json', - 'phone_formats': 'data/phone_formats.min.json', - 'qa_data': 'data/qa_data.min.json', - 'shortcuts': 'data/shortcuts.min.json', - 'territory_languages': 'data/territory_languages.min.json', - 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json' - }; - - let _cachedData = {}; - // expose the cache; useful for tests - _this.cache = () => _cachedData; - - - // Returns a Promise to fetch data - // (resolved with the data if we have it already) - _this.get = (which) => { - if (_cachedData[which]) { - return Promise.resolve(_cachedData[which]); - } - - const file = _fileMap[which]; - const url = file && _this.asset(file); - if (!url) { - return Promise.reject(`Unknown data file for "${which}"`); - } - - let prom = _inflight[url]; - if (!prom) { - _inflight[url] = prom = d3_json(url) - .then(result => { - delete _inflight[url]; - if (!result) { - throw new Error(`No data loaded for "${which}"`); - } - _cachedData[which] = result; - return result; - }) - .catch(err => { - delete _inflight[url]; - throw err; - }); - } - - return prom; - }; - - - // Accessor for the file map - _this.fileMap = function(val) { - if (!arguments.length) return _fileMap; - _fileMap = val; - return _this; - }; - - let _assetPath = ''; - _this.assetPath = function(val) { - if (!arguments.length) return _assetPath; - _assetPath = val; - return _this; - }; - - let _assetMap = {}; - _this.assetMap = function(val) { - if (!arguments.length) return _assetMap; - _assetMap = val; - return _this; - }; - - _this.asset = (val) => { - if (/^http(s)?:\/\//i.test(val)) return val; - const filename = _assetPath + val; - return _assetMap[filename] || filename; - }; - - return _this; - } - - let _detected; - - function utilDetect(refresh) { - if (_detected && !refresh) return _detected; - _detected = {}; - - const ua = navigator.userAgent; - let m = null; - - /* Browser */ - m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge - if (m !== null) { - _detected.browser = m[1]; - _detected.version = m[2]; - } - if (!_detected.browser) { - m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11 - if (m !== null) { - _detected.browser = 'msie'; - _detected.version = m[1]; - } - } - if (!_detected.browser) { - m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+ - if (m !== null) { - _detected.browser = 'Opera'; - _detected.version = m[2]; - } - } - if (!_detected.browser) { - m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); - if (m !== null) { - _detected.browser = m[1]; - _detected.version = m[2]; - m = ua.match(/version\/([\.\d]+)/i); - if (m !== null) _detected.version = m[1]; - } - } - if (!_detected.browser) { - _detected.browser = navigator.appName; - _detected.version = navigator.appVersion; - } - - // keep major.minor version only.. - _detected.version = _detected.version.split(/\W/).slice(0,2).join('.'); - - // detect other browser capabilities - // Legacy Opera has incomplete svg style support. See #715 - _detected.opera = (_detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15 ); - - if (_detected.browser.toLowerCase() === 'msie') { - _detected.ie = true; - _detected.browser = 'Internet Explorer'; - _detected.support = parseFloat(_detected.version) >= 11; - } else { - _detected.ie = false; - _detected.support = true; - } - - _detected.filedrop = (window.FileReader && 'ondrop' in window); - _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge'); - _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge'); - - - /* Platform */ - if (/Win/.test(ua)) { - _detected.os = 'win'; - _detected.platform = 'Windows'; - } else if (/Mac/.test(ua)) { - _detected.os = 'mac'; - _detected.platform = 'Macintosh'; - } else if (/X11/.test(ua) || /Linux/.test(ua)) { - _detected.os = 'linux'; - _detected.platform = 'Linux'; - } else { - _detected.os = 'win'; - _detected.platform = 'Unknown'; - } - - _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || - // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent, - // so assume any "mac" with multitouch is actually iOS - (navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1)) && - /WebKit/.test(ua) && - !/Edge/.test(ua) && - !window.MSStream; - - - /* Locale */ - // An array of locales requested by the browser in priority order. - _detected.browserLocales = Array.from(new Set( // remove duplicates - [navigator.language] - .concat(navigator.languages || []) - .concat([ - // old property for backwards compatibility - navigator.userLanguage, - // fallback to English - 'en' - ]) - // remove any undefined values - .filter(Boolean) - )); - - - /* Host */ - const loc = window.top.location; - let origin = loc.origin; - if (!origin) { // for unpatched IE11 - origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port: ''); - } - - _detected.host = origin + loc.pathname; - - - return _detected; - } - - var aesJs = createCommonjsModule(function (module, exports) { - /*! MIT License. Copyright 2015-2018 Richard Moore . See LICENSE.txt. */ - (function(root) { - - function checkInt(value) { - return (parseInt(value) === value); - } - - function checkInts(arrayish) { - if (!checkInt(arrayish.length)) { return false; } - - for (var i = 0; i < arrayish.length; i++) { - if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) { - return false; - } - } - - return true; - } - - function coerceArray(arg, copy) { - - // ArrayBuffer view - if (arg.buffer && arg.name === 'Uint8Array') { - - if (copy) { - if (arg.slice) { - arg = arg.slice(); - } else { - arg = Array.prototype.slice.call(arg); - } - } - - return arg; - } - - // It's an array; check it is a valid representation of a byte - if (Array.isArray(arg)) { - if (!checkInts(arg)) { - throw new Error('Array contains invalid value: ' + arg); - } - - return new Uint8Array(arg); - } - - // Something else, but behaves like an array (maybe a Buffer? Arguments?) - if (checkInt(arg.length) && checkInts(arg)) { - return new Uint8Array(arg); - } - - throw new Error('unsupported array-like object'); - } - - function createArray(length) { - return new Uint8Array(length); - } - - function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) { - if (sourceStart != null || sourceEnd != null) { - if (sourceArray.slice) { - sourceArray = sourceArray.slice(sourceStart, sourceEnd); - } else { - sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd); - } - } - targetArray.set(sourceArray, targetStart); - } - - - - var convertUtf8 = (function() { - function toBytes(text) { - var result = [], i = 0; - text = encodeURI(text); - while (i < text.length) { - var c = text.charCodeAt(i++); - - // if it is a % sign, encode the following 2 bytes as a hex value - if (c === 37) { - result.push(parseInt(text.substr(i, 2), 16)); - i += 2; - - // otherwise, just the actual byte - } else { - result.push(c); - } - } - - return coerceArray(result); - } - - function fromBytes(bytes) { - var result = [], i = 0; - - while (i < bytes.length) { - var c = bytes[i]; - - if (c < 128) { - result.push(String.fromCharCode(c)); - i++; - } else if (c > 191 && c < 224) { - result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f))); - i += 2; - } else { - result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f))); - i += 3; - } - } - - return result.join(''); - } - - return { - toBytes: toBytes, - fromBytes: fromBytes, - } - })(); - - var convertHex = (function() { - function toBytes(text) { - var result = []; - for (var i = 0; i < text.length; i += 2) { - result.push(parseInt(text.substr(i, 2), 16)); - } - - return result; - } - - // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html - var Hex = '0123456789abcdef'; - - function fromBytes(bytes) { - var result = []; - for (var i = 0; i < bytes.length; i++) { - var v = bytes[i]; - result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]); - } - return result.join(''); - } - - return { - toBytes: toBytes, - fromBytes: fromBytes, - } - })(); - - - // Number of rounds by keysize - var numberOfRounds = {16: 10, 24: 12, 32: 14}; - - // Round constant words - var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91]; - - // S-box and Inverse S-box (S is for Substitution) - var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]; - var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]; - - // Transformations for encryption - var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a]; - var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616]; - var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16]; - var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c]; - - // Transformations for decryption - var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742]; - var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857]; - var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8]; - var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]; - - // Transformations for decryption key expansion - var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]; - var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697]; - var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46]; - var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d]; - - function convertToInt32(bytes) { - var result = []; - for (var i = 0; i < bytes.length; i += 4) { - result.push( - (bytes[i ] << 24) | - (bytes[i + 1] << 16) | - (bytes[i + 2] << 8) | - bytes[i + 3] - ); - } - return result; - } - - var AES = function(key) { - if (!(this instanceof AES)) { - throw Error('AES must be instanitated with `new`'); - } - - Object.defineProperty(this, 'key', { - value: coerceArray(key, true) - }); - - this._prepare(); - }; - - - AES.prototype._prepare = function() { - - var rounds = numberOfRounds[this.key.length]; - if (rounds == null) { - throw new Error('invalid key size (must be 16, 24 or 32 bytes)'); - } - - // encryption round keys - this._Ke = []; - - // decryption round keys - this._Kd = []; - - for (var i = 0; i <= rounds; i++) { - this._Ke.push([0, 0, 0, 0]); - this._Kd.push([0, 0, 0, 0]); - } - - var roundKeyCount = (rounds + 1) * 4; - var KC = this.key.length / 4; - - // convert the key into ints - var tk = convertToInt32(this.key); - - // copy values into round key arrays - var index; - for (var i = 0; i < KC; i++) { - index = i >> 2; - this._Ke[index][i % 4] = tk[i]; - this._Kd[rounds - index][i % 4] = tk[i]; - } - - // key expansion (fips-197 section 5.2) - var rconpointer = 0; - var t = KC, tt; - while (t < roundKeyCount) { - tt = tk[KC - 1]; - tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^ - (S[(tt >> 8) & 0xFF] << 16) ^ - (S[ tt & 0xFF] << 8) ^ - S[(tt >> 24) & 0xFF] ^ - (rcon[rconpointer] << 24)); - rconpointer += 1; - - // key expansion (for non-256 bit) - if (KC != 8) { - for (var i = 1; i < KC; i++) { - tk[i] ^= tk[i - 1]; - } - - // key expansion for 256-bit keys is "slightly different" (fips-197) - } else { - for (var i = 1; i < (KC / 2); i++) { - tk[i] ^= tk[i - 1]; - } - tt = tk[(KC / 2) - 1]; - - tk[KC / 2] ^= (S[ tt & 0xFF] ^ - (S[(tt >> 8) & 0xFF] << 8) ^ - (S[(tt >> 16) & 0xFF] << 16) ^ - (S[(tt >> 24) & 0xFF] << 24)); - - for (var i = (KC / 2) + 1; i < KC; i++) { - tk[i] ^= tk[i - 1]; - } - } - - // copy values into round key arrays - var i = 0, r, c; - while (i < KC && t < roundKeyCount) { - r = t >> 2; - c = t % 4; - this._Ke[r][c] = tk[i]; - this._Kd[rounds - r][c] = tk[i++]; - t++; - } - } - - // inverse-cipher-ify the decryption round key (fips-197 section 5.3) - for (var r = 1; r < rounds; r++) { - for (var c = 0; c < 4; c++) { - tt = this._Kd[r][c]; - this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^ - U2[(tt >> 16) & 0xFF] ^ - U3[(tt >> 8) & 0xFF] ^ - U4[ tt & 0xFF]); - } - } - }; - - AES.prototype.encrypt = function(plaintext) { - if (plaintext.length != 16) { - throw new Error('invalid plaintext size (must be 16 bytes)'); - } - - var rounds = this._Ke.length - 1; - var a = [0, 0, 0, 0]; - - // convert plaintext to (ints ^ key) - var t = convertToInt32(plaintext); - for (var i = 0; i < 4; i++) { - t[i] ^= this._Ke[0][i]; - } - - // apply round transforms - for (var r = 1; r < rounds; r++) { - for (var i = 0; i < 4; i++) { - a[i] = (T1[(t[ i ] >> 24) & 0xff] ^ - T2[(t[(i + 1) % 4] >> 16) & 0xff] ^ - T3[(t[(i + 2) % 4] >> 8) & 0xff] ^ - T4[ t[(i + 3) % 4] & 0xff] ^ - this._Ke[r][i]); - } - t = a.slice(); - } - - // the last round is special - var result = createArray(16), tt; - for (var i = 0; i < 4; i++) { - tt = this._Ke[rounds][i]; - result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff; - result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff; - result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff; - result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff; - } - - return result; - }; - - AES.prototype.decrypt = function(ciphertext) { - if (ciphertext.length != 16) { - throw new Error('invalid ciphertext size (must be 16 bytes)'); - } - - var rounds = this._Kd.length - 1; - var a = [0, 0, 0, 0]; - - // convert plaintext to (ints ^ key) - var t = convertToInt32(ciphertext); - for (var i = 0; i < 4; i++) { - t[i] ^= this._Kd[0][i]; - } - - // apply round transforms - for (var r = 1; r < rounds; r++) { - for (var i = 0; i < 4; i++) { - a[i] = (T5[(t[ i ] >> 24) & 0xff] ^ - T6[(t[(i + 3) % 4] >> 16) & 0xff] ^ - T7[(t[(i + 2) % 4] >> 8) & 0xff] ^ - T8[ t[(i + 1) % 4] & 0xff] ^ - this._Kd[r][i]); - } - t = a.slice(); - } - - // the last round is special - var result = createArray(16), tt; - for (var i = 0; i < 4; i++) { - tt = this._Kd[rounds][i]; - result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff; - result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff; - result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff; - result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff; - } - - return result; - }; - - - /** - * Mode Of Operation - Electonic Codebook (ECB) - */ - var ModeOfOperationECB = function(key) { - if (!(this instanceof ModeOfOperationECB)) { - throw Error('AES must be instanitated with `new`'); - } - - this.description = "Electronic Code Block"; - this.name = "ecb"; - - this._aes = new AES(key); - }; - - ModeOfOperationECB.prototype.encrypt = function(plaintext) { - plaintext = coerceArray(plaintext); - - if ((plaintext.length % 16) !== 0) { - throw new Error('invalid plaintext size (must be multiple of 16 bytes)'); - } - - var ciphertext = createArray(plaintext.length); - var block = createArray(16); - - for (var i = 0; i < plaintext.length; i += 16) { - copyArray(plaintext, block, 0, i, i + 16); - block = this._aes.encrypt(block); - copyArray(block, ciphertext, i); - } - - return ciphertext; - }; - - ModeOfOperationECB.prototype.decrypt = function(ciphertext) { - ciphertext = coerceArray(ciphertext); - - if ((ciphertext.length % 16) !== 0) { - throw new Error('invalid ciphertext size (must be multiple of 16 bytes)'); - } - - var plaintext = createArray(ciphertext.length); - var block = createArray(16); - - for (var i = 0; i < ciphertext.length; i += 16) { - copyArray(ciphertext, block, 0, i, i + 16); - block = this._aes.decrypt(block); - copyArray(block, plaintext, i); - } - - return plaintext; - }; - - - /** - * Mode Of Operation - Cipher Block Chaining (CBC) - */ - var ModeOfOperationCBC = function(key, iv) { - if (!(this instanceof ModeOfOperationCBC)) { - throw Error('AES must be instanitated with `new`'); - } - - this.description = "Cipher Block Chaining"; - this.name = "cbc"; - - if (!iv) { - iv = createArray(16); - - } else if (iv.length != 16) { - throw new Error('invalid initialation vector size (must be 16 bytes)'); - } - - this._lastCipherblock = coerceArray(iv, true); - - this._aes = new AES(key); - }; - - ModeOfOperationCBC.prototype.encrypt = function(plaintext) { - plaintext = coerceArray(plaintext); - - if ((plaintext.length % 16) !== 0) { - throw new Error('invalid plaintext size (must be multiple of 16 bytes)'); - } - - var ciphertext = createArray(plaintext.length); - var block = createArray(16); - - for (var i = 0; i < plaintext.length; i += 16) { - copyArray(plaintext, block, 0, i, i + 16); - - for (var j = 0; j < 16; j++) { - block[j] ^= this._lastCipherblock[j]; - } - - this._lastCipherblock = this._aes.encrypt(block); - copyArray(this._lastCipherblock, ciphertext, i); - } - - return ciphertext; - }; - - ModeOfOperationCBC.prototype.decrypt = function(ciphertext) { - ciphertext = coerceArray(ciphertext); - - if ((ciphertext.length % 16) !== 0) { - throw new Error('invalid ciphertext size (must be multiple of 16 bytes)'); - } - - var plaintext = createArray(ciphertext.length); - var block = createArray(16); - - for (var i = 0; i < ciphertext.length; i += 16) { - copyArray(ciphertext, block, 0, i, i + 16); - block = this._aes.decrypt(block); - - for (var j = 0; j < 16; j++) { - plaintext[i + j] = block[j] ^ this._lastCipherblock[j]; - } - - copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16); - } - - return plaintext; - }; - - - /** - * Mode Of Operation - Cipher Feedback (CFB) - */ - var ModeOfOperationCFB = function(key, iv, segmentSize) { - if (!(this instanceof ModeOfOperationCFB)) { - throw Error('AES must be instanitated with `new`'); - } - - this.description = "Cipher Feedback"; - this.name = "cfb"; - - if (!iv) { - iv = createArray(16); - - } else if (iv.length != 16) { - throw new Error('invalid initialation vector size (must be 16 size)'); - } - - if (!segmentSize) { segmentSize = 1; } - - this.segmentSize = segmentSize; - - this._shiftRegister = coerceArray(iv, true); - - this._aes = new AES(key); - }; - - ModeOfOperationCFB.prototype.encrypt = function(plaintext) { - if ((plaintext.length % this.segmentSize) != 0) { - throw new Error('invalid plaintext size (must be segmentSize bytes)'); - } - - var encrypted = coerceArray(plaintext, true); - - var xorSegment; - for (var i = 0; i < encrypted.length; i += this.segmentSize) { - xorSegment = this._aes.encrypt(this._shiftRegister); - for (var j = 0; j < this.segmentSize; j++) { - encrypted[i + j] ^= xorSegment[j]; - } - - // Shift the register - copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); - copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); - } - - return encrypted; - }; - - ModeOfOperationCFB.prototype.decrypt = function(ciphertext) { - if ((ciphertext.length % this.segmentSize) != 0) { - throw new Error('invalid ciphertext size (must be segmentSize bytes)'); - } - - var plaintext = coerceArray(ciphertext, true); - - var xorSegment; - for (var i = 0; i < plaintext.length; i += this.segmentSize) { - xorSegment = this._aes.encrypt(this._shiftRegister); - - for (var j = 0; j < this.segmentSize; j++) { - plaintext[i + j] ^= xorSegment[j]; - } - - // Shift the register - copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); - copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); - } - - return plaintext; - }; - - /** - * Mode Of Operation - Output Feedback (OFB) - */ - var ModeOfOperationOFB = function(key, iv) { - if (!(this instanceof ModeOfOperationOFB)) { - throw Error('AES must be instanitated with `new`'); - } - - this.description = "Output Feedback"; - this.name = "ofb"; - - if (!iv) { - iv = createArray(16); - - } else if (iv.length != 16) { - throw new Error('invalid initialation vector size (must be 16 bytes)'); - } - - this._lastPrecipher = coerceArray(iv, true); - this._lastPrecipherIndex = 16; - - this._aes = new AES(key); - }; - - ModeOfOperationOFB.prototype.encrypt = function(plaintext) { - var encrypted = coerceArray(plaintext, true); - - for (var i = 0; i < encrypted.length; i++) { - if (this._lastPrecipherIndex === 16) { - this._lastPrecipher = this._aes.encrypt(this._lastPrecipher); - this._lastPrecipherIndex = 0; - } - encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++]; - } - - return encrypted; - }; - - // Decryption is symetric - ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt; - - - /** - * Counter object for CTR common mode of operation - */ - var Counter = function(initialValue) { - if (!(this instanceof Counter)) { - throw Error('Counter must be instanitated with `new`'); - } - - // We allow 0, but anything false-ish uses the default 1 - if (initialValue !== 0 && !initialValue) { initialValue = 1; } - - if (typeof(initialValue) === 'number') { - this._counter = createArray(16); - this.setValue(initialValue); - - } else { - this.setBytes(initialValue); - } - }; - - Counter.prototype.setValue = function(value) { - if (typeof(value) !== 'number' || parseInt(value) != value) { - throw new Error('invalid counter value (must be an integer)'); - } - - // We cannot safely handle numbers beyond the safe range for integers - if (value > Number.MAX_SAFE_INTEGER) { - throw new Error('integer value out of safe range'); - } - - for (var index = 15; index >= 0; --index) { - this._counter[index] = value % 256; - value = parseInt(value / 256); - } - }; - - Counter.prototype.setBytes = function(bytes) { - bytes = coerceArray(bytes, true); - - if (bytes.length != 16) { - throw new Error('invalid counter bytes size (must be 16 bytes)'); - } - - this._counter = bytes; - }; - - Counter.prototype.increment = function() { - for (var i = 15; i >= 0; i--) { - if (this._counter[i] === 255) { - this._counter[i] = 0; - } else { - this._counter[i]++; - break; - } - } - }; - - - /** - * Mode Of Operation - Counter (CTR) - */ - var ModeOfOperationCTR = function(key, counter) { - if (!(this instanceof ModeOfOperationCTR)) { - throw Error('AES must be instanitated with `new`'); - } - - this.description = "Counter"; - this.name = "ctr"; - - if (!(counter instanceof Counter)) { - counter = new Counter(counter); - } - - this._counter = counter; - - this._remainingCounter = null; - this._remainingCounterIndex = 16; - - this._aes = new AES(key); - }; - - ModeOfOperationCTR.prototype.encrypt = function(plaintext) { - var encrypted = coerceArray(plaintext, true); - - for (var i = 0; i < encrypted.length; i++) { - if (this._remainingCounterIndex === 16) { - this._remainingCounter = this._aes.encrypt(this._counter._counter); - this._remainingCounterIndex = 0; - this._counter.increment(); - } - encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++]; - } - - return encrypted; - }; - - // Decryption is symetric - ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; - - - /////////////////////// - // Padding - - // See:https://tools.ietf.org/html/rfc2315 - function pkcs7pad(data) { - data = coerceArray(data, true); - var padder = 16 - (data.length % 16); - var result = createArray(data.length + padder); - copyArray(data, result); - for (var i = data.length; i < result.length; i++) { - result[i] = padder; - } - return result; - } - - function pkcs7strip(data) { - data = coerceArray(data, true); - if (data.length < 16) { throw new Error('PKCS#7 invalid length'); } - - var padder = data[data.length - 1]; - if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); } - - var length = data.length - padder; - for (var i = 0; i < padder; i++) { - if (data[length + i] !== padder) { - throw new Error('PKCS#7 invalid padding byte'); - } - } - - var result = createArray(length); - copyArray(data, result, 0, 0, length); - return result; - } - - /////////////////////// - // Exporting - - - // The block cipher - var aesjs = { - AES: AES, - Counter: Counter, - - ModeOfOperation: { - ecb: ModeOfOperationECB, - cbc: ModeOfOperationCBC, - cfb: ModeOfOperationCFB, - ofb: ModeOfOperationOFB, - ctr: ModeOfOperationCTR - }, - - utils: { - hex: convertHex, - utf8: convertUtf8 - }, - - padding: { - pkcs7: { - pad: pkcs7pad, - strip: pkcs7strip - } - }, - - _arrayTest: { - coerceArray: coerceArray, - createArray: createArray, - copyArray: copyArray, - } - }; - - - // node.js - { - module.exports = aesjs; - - // RequireJS/AMD - // http://www.requirejs.org/docs/api.html - // https://github.com/amdjs/amdjs-api/wiki/AMD - } - - - })(); - }); - - // See https://github.com/ricmoo/aes-js - // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes). - // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16)); - - // This default signing key is built into iD and can be used to mask/unmask sensitive values. - const DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208]; - - - function utilAesEncrypt(text, key) { - key = key || DEFAULT_128; - const textBytes = aesJs.utils.utf8.toBytes(text); - const aesCtr = new aesJs.ModeOfOperation.ctr(key); - const encryptedBytes = aesCtr.encrypt(textBytes); - const encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes); - return encryptedHex; - } - - - function utilAesDecrypt(encryptedHex, key) { - key = key || DEFAULT_128; - const encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex); - const aesCtr = new aesJs.ModeOfOperation.ctr(key); - const decryptedBytes = aesCtr.decrypt(encryptedBytes); - const text = aesJs.utils.utf8.fromBytes(decryptedBytes); - return text; - } - - function utilCleanTags(tags) { - var out = {}; - for (var k in tags) { - if (!k) continue; - var v = tags[k]; - if (v !== undefined) { - out[k] = cleanValue(k, v); - } - } - - return out; - - - function cleanValue(k, v) { - function keepSpaces(k) { - return /_hours|_times|:conditional$/.test(k); - } - - function skip(k) { - return /^(description|note|fixme)$/.test(k); - } - - if (skip(k)) return v; - - var cleaned = v - .split(';') - .map(function(s) { return s.trim(); }) - .join(keepSpaces(k) ? '; ' : ';'); - - // The code below is not intended to validate websites and emails. - // It is only intended to prevent obvious copy-paste errors. (#2323) - // clean website- and email-like tags - if (k.indexOf('website') !== -1 || - k.indexOf('email') !== -1 || - cleaned.indexOf('http') === 0) { - cleaned = cleaned - .replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars - - } - - return cleaned; - } - } - - // Like selection.property('value', ...), but avoids no-op value sets, - // which can result in layout/repaint thrashing in some situations. - function utilGetSetValue(selection, value) { - function d3_selection_value(value) { - function valueNull() { - delete this.value; - } - - function valueConstant() { - if (this.value !== value) { - this.value = value; - } - } - - function valueFunction() { - var x = value.apply(this, arguments); - if (x == null) { - delete this.value; - } else if (this.value !== x) { - this.value = x; - } - } - - return value == null - ? valueNull : (typeof value === 'function' - ? valueFunction : valueConstant); - } - - if (arguments.length === 1) { - return selection.property('value'); - } - - return selection.each(d3_selection_value(value)); - } - - function utilKeybinding(namespace) { - var _keybindings = {}; - - - function testBindings(isCapturing) { - var didMatch = false; - var bindings = Object.keys(_keybindings).map(function(id) { return _keybindings[id]; }); - var i, binding; - - // Most key shortcuts will accept either lower or uppercase ('h' or 'H'), - // so we don't strictly match on the shift key, but we prioritize - // shifted keybindings first, and fallback to unshifted only if no match. - // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z') - - // priority match shifted keybindings first - for (i = 0; i < bindings.length; i++) { - binding = bindings[i]; - if (!binding.event.modifiers.shiftKey) continue; // no shift - if (!!binding.capture !== isCapturing) continue; - if (matches(binding, true)) { - binding.callback(); - didMatch = true; - } - } - - // then unshifted keybindings - if (didMatch) return; - for (i = 0; i < bindings.length; i++) { - binding = bindings[i]; - if (binding.event.modifiers.shiftKey) continue; // shift - if (!!binding.capture !== isCapturing) continue; - if (matches(binding, false)) { - binding.callback(); - } - } - - - function matches(binding, testShift) { - var event$1 = event; - var isMatch = false; - var tryKeyCode = true; - - // Prefer a match on `KeyboardEvent.key` - if (event$1.key !== undefined) { - tryKeyCode = (event$1.key.charCodeAt(0) > 255); // outside ISO-Latin-1 - isMatch = true; - - if (binding.event.key === undefined) { - isMatch = false; - } else if (Array.isArray(binding.event.key)) { - if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event$1.key.toLowerCase()) === -1) - isMatch = false; - } else { - if (event$1.key.toLowerCase() !== binding.event.key.toLowerCase()) - isMatch = false; - } - } - - // Fallback match on `KeyboardEvent.keyCode`, can happen if: - // - browser doesn't support `KeyboardEvent.key` - // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?) - if (!isMatch && tryKeyCode) { - isMatch = (event$1.keyCode === binding.event.keyCode); - } - - if (!isMatch) return false; - - // test modifier keys - if (!(event$1.ctrlKey && event$1.altKey)) { // if both are set, assume AltGr and skip it - #4096 - if (event$1.ctrlKey !== binding.event.modifiers.ctrlKey) return false; - if (event$1.altKey !== binding.event.modifiers.altKey) return false; - } - if (event$1.metaKey !== binding.event.modifiers.metaKey) return false; - if (testShift && event$1.shiftKey !== binding.event.modifiers.shiftKey) return false; - - return true; - } - } - - - function capture() { - testBindings(true); - } - - - function bubble() { - var tagName = select(event.target).node().tagName; - if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') { - return; - } - testBindings(false); - } - - - function keybinding(selection) { - selection = selection || select(document); - selection.on('keydown.capture.' + namespace, capture, true); - selection.on('keydown.bubble.' + namespace, bubble, false); - return keybinding; - } - - // was: keybinding.off() - keybinding.unbind = function(selection) { - _keybindings = []; - selection = selection || select(document); - selection.on('keydown.capture.' + namespace, null); - selection.on('keydown.bubble.' + namespace, null); - return keybinding; - }; - - - keybinding.clear = function() { - _keybindings = {}; - return keybinding; - }; - - - // Remove one or more keycode bindings. - keybinding.off = function(codes, capture) { - var arr = utilArrayUniq([].concat(codes)); - - for (var i = 0; i < arr.length; i++) { - var id = arr[i] + (capture ? '-capture' : '-bubble'); - delete _keybindings[id]; - } - return keybinding; - }; - - - // Add one or more keycode bindings. - keybinding.on = function(codes, callback, capture) { - if (typeof callback !== 'function') { - return keybinding.off(codes, capture); - } - - var arr = utilArrayUniq([].concat(codes)); - - for (var i = 0; i < arr.length; i++) { - var id = arr[i] + (capture ? '-capture' : '-bubble'); - var binding = { - id: id, - capture: capture, - callback: callback, - event: { - key: undefined, // preferred - keyCode: 0, // fallback - modifiers: { - shiftKey: false, - ctrlKey: false, - altKey: false, - metaKey: false - } - } - }; - - if (_keybindings[id]) { - console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console - } - - _keybindings[id] = binding; - - var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g); - for (var j = 0; j < matches.length; j++) { - // Normalise matching errors - if (matches[j] === '++') matches[j] = '+'; - - if (matches[j] in utilKeybinding.modifierCodes) { - var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]]; - binding.event.modifiers[prop] = true; - } else { - binding.event.key = utilKeybinding.keys[matches[j]] || matches[j]; - if (matches[j] in utilKeybinding.keyCodes) { - binding.event.keyCode = utilKeybinding.keyCodes[matches[j]]; - } - } - } - } - - return keybinding; - }; - - - return keybinding; - } - - - /* - * See https://github.com/keithamus/jwerty - */ - - utilKeybinding.modifierCodes = { - // Shift key, ⇧ - '⇧': 16, shift: 16, - // CTRL key, on Mac: ⌃ - '⌃': 17, ctrl: 17, - // ALT key, on Mac: ⌥ (Alt) - '⌥': 18, alt: 18, option: 18, - // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super) - '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91 - }; - - utilKeybinding.modifierProperties = { - 16: 'shiftKey', - 17: 'ctrlKey', - 18: 'altKey', - 91: 'metaKey' - }; - - utilKeybinding.keys = { - // Backspace key, on Mac: ⌫ (Backspace) - '⌫': 'Backspace', backspace: 'Backspace', - // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ - '⇥': 'Tab', '⇆': 'Tab', tab: 'Tab', - // Return key, ↩ - '↩': 'Enter', 'return': 'Enter', enter: 'Enter', '⌅': 'Enter', - // Pause/Break key - 'pause': 'Pause', 'pause-break': 'Pause', - // Caps Lock key, ⇪ - '⇪': 'CapsLock', caps: 'CapsLock', 'caps-lock': 'CapsLock', - // Escape key, on Mac: ⎋, on Windows: Esc - '⎋': ['Escape', 'Esc'], escape: ['Escape', 'Esc'], esc: ['Escape', 'Esc'], - // Space key - space: [' ', 'Spacebar'], - // Page-Up key, or pgup, on Mac: ↖ - '↖': 'PageUp', pgup: 'PageUp', 'page-up': 'PageUp', - // Page-Down key, or pgdown, on Mac: ↘ - '↘': 'PageDown', pgdown: 'PageDown', 'page-down': 'PageDown', - // END key, on Mac: ⇟ - '⇟': 'End', end: 'End', - // HOME key, on Mac: ⇞ - '⇞': 'Home', home: 'Home', - // Insert key, or ins - ins: 'Insert', insert: 'Insert', - // Delete key, on Mac: ⌦ (Delete) - '⌦': ['Delete', 'Del'], del: ['Delete', 'Del'], 'delete': ['Delete', 'Del'], - // Left Arrow Key, or ← - '←': ['ArrowLeft', 'Left'], left: ['ArrowLeft', 'Left'], 'arrow-left': ['ArrowLeft', 'Left'], - // Up Arrow Key, or ↑ - '↑': ['ArrowUp', 'Up'], up: ['ArrowUp', 'Up'], 'arrow-up': ['ArrowUp', 'Up'], - // Right Arrow Key, or → - '→': ['ArrowRight', 'Right'], right: ['ArrowRight', 'Right'], 'arrow-right': ['ArrowRight', 'Right'], - // Up Arrow Key, or ↓ - '↓': ['ArrowDown', 'Down'], down: ['ArrowDown', 'Down'], 'arrow-down': ['ArrowDown', 'Down'], - // odities, stuff for backward compatibility (browsers and code): - // Num-Multiply, or * - '*': ['*', 'Multiply'], star: ['*', 'Multiply'], asterisk: ['*', 'Multiply'], multiply: ['*', 'Multiply'], - // Num-Plus or + - '+': ['+', 'Add'], 'plus': ['+', 'Add'], - // Num-Subtract, or - - '-': ['-', 'Subtract'], subtract: ['-', 'Subtract'], 'dash': ['-', 'Subtract'], - // Semicolon - semicolon: ';', - // = or equals - equals: '=', - // Comma, or , - comma: ',', - // Period, or ., or full-stop - period: '.', 'full-stop': '.', - // Slash, or /, or forward-slash - slash: '/', 'forward-slash': '/', - // Tick, or `, or back-quote - tick: '`', 'back-quote': '`', - // Open bracket, or [ - 'open-bracket': '[', - // Back slash, or \ - 'back-slash': '\\', - // Close backet, or ] - 'close-bracket': ']', - // Apostrophe, or Quote, or ' - quote: '\'', apostrophe: '\'', - // NUMPAD 0-9 - 'num-0': '0', - 'num-1': '1', - 'num-2': '2', - 'num-3': '3', - 'num-4': '4', - 'num-5': '5', - 'num-6': '6', - 'num-7': '7', - 'num-8': '8', - 'num-9': '9', - // F1-F25 - f1: 'F1', - f2: 'F2', - f3: 'F3', - f4: 'F4', - f5: 'F5', - f6: 'F6', - f7: 'F7', - f8: 'F8', - f9: 'F9', - f10: 'F10', - f11: 'F11', - f12: 'F12', - f13: 'F13', - f14: 'F14', - f15: 'F15', - f16: 'F16', - f17: 'F17', - f18: 'F18', - f19: 'F19', - f20: 'F20', - f21: 'F21', - f22: 'F22', - f23: 'F23', - f24: 'F24', - f25: 'F25' - }; - - utilKeybinding.keyCodes = { - // Backspace key, on Mac: ⌫ (Backspace) - '⌫': 8, backspace: 8, - // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ - '⇥': 9, '⇆': 9, tab: 9, - // Return key, ↩ - '↩': 13, 'return': 13, enter: 13, '⌅': 13, - // Pause/Break key - 'pause': 19, 'pause-break': 19, - // Caps Lock key, ⇪ - '⇪': 20, caps: 20, 'caps-lock': 20, - // Escape key, on Mac: ⎋, on Windows: Esc - '⎋': 27, escape: 27, esc: 27, - // Space key - space: 32, - // Page-Up key, or pgup, on Mac: ↖ - '↖': 33, pgup: 33, 'page-up': 33, - // Page-Down key, or pgdown, on Mac: ↘ - '↘': 34, pgdown: 34, 'page-down': 34, - // END key, on Mac: ⇟ - '⇟': 35, end: 35, - // HOME key, on Mac: ⇞ - '⇞': 36, home: 36, - // Insert key, or ins - ins: 45, insert: 45, - // Delete key, on Mac: ⌦ (Delete) - '⌦': 46, del: 46, 'delete': 46, - // Left Arrow Key, or ← - '←': 37, left: 37, 'arrow-left': 37, - // Up Arrow Key, or ↑ - '↑': 38, up: 38, 'arrow-up': 38, - // Right Arrow Key, or → - '→': 39, right: 39, 'arrow-right': 39, - // Up Arrow Key, or ↓ - '↓': 40, down: 40, 'arrow-down': 40, - // odities, printing characters that come out wrong: - // Firefox Equals - 'ffequals': 61, - // Num-Multiply, or * - '*': 106, star: 106, asterisk: 106, multiply: 106, - // Num-Plus or + - '+': 107, 'plus': 107, - // Num-Subtract, or - - '-': 109, subtract: 109, - // Firefox Plus - 'ffplus': 171, - // Firefox Minus - 'ffminus': 173, - // Semicolon - ';': 186, semicolon: 186, - // = or equals - '=': 187, 'equals': 187, - // Comma, or , - ',': 188, comma: 188, - // Dash / Underscore key - 'dash': 189, - // Period, or ., or full-stop - '.': 190, period: 190, 'full-stop': 190, - // Slash, or /, or forward-slash - '/': 191, slash: 191, 'forward-slash': 191, - // Tick, or `, or back-quote - '`': 192, tick: 192, 'back-quote': 192, - // Open bracket, or [ - '[': 219, 'open-bracket': 219, - // Back slash, or \ - '\\': 220, 'back-slash': 220, - // Close backet, or ] - ']': 221, 'close-bracket': 221, - // Apostrophe, or Quote, or ' - '\'': 222, quote: 222, apostrophe: 222 - }; - - // NUMPAD 0-9 - var i$2 = 95, n = 0; - while (++i$2 < 106) { - utilKeybinding.keyCodes['num-' + n] = i$2; - ++n; - } - - // 0-9 - i$2 = 47; n = 0; - while (++i$2 < 58) { - utilKeybinding.keyCodes[n] = i$2; - ++n; - } - - // F1-F25 - i$2 = 111; n = 1; - while (++i$2 < 136) { - utilKeybinding.keyCodes['f' + n] = i$2; - ++n; - } - - // a-z - i$2 = 64; - while (++i$2 < 91) { - utilKeybinding.keyCodes[String.fromCharCode(i$2).toLowerCase()] = i$2; - } - - function utilObjectOmit(obj, omitKeys) { - return Object.keys(obj).reduce(function(result, key) { - if (omitKeys.indexOf(key) === -1) { - result[key] = obj[key]; // keep - } - return result; - }, {}); - } - - // Copies a variable number of methods from source to target. - function utilRebind(target, source) { - var i = 1, n = arguments.length, method; - while (++i < n) { - target[method = arguments[i]] = d3_rebind(target, source, source[method]); - } - return target; - } - - // Method is assumed to be a standard D3 getter-setter: - // If passed with no arguments, gets the value. - // If passed with arguments, sets the value and returns the target. - function d3_rebind(target, source, method) { - return function() { - var value = method.apply(source, arguments); - return value === source ? target : value; - }; - } - - // A per-domain session mutex backed by a cookie and dead man's - // switch. If the session crashes, the mutex will auto-release - // after 5 seconds. - - // This accepts a string and returns an object that complies with utilSessionMutexType - function utilSessionMutex(name) { - var mutex = {}; - var intervalID; - - function renew() { - var expires = new Date(); - expires.setSeconds(expires.getSeconds() + 5); - document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict'; - } - - mutex.lock = function () { - if (intervalID) return true; - var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); - if (cookie) return false; - renew(); - intervalID = window.setInterval(renew, 4000); - return true; - }; - - mutex.unlock = function () { - if (!intervalID) return; - document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict'; - clearInterval(intervalID); - intervalID = null; - }; - - mutex.locked = function () { - return !!intervalID; - }; - - return mutex; - } - - function utilTiler() { - var _size = [256, 256]; - var _scale = 256; - var _tileSize = 256; - var _zoomExtent = [0, 20]; - var _translate = [_size[0] / 2, _size[1] / 2]; - var _margin = 0; - var _skipNullIsland = false; - - - function clamp(num, min, max) { - return Math.max(min, Math.min(num, max)); - } - - - function nearNullIsland(tile) { - var x = tile[0]; - var y = tile[1]; - var z = tile[2]; - if (z >= 7) { - var center = Math.pow(2, z - 1); - var width = Math.pow(2, z - 6); - var min = center - (width / 2); - var max = center + (width / 2) - 1; - return x >= min && x <= max && y >= min && y <= max; - } - return false; - } - - - function tiler() { - var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize); - var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]); - var tileMin = 0; - var tileMax = Math.pow(2, z0) - 1; - var log2ts = Math.log(_tileSize) * Math.LOG2E; - var k = Math.pow(2, z - z0 + log2ts); - var origin = [ - (_translate[0] - _scale / 2) / k, - (_translate[1] - _scale / 2) / k - ]; - - var cols = range$1( - clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), - clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1) - ); - var rows = range$1( - clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), - clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1) - ); - - var tiles = []; - for (var i = 0; i < rows.length; i++) { - var y = rows[i]; - for (var j = 0; j < cols.length; j++) { - var x = cols[j]; - - if (i >= _margin && i <= rows.length - _margin && - j >= _margin && j <= cols.length - _margin) { - tiles.unshift([x, y, z0]); // tiles in view at beginning - } else { - tiles.push([x, y, z0]); // tiles in margin at the end - } - } - } - - tiles.translate = origin; - tiles.scale = k; - - return tiles; - } - - - /** - * getTiles() returns an array of tiles that cover the map view - */ - tiler.getTiles = function(projection) { - var origin = [ - projection.scale() * Math.PI - projection.translate()[0], - projection.scale() * Math.PI - projection.translate()[1] - ]; - - this - .size(projection.clipExtent()[1]) - .scale(projection.scale() * 2 * Math.PI) - .translate(projection.translate()); - - var tiles = tiler(); - var ts = tiles.scale; - - return tiles - .map(function(tile) { - if (_skipNullIsland && nearNullIsland(tile)) { - return false; - } - var x = tile[0] * ts - origin[0]; - var y = tile[1] * ts - origin[1]; - return { - id: tile.toString(), - xyz: tile, - extent: geoExtent( - projection.invert([x, y + ts]), - projection.invert([x + ts, y]) - ) - }; - }).filter(Boolean); - }; - - - /** - * getGeoJSON() returns a FeatureCollection for debugging tiles - */ - tiler.getGeoJSON = function(projection) { - var features = tiler.getTiles(projection).map(function(tile) { - return { - type: 'Feature', - properties: { - id: tile.id, - name: tile.id - }, - geometry: { - type: 'Polygon', - coordinates: [ tile.extent.polygon() ] - } - }; - }); - - return { - type: 'FeatureCollection', - features: features - }; - }; - - - tiler.tileSize = function(val) { - if (!arguments.length) return _tileSize; - _tileSize = val; - return tiler; - }; - - - tiler.zoomExtent = function(val) { - if (!arguments.length) return _zoomExtent; - _zoomExtent = val; - return tiler; - }; - - - tiler.size = function(val) { - if (!arguments.length) return _size; - _size = val; - return tiler; - }; - - - tiler.scale = function(val) { - if (!arguments.length) return _scale; - _scale = val; - return tiler; - }; - - - tiler.translate = function(val) { - if (!arguments.length) return _translate; - _translate = val; - return tiler; - }; - - - // number to extend the rows/columns beyond those covering the viewport - tiler.margin = function(val) { - if (!arguments.length) return _margin; - _margin = +val; - return tiler; - }; - - - tiler.skipNullIsland = function(val) { - if (!arguments.length) return _skipNullIsland; - _skipNullIsland = val; - return tiler; - }; - - - return tiler; - } - - function utilTriggerEvent(target, type) { - target.each(function() { - var evt = document.createEvent('HTMLEvents'); - evt.initEvent(type, true, true); - this.dispatchEvent(evt); - }); - } - - let _mainLocalizer = coreLocalizer(); // singleton - let _t = _mainLocalizer.t; - - // - // coreLocalizer manages language and locale parameters including translated strings - // - function coreLocalizer() { - - let localizer = {}; - - let _dataLanguages = {}; - - // `localeData` is an object containing all _supported_ locale codes -> language info. - // { - // en: { rtl: false, languageNames: {…}, scriptNames: {…} }, - // de: { rtl: false, languageNames: {…}, scriptNames: {…} }, - // … - // } - let _dataLocales = {}; - - // `localeStrings` is an object containing all _loaded_ locale codes -> string data. - // { - // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … }, - // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … }, - // … - // } - let _localeStrings = {}; - - // the current locale parameters - let _localeCode = 'en-US'; - let _languageCode = 'en'; - let _textDirection = 'ltr'; - let _usesMetric = false; - let _languageNames = {}; - let _scriptNames = {}; - - // getters for the current locale parameters - localizer.localeCode = () => _localeCode; - localizer.languageCode = () => _languageCode; - localizer.textDirection = () => _textDirection; - localizer.usesMetric = () => _usesMetric; - localizer.languageNames = () => _languageNames; - localizer.scriptNames = () => _scriptNames; - - - var _loadPromise; - - localizer.ensureLoaded = () => { - - if (_loadPromise) return _loadPromise; - - return _loadPromise = Promise.all([ - // load the list of langauges - _mainFileFetcher.get('languages'), - // load the list of supported locales - _mainFileFetcher.get('locales') - ]) - .then(results => { - _dataLanguages = results[0]; - _dataLocales = results[1]; - }) - .then(() => { - const hash = utilStringQs(window.location.hash); - - if (hash.locale && _dataLocales[hash.locale]) { - // the locale can be manually set in the URL hash - _localeCode = hash.locale; - } else { - // otherwise use the locale specified by the browser - _localeCode = supportedBrowserLocale(); - } - - return Promise.all([ - // always load the English locale strings as fallbacks - localizer.loadLocale('en'), - // load the preferred locale - localizer.loadLocale(_localeCode) - ]); - }) - .then(() => { - updateForCurrentLocale(); - }) - .catch(err => console.error(err)); // eslint-disable-line - }; - - // Returns the best locale requested by the browser supported by iD, if any - function supportedBrowserLocale() { - // list of locales preferred by the browser in priority order - let browserLocales = utilDetect().browserLocales; - let supportedLocales = _dataLocales; - - for (let i in browserLocales) { - let browserLocale = browserLocales[i]; - if (browserLocale.includes('-')) { // full locale ('es-ES') - - if (supportedLocales[browserLocale]) return browserLocale; - - // If full locale not supported ('es-FAKE'), fallback to the base ('es') - let langPart = browserLocale.split('-')[0]; - if (supportedLocales[langPart]) return langPart; - - } else { // base locale ('es') - - // prefer a lower-priority full locale with this base ('es' < 'es-ES') - let fullLocale = browserLocales.find((locale, index) => { - return index > i && - locale !== browserLocale && - locale.split('-')[0] === browserLocale && - supportedLocales[locale]; - }); - if (fullLocale) return fullLocale; - - if (supportedLocales[browserLocale]) return browserLocale; - } - } - - return null; - } - - function updateForCurrentLocale() { - if (!_localeCode) return; - - _languageCode = _localeCode.split('-')[0]; - - const currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode]; - - const hash = utilStringQs(window.location.hash); - - if (hash.rtl === 'true') { - _textDirection = 'rtl'; - } else if (hash.rtl === 'false') { - _textDirection = 'ltr'; - } else { - _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr'; - } - - _languageNames = currentData && currentData.languageNames; - _scriptNames = currentData && currentData.scriptNames; - - _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us'; - } - - - /* Locales */ - // Returns a Promise to load the strings for the requested locale - localizer.loadLocale = (requested) => { - - if (!_dataLocales) { - return Promise.reject('loadLocale called before init'); - } - - let locale = requested; - - // US English is the default - if (locale.toLowerCase() === 'en-us') locale = 'en'; - - if (!_dataLocales[locale]) { - return Promise.reject(`Unsupported locale: ${requested}`); - } - - if (_localeStrings[locale]) { // already loaded - return Promise.resolve(locale); - } - - let fileMap = _mainFileFetcher.fileMap(); - const key = `locale_${locale}`; - fileMap[key] = `locales/${locale}.json`; - - return _mainFileFetcher.get(key) - .then(d => { - _localeStrings[locale] = d[locale]; - return locale; - }); - }; - - /** - * Given a string identifier, try to find that string in the current - * language, and return it. This function will be called recursively - * with locale `en` if a string can not be found in the requested language. - * - * @param {string} s string identifier - * @param {object?} replacements token replacements and default string - * @param {string?} locale locale to use (defaults to currentLocale) - * @return {string?} localized string - */ - localizer.t = function(s, replacements, locale) { - locale = locale || _localeCode; - - // US English is the default - if (locale.toLowerCase() === 'en-us') locale = 'en'; - - let path = s - .split('.') - .map(s => s.replace(//g, '.')) - .reverse(); - - let result = _localeStrings[locale]; - - while (result !== undefined && path.length) { - result = result[path.pop()]; - } - - if (result !== undefined) { - if (replacements) { - for (let k in replacements) { - const token = `{${k}}`; - const regex = new RegExp(token, 'g'); - result = result.replace(regex, replacements[k]); - } - } - return result; - } - - if (locale !== 'en') { - return localizer.t(s, replacements, 'en'); // fallback - recurse with 'en' - } - - if (replacements && 'default' in replacements) { - return replacements.default; // fallback - replacements.default - } - - const missing = `Missing ${locale} translation: ${s}`; - if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line - - return missing; - }; - - localizer.languageName = (code, options) => { - - if (_languageNames[code]) { // name in locale langauge - // e.g. "German" - return _languageNames[code]; - } - - // sometimes we only want the local name - if (options && options.localOnly) return null; - - const langInfo = _dataLanguages[code]; - if (langInfo) { - if (langInfo.nativeName) { // name in native language - // e.g. "Deutsch (de)" - return localizer.t('translate.language_and_code', { language: langInfo.nativeName, code: code }); - - } else if (langInfo.base && langInfo.script) { - const base = langInfo.base; // the code of the langauge this is based on - - if (_languageNames[base]) { // base language name in locale langauge - const scriptCode = langInfo.script; - const script = _scriptNames[scriptCode] || scriptCode; - // e.g. "Serbian (Cyrillic)" - return localizer.t('translate.language_and_code', { language: _languageNames[base], code: script }); - - } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) { - // e.g. "српски (sr-Cyrl)" - return localizer.t('translate.language_and_code', { language: _dataLanguages[base].nativeName, code: code }); - } - } - } - return code; // if not found, use the code - }; - - return localizer; - } - - // - // `presetCollection` is a wrapper around an `Array` of presets `collection`, - // and decorated with some extra methods for searching and matching geometry - // - function presetCollection(collection) { - const MAXRESULTS = 50; - let _this = {}; - let _memo = {}; - - _this.collection = collection; - - _this.item = (id) => { - if (_memo[id]) return _memo[id]; - const found = _this.collection.find(d => d.id === id); - if (found) _memo[id] = found; - return found; - }; - - _this.index = (id) => _this.collection.findIndex(d => d.id === id); - - _this.matchGeometry = (geometry) => { - return presetCollection( - _this.collection.filter(d => d.matchGeometry(geometry)) - ); - }; - - _this.matchAllGeometry = (geometries) => { - return presetCollection( - _this.collection.filter(d => d && d.matchAllGeometry(geometries)) - ); - }; - - _this.matchAnyGeometry = (geometries) => { - return presetCollection( - _this.collection.filter(d => geometries.some(geom => d.matchGeometry(geom))) - ); - }; - - _this.fallback = (geometry) => { - let id = geometry; - if (id === 'vertex') id = 'point'; - return _this.item(id); - }; - - _this.search = (value, geometry, countryCode) => { - if (!value) return _this; - - value = value.toLowerCase().trim(); - - // match at name beginning or just after a space (e.g. "office" -> match "Law Office") - function leading(a) { - const index = a.indexOf(value); - return index === 0 || a[index - 1] === ' '; - } - - // match at name beginning only - function leadingStrict(a) { - const index = a.indexOf(value); - return index === 0; - } - - function sortNames(a, b) { - let aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase(); - let bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase(); - - // priority if search string matches preset name exactly - #4325 - if (value === aCompare) return -1; - if (value === bCompare) return 1; - - // priority for higher matchScore - let i = b.originalScore - a.originalScore; - if (i !== 0) return i; - - // priority if search string appears earlier in preset name - i = aCompare.indexOf(value) - bCompare.indexOf(value); - if (i !== 0) return i; - - // priority for shorter preset names - return aCompare.length - bCompare.length; - } - - let pool = _this.collection; - if (countryCode) { - pool = pool.filter(a => { - if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false; - if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false; - return true; - }); - } - const searchable = pool.filter(a => a.searchable !== false && a.suggestion !== true); - const suggestions = pool.filter(a => a.suggestion === true); - - // matches value to preset.name - const leading_name = searchable - .filter(a => leading(a.name().toLowerCase())) - .sort(sortNames); - - // matches value to preset suggestion name (original name is unhyphenated) - const leading_suggestions = suggestions - .filter(a => leadingStrict(a.originalName.toLowerCase())) - .sort(sortNames); - - // matches value to preset.terms values - const leading_terms = searchable - .filter(a => (a.terms() || []).some(leading)); - - // matches value to preset.tags values - const leading_tag_values = searchable - .filter(a => Object.values(a.tags || {}).filter(val => val !== '*').some(leading)); - - // finds close matches to value in preset.name - const similar_name = searchable - .map(a => ({ preset: a, dist: utilEditDistance(value, a.name()) })) - .filter(a => a.dist + Math.min(value.length - a.preset.name().length, 0) < 3) - .sort((a, b) => a.dist - b.dist) - .map(a => a.preset); - - // finds close matches to value to preset suggestion name (original name is unhyphenated) - const similar_suggestions = suggestions - .map(a => ({ preset: a, dist: utilEditDistance(value, a.originalName.toLowerCase()) })) - .filter(a => a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1) - .sort((a, b) => a.dist - b.dist) - .map(a => a.preset); - - // finds close matches to value in preset.terms - const similar_terms = searchable - .filter(a => { - return (a.terms() || []).some(b => { - return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3; - }); - }); - - let results = leading_name.concat( - leading_suggestions, - leading_terms, - leading_tag_values, - similar_name, - similar_suggestions, - similar_terms - ).slice(0, MAXRESULTS - 1); - - if (geometry) { - if (typeof geometry === 'string') { - results.push(_this.fallback(geometry)); - } else { - geometry.forEach(geom => results.push(_this.fallback(geom))); - } - } - - return presetCollection(utilArrayUniq(results)); - }; - - - return _this; - } - - // - // `presetCategory` builds a `presetCollection` of member presets, - // decorated with some extra methods for searching and matching geometry - // - function presetCategory(categoryID, category, all) { - let _this = Object.assign({}, category); // shallow copy - - _this.id = categoryID; - - _this.members = presetCollection( - category.members.map(presetID => all.item(presetID)).filter(Boolean) - ); - - _this.geometry = _this.members.collection - .reduce((acc, preset) => { - for (let i in preset.geometry) { - const geometry = preset.geometry[i]; - if (acc.indexOf(geometry) === -1) { - acc.push(geometry); - } - } - return acc; - }, []); - - _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0; - - _this.matchAllGeometry = (geometries) => _this.members.collection - .some(preset => preset.matchAllGeometry(geometries)); - - _this.matchScore = () => -1; - - _this.name = () => _t(`presets.categories.${categoryID}.name`, { 'default': categoryID }); - - _this.terms = () => []; - - - return _this; - } - - // - // `presetField` decorates a given `field` Object - // with some extra methods for searching and matching geometry - // - function presetField(fieldID, field) { - let _this = Object.assign({}, field); // shallow copy - - _this.id = fieldID; - - // for use in classes, element ids, css selectors - _this.safeid = utilSafeClassName(fieldID); - - _this.matchGeometry = (geom) => !_this.geometry || _this.geometry.indexOf(geom) !== -1; - - _this.matchAllGeometry = (geometries) => { - return !_this.geometry || geometries.every(geom => _this.geometry.indexOf(geom) !== -1); - }; - - _this.t = (scope, options) => _t(`presets.fields.${fieldID}.${scope}`, options); - - _this.label = () => _this.overrideLabel || _this.t('label', { 'default': fieldID }); - - const _placeholder = _this.placeholder; - _this.placeholder = () => _this.t('placeholder', { 'default': _placeholder }); - - _this.originalTerms = (_this.terms || []).join(); - - _this.terms = () => _this.t('terms', { 'default': _this.originalTerms }) - .toLowerCase().trim().split(/\s*,+\s*/); - - - return _this; - } - - // - // `presetPreset` decorates a given `preset` Object - // with some extra methods for searching and matching geometry - // - function presetPreset(presetID, preset, addable, allFields, allPresets) { - allFields = allFields || {}; - allPresets = allPresets || {}; - let _this = Object.assign({}, preset); // shallow copy - let _addable = addable || false; - let _resolvedFields; // cache - let _resolvedMoreFields; // cache - - _this.id = presetID; - - _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids - - _this.originalTerms = (_this.terms || []).join(); - - _this.originalName = _this.name || ''; - - _this.originalScore = _this.matchScore || 1; - - _this.originalReference = _this.reference || {}; - - _this.originalFields = (_this.fields || []); - - _this.originalMoreFields = (_this.moreFields || []); - - _this.fields = () => _resolvedFields || (_resolvedFields = resolve('fields')); - - _this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields')); - - _this.resetFields = () => _resolvedFields = _resolvedMoreFields = null; - - _this.tags = _this.tags || {}; - - _this.addTags = _this.addTags || _this.tags; - - _this.removeTags = _this.removeTags || _this.addTags; - - _this.geometry = (_this.geometry || []); - - _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0; - - _this.matchAllGeometry = (geoms) => geoms.every(_this.matchGeometry); - - _this.matchScore = (entityTags) => { - const tags = _this.tags; - let seen = {}; - let score = 0; - - // match on tags - for (let k in tags) { - seen[k] = true; - if (entityTags[k] === tags[k]) { - score += _this.originalScore; - } else if (tags[k] === '*' && k in entityTags) { - score += _this.originalScore / 2; - } else { - return -1; - } - } - - // boost score for additional matches in addTags - #6802 - const addTags = _this.addTags; - for (let k in addTags) { - if (!seen[k] && entityTags[k] === addTags[k]) { - score += _this.originalScore; - } - } - - return score; - }; - - - let _textCache = {}; - _this.t = (scope, options) => { - const textID = `presets.presets.${presetID}.${scope}`; - if (_textCache[textID]) return _textCache[textID]; - return _textCache[textID] = _t(textID, options); - }; - - - _this.name = () => { - if (_this.suggestion) { - let path = presetID.split('/'); - path.pop(); // remove brand name - // NOTE: insert an en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc) - return _this.originalName + ' – ' + _t('presets.presets.' + path.join('/') + '.name'); - } - return _this.t('name', { 'default': _this.originalName }); - }; - - - _this.terms = () => _this.t('terms', { 'default': _this.originalTerms }) - .toLowerCase().trim().split(/\s*,+\s*/); - - - _this.isFallback = () => { - const tagCount = Object.keys(_this.tags).length; - return tagCount === 0 || (tagCount === 1 && _this.tags.hasOwnProperty('area')); - }; - - - _this.addable = function(val) { - if (!arguments.length) return _addable; - _addable = val; - return _this; - }; - - - _this.reference = (geom) => { - // Lookup documentation on Wikidata... - const qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata']; - if (qid) { - return { qid: qid }; - } - - // Lookup documentation on OSM Wikibase... - let key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0]; - let value = _this.originalReference.value || _this.tags[key]; - - if (geom === 'relation' && key === 'type') { - if (value in _this.tags) { - key = value; - value = _this.tags[key]; - } else { - return { rtype: value }; - } - } - - if (value === '*') { - return { key: key }; - } else { - return { key: key, value: value }; - } - }; - - - _this.unsetTags = (tags, geometry, skipFieldDefaults) => { - tags = utilObjectOmit(tags, Object.keys(_this.removeTags)); - - if (geometry && !skipFieldDefaults) { - _this.fields().forEach(field => { - if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) { - delete tags[field.key]; - } - }); - } - - delete tags.area; - return tags; - }; - - - _this.setTags = (tags, geometry, skipFieldDefaults) => { - const addTags = _this.addTags; - tags = Object.assign({}, tags); // shallow copy - - for (let k in addTags) { - if (addTags[k] === '*') { - tags[k] = 'yes'; - } else { - tags[k] = addTags[k]; - } - } - - // Add area=yes if necessary. - // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of: - // 1. chosen preset could be either an area or a line (`barrier=city_wall`) - // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`) - if (!addTags.hasOwnProperty('area')) { - delete tags.area; - if (geometry === 'area') { - let needsAreaTag = true; - if (_this.geometry.indexOf('line') === -1) { - for (let k in addTags) { - if (k in osmAreaKeys) { - needsAreaTag = false; - break; - } - } - } - if (needsAreaTag) { - tags.area = 'yes'; - } - } - } - - if (geometry && !skipFieldDefaults) { - _this.fields().forEach(field => { - if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) { - tags[field.key] = field.default; - } - }); - } - - return tags; - }; - - - // For a preset without fields, use the fields of the parent preset. - // Replace {preset} placeholders with the fields of the specified presets. - function resolve(which) { - const fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields); - let resolved = []; - - fieldIDs.forEach(fieldID => { - const match = fieldID.match(/\{(.*)\}/); - if (match !== null) { // a presetID wrapped in braces {} - resolved = resolved.concat(inheritFields(match[1], which)); - } else if (allFields[fieldID]) { // a normal fieldID - resolved.push(allFields[fieldID]); - } else { - console.log(`Cannot resolve "${fieldID}" found in ${_this.id}.${which}`); // eslint-disable-line no-console - } - }); - - // no fields resolved, so use the parent's if possible - if (!resolved.length) { - const endIndex = _this.id.lastIndexOf('/'); - const parentID = endIndex && _this.id.substring(0, endIndex); - if (parentID) { - resolved = inheritFields(parentID, which); - } - } - - return utilArrayUniq(resolved); - - - // returns an array of fields to inherit from the given presetID, if found - function inheritFields(presetID, which) { - const parent = allPresets[presetID]; - if (!parent) return []; - - if (which === 'fields') { - return parent.fields().filter(shouldInherit); - } else if (which === 'moreFields') { - return parent.moreFields(); - } else { - return []; - } - } - - - // Skip `fields` for the keys which define the preset. - // These are usually `typeCombo` fields like `shop=*` - function shouldInherit(f) { - if (f.key && _this.tags[f.key] !== undefined && - // inherit anyway if multiple values are allowed or just a checkbox - f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check' - ) return false; - - return true; - } - } - - - return _this; - } - - let _mainPresetIndex = presetIndex(); // singleton - - // - // `presetIndex` wraps a `presetCollection` - // with methods for loading new data and returning defaults - // - function presetIndex() { - const dispatch$1 = dispatch('favoritePreset', 'recentsChange'); - const MAXRECENTS = 30; - - // seed the preset lists with geometry fallbacks - const POINT = presetPreset('point', { name: 'Point', tags: {}, geometry: ['point', 'vertex'], matchScore: 0.1 } ); - const LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } ); - const AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } ); - const RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } ); - - let _this = presetCollection([POINT, LINE, AREA, RELATION]); - let _presets = { point: POINT, line: LINE, area: AREA, relation: RELATION }; - - let _defaults = { - point: presetCollection([POINT]), - vertex: presetCollection([POINT]), - line: presetCollection([LINE]), - area: presetCollection([AREA]), - relation: presetCollection([RELATION]) - }; - - let _fields = {}; - let _categories = {}; - let _universal = []; - let _addablePresetIDs = null; // Set of preset IDs that the user can add - let _recents; - let _favorites; - - // Index of presets by (geometry, tag key). - let _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} }; - - let _loadPromise; - - _this.ensureLoaded = () => { - if (_loadPromise) return _loadPromise; - - return _loadPromise = Promise.all([ - _mainFileFetcher.get('preset_categories'), - _mainFileFetcher.get('preset_defaults'), - _mainFileFetcher.get('preset_presets'), - _mainFileFetcher.get('preset_fields') - ]) - .then(vals => { - _this.merge({ - categories: vals[0], - defaults: vals[1], - presets: vals[2], - fields: vals[3] - }); - osmSetAreaKeys(_this.areaKeys()); - osmSetPointTags(_this.pointTags()); - osmSetVertexTags(_this.vertexTags()); - }); - }; - - - _this.merge = (d) => { - // Merge Fields - if (d.fields) { - Object.keys(d.fields).forEach(fieldID => { - const f = d.fields[fieldID]; - if (f) { // add or replace - _fields[fieldID] = presetField(fieldID, f); - } else { // remove - delete _fields[fieldID]; - } - }); - } - - // Merge Presets - if (d.presets) { - Object.keys(d.presets).forEach(presetID => { - const p = d.presets[presetID]; - if (p) { // add or replace - const isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID); - _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets); - } else { // remove (but not if it's a fallback) - const existing = _presets[presetID]; - if (existing && !existing.isFallback()) { - delete _presets[presetID]; - } - } - }); - } - - // Need to rebuild _this.collection before loading categories - _this.collection = Object.values(_presets).concat(Object.values(_categories)); - - // Merge Categories - if (d.categories) { - Object.keys(d.categories).forEach(categoryID => { - const c = d.categories[categoryID]; - if (c) { // add or replace - _categories[categoryID] = presetCategory(categoryID, c, _this); - } else { // remove - delete _categories[categoryID]; - } - }); - } - - // Rebuild _this.collection after loading categories - _this.collection = Object.values(_presets).concat(Object.values(_categories)); - - // Merge Defaults - if (d.defaults) { - Object.keys(d.defaults).forEach(geometry => { - const def = d.defaults[geometry]; - if (Array.isArray(def)) { // add or replace - _defaults[geometry] = presetCollection( - def.map(id => _presets[id] || _categories[id]).filter(Boolean) - ); - } else { // remove - delete _defaults[geometry]; - } - }); - } - - // Rebuild universal fields array - _universal = Object.values(_fields).filter(field => field.universal); - - // Reset all the preset fields - they'll need to be resolved again - Object.values(_presets).forEach(preset => preset.resetFields()); - - // Rebuild geometry index - _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} }; - _this.collection.forEach(preset => { - (preset.geometry || []).forEach(geometry => { - let g = _geometryIndex[geometry]; - for (let key in preset.tags) { - (g[key] = g[key] || []).push(preset); - } - }); - }); - - return _this; - }; - - - _this.match = (entity, resolver) => { - return resolver.transient(entity, 'presetMatch', () => { - let geometry = entity.geometry(resolver); - // Treat entities on addr:interpolation lines as points, not vertices - #3241 - if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) { - geometry = 'point'; - } - return _this.matchTags(entity.tags, geometry); - }); - }; - - - _this.matchTags = (tags, geometry) => { - const geometryMatches = _geometryIndex[geometry]; - let address; - let best = -1; - let match; - - for (let k in tags) { - // If any part of an address is present, allow fallback to "Address" preset - #4353 - if (/^addr:/.test(k) && geometryMatches['addr:*']) { - address = geometryMatches['addr:*'][0]; - } - - const keyMatches = geometryMatches[k]; - if (!keyMatches) continue; - - for (let i = 0; i < keyMatches.length; i++) { - const score = keyMatches[i].matchScore(tags); - if (score > best) { - best = score; - match = keyMatches[i]; - } - } - } - - if (address && (!match || match.isFallback())) { - match = address; - } - return match || _this.fallback(geometry); - }; - - - _this.allowsVertex = (entity, resolver) => { - if (entity.type !== 'node') return false; - if (Object.keys(entity.tags).length === 0) return true; - - return resolver.transient(entity, 'vertexMatch', () => { - // address lines allow vertices to act as standalone points - if (entity.isOnAddressLine(resolver)) return true; - - const geometries = osmNodeGeometriesForTags(entity.tags); - if (geometries.vertex) return true; - if (geometries.point) return false; - // allow vertices for unspecified points - return true; - }); - }; - - - // Because of the open nature of tagging, iD will never have a complete - // list of tags used in OSM, so we want it to have logic like "assume - // that a closed way with an amenity tag is an area, unless the amenity - // is one of these specific types". This function computes a structure - // that allows testing of such conditions, based on the presets designated - // as as supporting (or not supporting) the area geometry. - // - // The returned object L is a keeplist/discardlist of tags. A closed way - // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])` - // (see `Way#isArea()`). In other words, the keys of L form the keeplist, - // and the subkeys form the discardlist. - _this.areaKeys = () => { - // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions) - const ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type']; - let areaKeys = {}; - - // ignore name-suggestion-index and deprecated presets - const presets = _this.collection.filter(p => !p.suggestion && !p.replacement); - - // keeplist - presets.forEach(p => { - let key; - for (key in p.tags) break; // pick the first tag - if (!key) return; - if (ignore.indexOf(key) !== -1) return; - - if (p.geometry.indexOf('area') !== -1) { // probably an area.. - areaKeys[key] = areaKeys[key] || {}; - } - }); - - // discardlist - presets.forEach(p => { - let key; - for (key in p.addTags) { - // examine all addTags to get a better sense of what can be tagged on lines - #6800 - const value = p.addTags[key]; - if (key in areaKeys && // probably an area... - p.geometry.indexOf('line') !== -1 && // but sometimes a line - value !== '*') { - areaKeys[key][value] = true; - } - } - }); - - return areaKeys; - }; - - - _this.pointTags = () => { - return _this.collection.reduce((pointTags, d) => { - // ignore name-suggestion-index, deprecated, and generic presets - if (d.suggestion || d.replacement || d.searchable === false) return pointTags; - - // only care about the primary tag - let key; - for (key in d.tags) break; // pick the first tag - if (!key) return pointTags; - - // if this can be a point - if (d.geometry.indexOf('point') !== -1) { - pointTags[key] = pointTags[key] || {}; - pointTags[key][d.tags[key]] = true; - } - return pointTags; - }, {}); - }; - - - _this.vertexTags = () => { - return _this.collection.reduce((vertexTags, d) => { - // ignore name-suggestion-index, deprecated, and generic presets - if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; - - // only care about the primary tag - let key; - for (key in d.tags) break; // pick the first tag - if (!key) return vertexTags; - - // if this can be a vertex - if (d.geometry.indexOf('vertex') !== -1) { - vertexTags[key] = vertexTags[key] || {}; - vertexTags[key][d.tags[key]] = true; - } - return vertexTags; - }, {}); - }; - - - _this.field = (id) => _fields[id]; - - _this.universal = () => _universal; - - - _this.defaults = (geometry, n, startWithRecents) => { - let recents = []; - if (startWithRecents) { - recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4); - } - let defaults; - if (_addablePresetIDs) { - defaults = Array.from(_addablePresetIDs).map(function(id) { - var preset = _this.item(id); - if (preset && preset.matchGeometry(geometry)) return preset; - return null; - }).filter(Boolean); - } else { - defaults = _defaults[geometry].collection.concat(_this.fallback(geometry)); - } - - return presetCollection( - utilArrayUniq(recents.concat(defaults)).slice(0, n - 1) - ); - }; - - // pass a Set of addable preset ids - _this.addablePresetIDs = function(val) { - if (!arguments.length) return _addablePresetIDs; - - // accept and convert arrays - if (Array.isArray(val)) val = new Set(val); - - _addablePresetIDs = val; - if (_addablePresetIDs) { // reset all presets - _this.collection.forEach(p => { - // categories aren't addable - if (p.addable) p.addable(_addablePresetIDs.has(p.id)); - }); - } else { - _this.collection.forEach(p => { - if (p.addable) p.addable(true); - }); - } - - return _this; - }; - - - _this.recent = () => { - return presetCollection( - utilArrayUniq(_this.getRecents().map(d => d.preset)) - ); - }; - - - function RibbonItem(preset, source) { - let item = {}; - item.preset = preset; - item.source = source; - - item.isFavorite = () => item.source === 'favorite'; - item.isRecent = () => item.source === 'recent'; - item.matches = (preset) => item.preset.id === preset.id; - item.minified = () => ({ pID: item.preset.id }); - - return item; - } - - - function ribbonItemForMinified(d, source) { - if (d && d.pID) { - const preset = _this.item(d.pID); - if (!preset) return null; - return RibbonItem(preset, source); - } - return null; - } - - - _this.getGenericRibbonItems = () => { - return ['point', 'line', 'area'].map(id => RibbonItem(_this.item(id), 'generic')); - }; - - - _this.getAddable = () => { - if (!_addablePresetIDs) return []; - - return _addablePresetIDs.map((id) => { - const preset = _this.item(id); - if (preset) { - return RibbonItem(preset, 'addable'); - } - }).filter(Boolean); - }; - - - function setRecents(items) { - _recents = items; - const minifiedItems = items.map(d => d.minified()); - corePreferences('preset_recents', JSON.stringify(minifiedItems)); - dispatch$1.call('recentsChange'); - } - - - _this.getRecents = () => { - if (!_recents) { - // fetch from local storage - _recents = (JSON.parse(corePreferences('preset_recents')) || []) - .reduce((acc, d) => { - let item = ribbonItemForMinified(d, 'recent'); - if (item && item.preset.addable()) acc.push(item); - return acc; - }, []); - } - return _recents; - }; - - - _this.addRecent = (preset, besidePreset, after) => { - const recents = _this.getRecents(); - - const beforeItem = _this.recentMatching(besidePreset); - let toIndex = recents.indexOf(beforeItem); - if (after) toIndex += 1; - - const newItem = RibbonItem(preset, 'recent'); - recents.splice(toIndex, 0, newItem); - setRecents(recents); - }; - - - _this.removeRecent = (preset) => { - const item = _this.recentMatching(preset); - if (item) { - let items = _this.getRecents(); - items.splice(items.indexOf(item), 1); - setRecents(items); - } - }; - - - _this.recentMatching = (preset) => { - const items = _this.getRecents(); - for (let i in items) { - if (items[i].matches(preset)) { - return items[i]; - } - } - return null; - }; - - - _this.moveItem = (items, fromIndex, toIndex) => { - if (fromIndex === toIndex || - fromIndex < 0 || toIndex < 0 || - fromIndex >= items.length || toIndex >= items.length - ) return null; - - items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]); - return items; - }; - - - _this.moveRecent = (item, beforeItem) => { - const recents = _this.getRecents(); - const fromIndex = recents.indexOf(item); - const toIndex = recents.indexOf(beforeItem); - const items = _this.moveItem(recents, fromIndex, toIndex); - if (items) setRecents(items); - }; - - - _this.setMostRecent = (preset) => { - if (preset.searchable === false) return; - - let items = _this.getRecents(); - let item = _this.recentMatching(preset); - if (item) { - items.splice(items.indexOf(item), 1); - } else { - item = RibbonItem(preset, 'recent'); - } - - // remove the last recent (first in, first out) - while (items.length >= MAXRECENTS) { - items.pop(); - } - - // prepend array - items.unshift(item); - setRecents(items); - }; - - function setFavorites(items) { - _favorites = items; - const minifiedItems = items.map(d => d.minified()); - corePreferences('preset_favorites', JSON.stringify(minifiedItems)); - - // call update - dispatch$1.call('favoritePreset'); - } - - _this.addFavorite = (preset, besidePreset, after) => { - const favorites = _this.getFavorites(); - - const beforeItem = _this.favoriteMatching(besidePreset); - let toIndex = favorites.indexOf(beforeItem); - if (after) toIndex += 1; - - const newItem = RibbonItem(preset, 'favorite'); - favorites.splice(toIndex, 0, newItem); - setFavorites(favorites); - }; - - _this.toggleFavorite = (preset) => { - const favs = _this.getFavorites(); - const favorite = _this.favoriteMatching(preset); - if (favorite) { - favs.splice(favs.indexOf(favorite), 1); - } else { - // only allow 10 favorites - if (favs.length === 10) { - // remove the last favorite (last in, first out) - favs.pop(); - } - // append array - favs.push(RibbonItem(preset, 'favorite')); - } - setFavorites(favs); - }; - - - _this.removeFavorite = (preset) => { - const item = _this.favoriteMatching(preset); - if (item) { - const items = _this.getFavorites(); - items.splice(items.indexOf(item), 1); - setFavorites(items); - } - }; - - - _this.getFavorites = () => { - if (!_favorites) { - - // fetch from local storage - let rawFavorites = JSON.parse(corePreferences('preset_favorites')); - - if (!rawFavorites) { - rawFavorites = []; - corePreferences('preset_favorites', JSON.stringify(rawFavorites)); - } - - _favorites = rawFavorites.reduce((output, d) => { - const item = ribbonItemForMinified(d, 'favorite'); - if (item && item.preset.addable()) output.push(item); - return output; - }, []); - } - return _favorites; - }; - - - _this.favoriteMatching = (preset) => { - const favs = _this.getFavorites(); - for (let index in favs) { - if (favs[index].matches(preset)) { - return favs[index]; - } - } - return null; - }; - - - return utilRebind(_this, dispatch$1, 'on'); - } - - function utilTagText(entity) { - var obj = (entity && entity.tags) || {}; - return Object.keys(obj) - .map(function(k) { return k + '=' + obj[k]; }) - .join(', '); - } - - - function utilTotalExtent(array, graph) { - var extent = geoExtent(); - var val, entity; - for (var i = 0; i < array.length; i++) { - val = array[i]; - entity = typeof val === 'string' ? graph.hasEntity(val) : val; - if (entity) { - extent._extend(entity.extent(graph)); - } - } - return extent; - } - - - function utilTagDiff(oldTags, newTags) { - var tagDiff = []; - var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort(); - keys.forEach(function(k) { - var oldVal = oldTags[k]; - var newVal = newTags[k]; - - if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) { - tagDiff.push({ - type: '-', - key: k, - oldVal: oldVal, - newVal: newVal, - display: '- ' + k + '=' + oldVal - }); - } - if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) { - tagDiff.push({ - type: '+', - key: k, - oldVal: oldVal, - newVal: newVal, - display: '+ ' + k + '=' + newVal - }); - } - }); - return tagDiff; - } - - - function utilEntitySelector(ids) { - return ids.length ? '.' + ids.join(',.') : 'nothing'; - } - - - // returns an selector to select entity ids for: - // - entityIDs passed in - // - shallow descendant entityIDs for any of those entities that are relations - function utilEntityOrMemberSelector(ids, graph) { - var seen = new Set(ids); - ids.forEach(collectShallowDescendants); - return utilEntitySelector(Array.from(seen)); - - function collectShallowDescendants(id) { - var entity = graph.hasEntity(id); - if (!entity || entity.type !== 'relation') return; - - entity.members - .map(function(member) { return member.id; }) - .forEach(function(id) { seen.add(id); }); - } - } - - - // returns an selector to select entity ids for: - // - entityIDs passed in - // - deep descendant entityIDs for any of those entities that are relations - function utilEntityOrDeepMemberSelector(ids, graph) { - return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph)); - } - - - // returns an selector to select entity ids for: - // - entityIDs passed in - // - deep descendant entityIDs for any of those entities that are relations - function utilEntityAndDeepMemberIDs(ids, graph) { - var seen = new Set(); - ids.forEach(collectDeepDescendants); - return Array.from(seen); - - function collectDeepDescendants(id) { - if (seen.has(id)) return; - seen.add(id); - - var entity = graph.hasEntity(id); - if (!entity || entity.type !== 'relation') return; - - entity.members - .map(function(member) { return member.id; }) - .forEach(collectDeepDescendants); // recurse - } - } - - // returns an selector to select entity ids for: - // - deep descendant entityIDs for any of those entities that are relations - function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) { - var idsSet = new Set(ids); - var seen = new Set(); - var returners = new Set(); - ids.forEach(collectDeepDescendants); - return utilEntitySelector(Array.from(returners)); - - function collectDeepDescendants(id) { - if (seen.has(id)) return; - seen.add(id); - - if (!idsSet.has(id)) { - returners.add(id); - } - - var entity = graph.hasEntity(id); - if (!entity || entity.type !== 'relation') return; - if (skipMultipolgonMembers && entity.isMultipolygon()) return; - entity.members - .map(function(member) { return member.id; }) - .forEach(collectDeepDescendants); // recurse - } - } - - - // Adds or removes highlight styling for the specified entities - function utilHighlightEntities(ids, highlighted, context) { - context.surface() - .selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())) - .classed('highlighted', highlighted); - } - - - // returns an Array that is the union of: - // - nodes for any nodeIDs passed in - // - child nodes of any wayIDs passed in - // - descendant member and child nodes of relationIDs passed in - function utilGetAllNodes(ids, graph) { - var seen = new Set(); - var nodes = new Set(); - - ids.forEach(collectNodes); - return Array.from(nodes); - - function collectNodes(id) { - if (seen.has(id)) return; - seen.add(id); - - var entity = graph.hasEntity(id); - if (!entity) return; - - if (entity.type === 'node') { - nodes.add(entity); - } else if (entity.type === 'way') { - entity.nodes.forEach(collectNodes); - } else { - entity.members - .map(function(member) { return member.id; }) - .forEach(collectNodes); // recurse - } - } - } - - - function utilDisplayName(entity) { - var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase(); - var name = entity.tags[localizedNameKey] || entity.tags.name || ''; - var network = entity.tags.cycle_network || entity.tags.network; - - if (!name && entity.tags.ref) { - name = entity.tags.ref; - if (network) { - name = network + ' ' + name; - } - } - - return name; - } - - - function utilDisplayNameForPath(entity) { - var name = utilDisplayName(entity); - var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1; - - if (!isFirefox && name && rtlRegex.test(name)) { - name = fixRTLTextForSvg(name); - } - - return name; - } - - - function utilDisplayType(id) { - return { - n: _t('inspector.node'), - w: _t('inspector.way'), - r: _t('inspector.relation') - }[id.charAt(0)]; - } - - - function utilDisplayLabel(entity, graph) { - var displayName = utilDisplayName(entity); - if (displayName) { - // use the display name if there is one - return displayName; - } - var preset = _mainPresetIndex.match(entity, graph); - if (preset && preset.name()) { - // use the preset name if there is a match - return preset.name(); - } - // fallback to the display type (node/way/relation) - return utilDisplayType(entity.id); - } - - - function utilEntityRoot(entityType) { - return { - node: 'n', - way: 'w', - relation: 'r' - }[entityType]; - } - - - // Returns a single object containing the tags of all the given entities. - // Example: - // { - // highway: 'service', - // service: 'parking_aisle' - // } - // + - // { - // highway: 'service', - // service: 'driveway', - // width: '3' - // } - // = - // { - // highway: 'service', - // service: [ 'driveway', 'parking_aisle' ], - // width: [ '3', undefined ] - // } - function utilCombinedTags(entityIDs, graph) { - - var tags = {}; - var tagCounts = {}; - var allKeys = new Set(); - - var entities = entityIDs.map(function(entityID) { - return graph.hasEntity(entityID); - }).filter(Boolean); - - // gather the aggregate keys - entities.forEach(function(entity) { - var keys = Object.keys(entity.tags).filter(Boolean); - keys.forEach(function(key) { - allKeys.add(key); - }); - }); - - entities.forEach(function(entity) { - - allKeys.forEach(function(key) { - - var value = entity.tags[key]; // purposely allow `undefined` - - if (!tags.hasOwnProperty(key)) { - // first value, set as raw - tags[key] = value; - } else { - if (!Array.isArray(tags[key])) { - if (tags[key] !== value) { - // first alternate value, replace single value with array - tags[key] = [tags[key], value]; - } - } else { // type is array - if (tags[key].indexOf(value) === -1) { - // subsequent alternate value, add to array - tags[key].push(value); - } - } - } - - var tagHash = key + '=' + value; - if (!tagCounts[tagHash]) tagCounts[tagHash] = 0; - tagCounts[tagHash] += 1; - }); - }); - - for (var key in tags) { - if (!Array.isArray(tags[key])) continue; - - // sort values by frequency then alphabetically - tags[key] = tags[key].sort(function(val1, val2) { - var key = key; // capture - var count2 = tagCounts[key + '=' + val2]; - var count1 = tagCounts[key + '=' + val1]; - if (count2 !== count1) { - return count2 - count1; - } - if (val2 && val1) { - return val1.localeCompare(val2); - } - return val1 ? 1 : -1; - }); - } - - return tags; - } - - - function utilStringQs(str) { - var i = 0; // advance past any leading '?' or '#' characters - while (i < str.length && (str[i] === '?' || str[i] === '#')) i++; - str = str.slice(i); - - return str.split('&').reduce(function(obj, pair){ - var parts = pair.split('='); - if (parts.length === 2) { - obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]); - } - return obj; - }, {}); - } - - - function utilQsString(obj, noencode) { - // encode everything except special characters used in certain hash parameters: - // "/" in map states, ":", ",", {" and "}" in background - function softEncode(s) { - return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent); - } - - return Object.keys(obj).sort().map(function(key) { - return encodeURIComponent(key) + '=' + ( - noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key])); - }).join('&'); - } - - - function utilPrefixDOMProperty(property) { - var prefixes = ['webkit', 'ms', 'moz', 'o']; - var i = -1; - var n = prefixes.length; - var s = document.body; - - if (property in s) - return property; - - property = property.substr(0, 1).toUpperCase() + property.substr(1); - - while (++i < n) { - if (prefixes[i] + property in s) { - return prefixes[i] + property; - } - } - - return false; - } - - - function utilPrefixCSSProperty(property) { - var prefixes = ['webkit', 'ms', 'Moz', 'O']; - var i = -1; - var n = prefixes.length; - var s = document.body.style; - - if (property.toLowerCase() in s) { - return property.toLowerCase(); - } - - while (++i < n) { - if (prefixes[i] + property in s) { - return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase(); - } - } - - return false; - } - - - var transformProperty; - function utilSetTransform(el, x, y, scale) { - var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'); - var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' - : 'translate3d(' + x + 'px,' + y + 'px,0)'; - return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : '')); - } - - - // Calculates Levenshtein distance between two strings - // see: https://en.wikipedia.org/wiki/Levenshtein_distance - // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents. - function utilEditDistance(a, b) { - a = remove$1(a.toLowerCase()); - b = remove$1(b.toLowerCase()); - if (a.length === 0) return b.length; - if (b.length === 0) return a.length; - var matrix = []; - for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; } - for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; } - for (i = 1; i <= b.length; i++) { - for (j = 1; j <= a.length; j++) { - if (b.charAt(i-1) === a.charAt(j-1)) { - matrix[i][j] = matrix[i-1][j-1]; - } else { - matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution - Math.min(matrix[i][j-1] + 1, // insertion - matrix[i-1][j] + 1)); // deletion - } - } - } - return matrix[b.length][a.length]; - } - - - // a d3.mouse-alike which - // 1. Only works on HTML elements, not SVG - // 2. Does not cause style recalculation - function utilFastMouse(container) { - var rect = container.getBoundingClientRect(); - var rectLeft = rect.left; - var rectTop = rect.top; - var clientLeft = +container.clientLeft; - var clientTop = +container.clientTop; - return function(e) { - return [ - e.clientX - rectLeft - clientLeft, - e.clientY - rectTop - clientTop]; - }; - } - - - function utilAsyncMap(inputs, func, callback) { - var remaining = inputs.length; - var results = []; - var errors = []; - - inputs.forEach(function(d, i) { - func(d, function done(err, data) { - errors[i] = err; - results[i] = data; - remaining--; - if (!remaining) callback(errors, results); - }); - }); - } - - - // wraps an index to an interval [0..length-1] - function utilWrap(index, length) { - if (index < 0) { - index += Math.ceil(-index/length)*length; - } - return index % length; - } - - - /** - * a replacement for functor - * - * @param {*} value any value - * @returns {Function} a function that returns that value or the value if it's a function - */ - function utilFunctor(value) { - if (typeof value === 'function') return value; - return function() { - return value; - }; - } - - - function utilNoAuto(selection) { - var isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea'); - - return selection - // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off' - .attr('autocomplete', 'new-password') - .attr('autocorrect', 'off') - .attr('autocapitalize', 'off') - .attr('spellcheck', isText ? 'true' : 'false'); - } - - - // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript - // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/ - function utilHashcode(str) { - var hash = 0; - if (str.length === 0) { - return hash; - } - for (var i = 0; i < str.length; i++) { - var char = str.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // Convert to 32bit integer - } - return hash; - } - - // Returns version of `str` with all runs of special characters replaced by `_`; - // suitable for HTML ids, classes, selectors, etc. - function utilSafeClassName(str) { - return str.toLowerCase().replace(/[^a-z0-9]+/g, '_'); - } - - // Returns string based on `val` that is highly unlikely to collide with an id - // used previously or that's present elsewhere in the document. Useful for preventing - // browser-provided autofills or when embedding iD on pages with unknown elements. - function utilUniqueDomId(val) { - return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString(); - } - - // Returns the length of `str` in unicode characters. This can be less than - // `String.length()` since a single unicode character can be composed of multiple - // JavaScript UTF-16 code units. - function utilUnicodeCharsCount(str) { - // Native ES2015 implementations of `Array.from` split strings into unicode characters - return Array.from(str).length; - } - - // Returns a new string representing `str` cut from its start to `limit` length - // in unicode characters. Note that this runs the risk of splitting graphemes. - function utilUnicodeCharsTruncated(str, limit) { - return Array.from(str).slice(0, limit).join(''); - } - - function osmEntity(attrs) { - // For prototypal inheritance. - if (this instanceof osmEntity) return; - - // Create the appropriate subtype. - if (attrs && attrs.type) { - return osmEntity[attrs.type].apply(this, arguments); - } else if (attrs && attrs.id) { - return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments); - } - - // Initialize a generic Entity (used only in tests). - return (new osmEntity()).initialize(arguments); - } - - - osmEntity.id = function(type) { - return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--); - }; - - - osmEntity.id.next = { - changeset: -1, node: -1, way: -1, relation: -1 - }; - - - osmEntity.id.fromOSM = function(type, id) { - return type[0] + id; - }; - - - osmEntity.id.toOSM = function(id) { - return id.slice(1); - }; - - - osmEntity.id.type = function(id) { - return { 'c': 'changeset', 'n': 'node', 'w': 'way', 'r': 'relation' }[id[0]]; - }; - - - // A function suitable for use as the second argument to d3.selection#data(). - osmEntity.key = function(entity) { - return entity.id + 'v' + (entity.v || 0); - }; - - var _deprecatedTagValuesByKey; - - osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) { - if (!_deprecatedTagValuesByKey) { - _deprecatedTagValuesByKey = {}; - dataDeprecated.forEach(function(d) { - var oldKeys = Object.keys(d.old); - if (oldKeys.length === 1) { - var oldKey = oldKeys[0]; - var oldValue = d.old[oldKey]; - if (oldValue !== '*') { - if (!_deprecatedTagValuesByKey[oldKey]) { - _deprecatedTagValuesByKey[oldKey] = [oldValue]; - } else { - _deprecatedTagValuesByKey[oldKey].push(oldValue); - } - } - } - }); - } - return _deprecatedTagValuesByKey; - }; - - - osmEntity.prototype = { - - tags: {}, - - - initialize: function(sources) { - for (var i = 0; i < sources.length; ++i) { - var source = sources[i]; - for (var prop in source) { - if (Object.prototype.hasOwnProperty.call(source, prop)) { - if (source[prop] === undefined) { - delete this[prop]; - } else { - this[prop] = source[prop]; - } - } - } - } - - if (!this.id && this.type) { - this.id = osmEntity.id(this.type); - } - if (!this.hasOwnProperty('visible')) { - this.visible = true; - } - - return this; - }, - - - copy: function(resolver, copies) { - if (copies[this.id]) - return copies[this.id]; - - var copy = osmEntity(this, { id: undefined, user: undefined, version: undefined }); - copies[this.id] = copy; - - return copy; - }, - - - osmId: function() { - return osmEntity.id.toOSM(this.id); - }, - - - isNew: function() { - return this.osmId() < 0; - }, - - - update: function(attrs) { - return osmEntity(this, attrs, { v: 1 + (this.v || 0) }); - }, - - - mergeTags: function(tags) { - var merged = Object.assign({}, this.tags); // shallow copy - var changed = false; - for (var k in tags) { - var t1 = merged[k]; - var t2 = tags[k]; - if (!t1) { - changed = true; - merged[k] = t2; - } else if (t1 !== t2) { - changed = true; - merged[k] = utilUnicodeCharsTruncated( - utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), - 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue() - ); - } - } - return changed ? this.update({ tags: merged }) : this; - }, - - - intersects: function(extent, resolver) { - return this.extent(resolver).intersects(extent); - }, - - - hasNonGeometryTags: function() { - return Object.keys(this.tags).some(function(k) { return k !== 'area'; }); - }, - - hasParentRelations: function(resolver) { - return resolver.parentRelations(this).length > 0; - }, - - hasInterestingTags: function() { - return Object.keys(this.tags).some(osmIsInterestingTag); - }, - - hasWikidata: function() { - return !!this.tags.wikidata || !!this.tags['brand:wikidata']; - }, - - isHighwayIntersection: function() { - return false; - }, - - isDegenerate: function() { - return true; - }, - - deprecatedTags: function(dataDeprecated) { - var tags = this.tags; - - // if there are no tags, none can be deprecated - if (Object.keys(tags).length === 0) return []; - - var deprecated = []; - dataDeprecated.forEach(function(d) { - var oldKeys = Object.keys(d.old); - var matchesDeprecatedTags = oldKeys.every(function(oldKey) { - if (!tags[oldKey]) return false; - if (d.old[oldKey] === '*') return true; - - var vals = tags[oldKey].split(';').filter(Boolean); - if (vals.length === 0) { - return false; - } else if (vals.length > 1) { - return vals.indexOf(d.old[oldKey]) !== -1; - } else { - if (tags[oldKey] === d.old[oldKey]) { - if (d.replace && d.old[oldKey] === d.replace[oldKey]) { - var replaceKeys = Object.keys(d.replace); - return !replaceKeys.every(function(replaceKey) { - return tags[replaceKey] === d.replace[replaceKey]; - }); - } else { - return true; - } - } - } - return false; - }); - if (matchesDeprecatedTags) { - deprecated.push(d); - } - }); - - return deprecated; - } - }; - - function osmLanes(entity) { - if (entity.type !== 'way') return null; - if (!entity.tags.highway) return null; - - var tags = entity.tags; - var isOneWay = entity.isOneWay(); - var laneCount = getLaneCount(tags, isOneWay); - var maxspeed = parseMaxspeed(tags); - - var laneDirections = parseLaneDirections(tags, isOneWay, laneCount); - var forward = laneDirections.forward; - var backward = laneDirections.backward; - var bothways = laneDirections.bothways; - - // parse the piped string 'x|y|z' format - var turnLanes = {}; - turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']); - turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']); - turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']); - - var maxspeedLanes = {}; - maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed); - maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed); - maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed); - - var psvLanes = {}; - psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']); - psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']); - psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']); - - var busLanes = {}; - busLanes.unspecified = parseMiscLanes(tags['bus:lanes']); - busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']); - busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']); - - var taxiLanes = {}; - taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']); - taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']); - taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']); - - var hovLanes = {}; - hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']); - hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']); - hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']); - - var hgvLanes = {}; - hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']); - hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']); - hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']); - - var bicyclewayLanes = {}; - bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']); - bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']); - bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']); - - var lanesObj = { - forward: [], - backward: [], - unspecified: [] - }; - - // map forward/backward/unspecified of each lane type to lanesObj - mapToLanesObj(lanesObj, turnLanes, 'turnLane'); - mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed'); - mapToLanesObj(lanesObj, psvLanes, 'psv'); - mapToLanesObj(lanesObj, busLanes, 'bus'); - mapToLanesObj(lanesObj, taxiLanes, 'taxi'); - mapToLanesObj(lanesObj, hovLanes, 'hov'); - mapToLanesObj(lanesObj, hgvLanes, 'hgv'); - mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway'); - - return { - metadata: { - count: laneCount, - oneway: isOneWay, - forward: forward, - backward: backward, - bothways: bothways, - turnLanes: turnLanes, - maxspeed: maxspeed, - maxspeedLanes: maxspeedLanes, - psvLanes: psvLanes, - busLanes: busLanes, - taxiLanes: taxiLanes, - hovLanes: hovLanes, - hgvLanes: hgvLanes, - bicyclewayLanes: bicyclewayLanes - }, - lanes: lanesObj - }; - } - - - function getLaneCount(tags, isOneWay) { - var count; - if (tags.lanes) { - count = parseInt(tags.lanes, 10); - if (count > 0) { - return count; - } - } - - - switch (tags.highway) { - case 'trunk': - case 'motorway': - count = isOneWay ? 2 : 4; - break; - default: - count = isOneWay ? 1 : 2; - break; - } - - return count; - } - - - function parseMaxspeed(tags) { - var maxspeed = tags.maxspeed; - if (!maxspeed) return; - - var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/; - if (!maxspeedRegex.test(maxspeed)) return; - - return parseInt(maxspeed, 10); - } - - - function parseLaneDirections(tags, isOneWay, laneCount) { - var forward = parseInt(tags['lanes:forward'], 10); - var backward = parseInt(tags['lanes:backward'], 10); - var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0; - - if (parseInt(tags.oneway, 10) === -1) { - forward = 0; - bothways = 0; - backward = laneCount; - } - else if (isOneWay) { - forward = laneCount; - bothways = 0; - backward = 0; - } - else if (isNaN(forward) && isNaN(backward)) { - backward = Math.floor((laneCount - bothways) / 2); - forward = laneCount - bothways - backward; - } - else if (isNaN(forward)) { - if (backward > laneCount - bothways) { - backward = laneCount - bothways; - } - forward = laneCount - bothways - backward; - } - else if (isNaN(backward)) { - if (forward > laneCount - bothways) { - forward = laneCount - bothways; - } - backward = laneCount - bothways - forward; - } - return { - forward: forward, - backward: backward, - bothways: bothways - }; - } - - - function parseTurnLanes(tag){ - if (!tag) return; - - var validValues = [ - 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', - 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none' - ]; - - return tag.split('|') - .map(function (s) { - if (s === '') s = 'none'; - return s.split(';') - .map(function (d) { - return validValues.indexOf(d) === -1 ? 'unknown': d; - }); - }); - } - - - function parseMaxspeedLanes(tag, maxspeed) { - if (!tag) return; - - return tag.split('|') - .map(function (s) { - if (s === 'none') return s; - var m = parseInt(s, 10); - if (s === '' || m === maxspeed) return null; - return isNaN(m) ? 'unknown': m; - }); - } - - - function parseMiscLanes(tag) { - if (!tag) return; - - var validValues = [ - 'yes', 'no', 'designated' - ]; - - return tag.split('|') - .map(function (s) { - if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; - }); - } - - - function parseBicycleWay(tag) { - if (!tag) return; - - var validValues = [ - 'yes', 'no', 'designated', 'lane' - ]; - - return tag.split('|') - .map(function (s) { - if (s === '') s = 'no'; - return validValues.indexOf(s) === -1 ? 'unknown': s; - }); - } - - - function mapToLanesObj(lanesObj, data, key) { - if (data.forward) data.forward.forEach(function(l, i) { - if (!lanesObj.forward[i]) lanesObj.forward[i] = {}; - lanesObj.forward[i][key] = l; - }); - if (data.backward) data.backward.forEach(function(l, i) { - if (!lanesObj.backward[i]) lanesObj.backward[i] = {}; - lanesObj.backward[i][key] = l; - }); - if (data.unspecified) data.unspecified.forEach(function(l, i) { - if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {}; - lanesObj.unspecified[i][key] = l; - }); - } - - function osmWay() { - if (!(this instanceof osmWay)) { - return (new osmWay()).initialize(arguments); - } else if (arguments.length) { - this.initialize(arguments); - } - } - - - osmEntity.way = osmWay; - - osmWay.prototype = Object.create(osmEntity.prototype); - - - Object.assign(osmWay.prototype, { - type: 'way', - nodes: [], - - - copy: function(resolver, copies) { - if (copies[this.id]) return copies[this.id]; - - var copy = osmEntity.prototype.copy.call(this, resolver, copies); - - var nodes = this.nodes.map(function(id) { - return resolver.entity(id).copy(resolver, copies).id; - }); - - copy = copy.update({ nodes: nodes }); - copies[this.id] = copy; - - return copy; - }, - - - extent: function(resolver) { - return resolver.transient(this, 'extent', function() { - var extent = geoExtent(); - for (var i = 0; i < this.nodes.length; i++) { - var node = resolver.hasEntity(this.nodes[i]); - if (node) { - extent._extend(node.extent()); - } - } - return extent; - }); - }, - - - first: function() { - return this.nodes[0]; - }, - - - last: function() { - return this.nodes[this.nodes.length - 1]; - }, - - - contains: function(node) { - return this.nodes.indexOf(node) >= 0; - }, - - - affix: function(node) { - if (this.nodes[0] === node) return 'prefix'; - if (this.nodes[this.nodes.length - 1] === node) return 'suffix'; - }, - - - layer: function() { - // explicit layer tag, clamp between -10, 10.. - if (isFinite(this.tags.layer)) { - return Math.max(-10, Math.min(+(this.tags.layer), 10)); - } - - // implied layer tag.. - if (this.tags.covered === 'yes') return -1; - if (this.tags.location === 'overground') return 1; - if (this.tags.location === 'underground') return -1; - if (this.tags.location === 'underwater') return -10; - - if (this.tags.power === 'line') return 10; - if (this.tags.power === 'minor_line') return 10; - if (this.tags.aerialway) return 10; - if (this.tags.bridge) return 1; - if (this.tags.cutting) return -1; - if (this.tags.tunnel) return -1; - if (this.tags.waterway) return -1; - if (this.tags.man_made === 'pipeline') return -10; - if (this.tags.boundary) return -10; - return 0; - }, - - - // the approximate width of the line based on its tags except its `width` tag - impliedLineWidthMeters: function() { - var averageWidths = { - highway: { // width is for single lane - motorway: 5, motorway_link: 5, trunk: 4.5, trunk_link: 4.5, - primary: 4, secondary: 4, tertiary: 4, - primary_link: 4, secondary_link: 4, tertiary_link: 4, - unclassified: 4, road: 4, living_street: 4, bus_guideway: 4, pedestrian: 4, - residential: 3.5, service: 3.5, track: 3, cycleway: 2.5, - bridleway: 2, corridor: 2, steps: 2, path: 1.5, footway: 1.5 - }, - railway: { // width includes ties and rail bed, not just track gauge - rail: 2.5, light_rail: 2.5, tram: 2.5, subway: 2.5, - monorail: 2.5, funicular: 2.5, disused: 2.5, preserved: 2.5, - miniature: 1.5, narrow_gauge: 1.5 - }, - waterway: { - river: 50, canal: 25, stream: 5, tidal_channel: 5, fish_pass: 2.5, drain: 2.5, ditch: 1.5 - } - }; - for (var key in averageWidths) { - if (this.tags[key] && averageWidths[key][this.tags[key]]) { - var width = averageWidths[key][this.tags[key]]; - if (key === 'highway') { - var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10); - if (!laneCount) laneCount = this.isOneWay() ? 1 : 2; - - return width * laneCount; - } - return width; - } - } - return null; - }, - - - isOneWay: function() { - // explicit oneway tag.. - var values = { - 'yes': true, - '1': true, - '-1': true, - 'reversible': true, - 'alternating': true, - 'no': false, - '0': false - }; - if (values[this.tags.oneway] !== undefined) { - return values[this.tags.oneway]; - } - - // implied oneway tag.. - for (var key in this.tags) { - if (key in osmOneWayTags && (this.tags[key] in osmOneWayTags[key])) - return true; - } - return false; - }, - - // Some identifier for tag that implies that this way is "sided", - // i.e. the right side is the 'inside' (e.g. the right side of a - // natural=cliff is lower). - sidednessIdentifier: function() { - for (var key in this.tags) { - var value = this.tags[key]; - if (key in osmRightSideIsInsideTags && (value in osmRightSideIsInsideTags[key])) { - if (osmRightSideIsInsideTags[key][value] === true) { - return key; - } else { - // if the map's value is something other than a - // literal true, we should use it so we can - // special case some keys (e.g. natural=coastline - // is handled differently to other naturals). - return osmRightSideIsInsideTags[key][value]; - } - } - } - - return null; - }, - - isSided: function() { - if (this.tags.two_sided === 'yes') { - return false; - } - - return this.sidednessIdentifier() !== null; - }, - - lanes: function() { - return osmLanes(this); - }, - - - isClosed: function() { - return this.nodes.length > 1 && this.first() === this.last(); - }, - - - isConvex: function(resolver) { - if (!this.isClosed() || this.isDegenerate()) return null; - - var nodes = utilArrayUniq(resolver.childNodes(this)); - var coords = nodes.map(function(n) { return n.loc; }); - var curr = 0; - var prev = 0; - - for (var i = 0; i < coords.length; i++) { - var o = coords[(i+1) % coords.length]; - var a = coords[i]; - var b = coords[(i+2) % coords.length]; - var res = geoVecCross(a, b, o); - - curr = (res > 0) ? 1 : (res < 0) ? -1 : 0; - if (curr === 0) { - continue; - } else if (prev && curr !== prev) { - return false; - } - prev = curr; - } - return true; - }, - - // returns an object with the tag that implies this is an area, if any - tagSuggestingArea: function() { - return osmTagSuggestingArea(this.tags); - }, - - isArea: function() { - if (this.tags.area === 'yes') - return true; - if (!this.isClosed() || this.tags.area === 'no') - return false; - return this.tagSuggestingArea() !== null; - }, - - - isDegenerate: function() { - return (new Set(this.nodes).size < (this.isArea() ? 3 : 2)); - }, - - - areAdjacent: function(n1, n2) { - for (var i = 0; i < this.nodes.length; i++) { - if (this.nodes[i] === n1) { - if (this.nodes[i - 1] === n2) return true; - if (this.nodes[i + 1] === n2) return true; - } - } - return false; - }, - - - geometry: function(graph) { - return graph.transient(this, 'geometry', function() { - return this.isArea() ? 'area' : 'line'; - }); - }, - - - // returns an array of objects representing the segments between the nodes in this way - segments: function(graph) { - - function segmentExtent(graph) { - var n1 = graph.hasEntity(this.nodes[0]); - var n2 = graph.hasEntity(this.nodes[1]); - return n1 && n2 && geoExtent([ - [ - Math.min(n1.loc[0], n2.loc[0]), - Math.min(n1.loc[1], n2.loc[1]) - ], - [ - Math.max(n1.loc[0], n2.loc[0]), - Math.max(n1.loc[1], n2.loc[1]) - ] - ]); - } - - return graph.transient(this, 'segments', function() { - var segments = []; - for (var i = 0; i < this.nodes.length - 1; i++) { - segments.push({ - id: this.id + '-' + i, - wayId: this.id, - index: i, - nodes: [this.nodes[i], this.nodes[i + 1]], - extent: segmentExtent - }); - } - return segments; - }); - }, - - - // If this way is not closed, append the beginning node to the end of the nodelist to close it. - close: function() { - if (this.isClosed() || !this.nodes.length) return this; - - var nodes = this.nodes.slice(); - nodes = nodes.filter(noRepeatNodes); - nodes.push(nodes[0]); - return this.update({ nodes: nodes }); - }, - - - // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it. - unclose: function() { - if (!this.isClosed()) return this; - - var nodes = this.nodes.slice(); - var connector = this.first(); - var i = nodes.length - 1; - - // remove trailing connectors.. - while (i > 0 && nodes.length > 1 && nodes[i] === connector) { - nodes.splice(i, 1); - i = nodes.length - 1; - } - - nodes = nodes.filter(noRepeatNodes); - return this.update({ nodes: nodes }); - }, - - - // Adds a node (id) in front of the node which is currently at position index. - // If index is undefined, the node will be added to the end of the way for linear ways, - // or just before the final connecting node for circular ways. - // Consecutive duplicates are eliminated including existing ones. - // Circularity is always preserved when adding a node. - addNode: function(id, index) { - var nodes = this.nodes.slice(); - var isClosed = this.isClosed(); - var max = isClosed ? nodes.length - 1 : nodes.length; - - if (index === undefined) { - index = max; - } - - if (index < 0 || index > max) { - throw new RangeError('index ' + index + ' out of range 0..' + max); - } - - // If this is a closed way, remove all connector nodes except the first one - // (there may be duplicates) and adjust index if necessary.. - if (isClosed) { - var connector = this.first(); - - // leading connectors.. - var i = 1; - while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) { - nodes.splice(i, 1); - if (index > i) index--; - } - - // trailing connectors.. - i = nodes.length - 1; - while (i > 0 && nodes.length > 1 && nodes[i] === connector) { - nodes.splice(i, 1); - if (index > i) index--; - i = nodes.length - 1; - } - } - - nodes.splice(index, 0, id); - nodes = nodes.filter(noRepeatNodes); - - // If the way was closed before, append a connector node to keep it closed.. - if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { - nodes.push(nodes[0]); - } - - return this.update({ nodes: nodes }); - }, - - - // Replaces the node which is currently at position index with the given node (id). - // Consecutive duplicates are eliminated including existing ones. - // Circularity is preserved when updating a node. - updateNode: function(id, index) { - var nodes = this.nodes.slice(); - var isClosed = this.isClosed(); - var max = nodes.length - 1; - - if (index === undefined || index < 0 || index > max) { - throw new RangeError('index ' + index + ' out of range 0..' + max); - } - - // If this is a closed way, remove all connector nodes except the first one - // (there may be duplicates) and adjust index if necessary.. - if (isClosed) { - var connector = this.first(); - - // leading connectors.. - var i = 1; - while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) { - nodes.splice(i, 1); - if (index > i) index--; - } - - // trailing connectors.. - i = nodes.length - 1; - while (i > 0 && nodes.length > 1 && nodes[i] === connector) { - nodes.splice(i, 1); - if (index === i) index = 0; // update leading connector instead - i = nodes.length - 1; - } - } - - nodes.splice(index, 1, id); - nodes = nodes.filter(noRepeatNodes); - - // If the way was closed before, append a connector node to keep it closed.. - if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { - nodes.push(nodes[0]); - } - - return this.update({nodes: nodes}); - }, - - - // Replaces each occurrence of node id needle with replacement. - // Consecutive duplicates are eliminated including existing ones. - // Circularity is preserved. - replaceNode: function(needleID, replacementID) { - var nodes = this.nodes.slice(); - var isClosed = this.isClosed(); - - for (var i = 0; i < nodes.length; i++) { - if (nodes[i] === needleID) { - nodes[i] = replacementID; - } - } - - nodes = nodes.filter(noRepeatNodes); - - // If the way was closed before, append a connector node to keep it closed.. - if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { - nodes.push(nodes[0]); - } - - return this.update({nodes: nodes}); - }, - - - // Removes each occurrence of node id. - // Consecutive duplicates are eliminated including existing ones. - // Circularity is preserved. - removeNode: function(id) { - var nodes = this.nodes.slice(); - var isClosed = this.isClosed(); - - nodes = nodes - .filter(function(node) { return node !== id; }) - .filter(noRepeatNodes); - - // If the way was closed before, append a connector node to keep it closed.. - if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) { - nodes.push(nodes[0]); - } - - return this.update({nodes: nodes}); - }, - - - asJXON: function(changeset_id) { - var r = { - way: { - '@id': this.osmId(), - '@version': this.version || 0, - nd: this.nodes.map(function(id) { - return { keyAttributes: { ref: osmEntity.id.toOSM(id) } }; - }, this), - tag: Object.keys(this.tags).map(function(k) { - return { keyAttributes: { k: k, v: this.tags[k] } }; - }, this) - } - }; - if (changeset_id) { - r.way['@changeset'] = changeset_id; - } - return r; - }, - - - asGeoJSON: function(resolver) { - return resolver.transient(this, 'GeoJSON', function() { - var coordinates = resolver.childNodes(this) - .map(function(n) { return n.loc; }); - - if (this.isArea() && this.isClosed()) { - return { - type: 'Polygon', - coordinates: [coordinates] - }; - } else { - return { - type: 'LineString', - coordinates: coordinates - }; - } - }); - }, - - - area: function(resolver) { - return resolver.transient(this, 'area', function() { - var nodes = resolver.childNodes(this); - - var json = { - type: 'Polygon', - coordinates: [ nodes.map(function(n) { return n.loc; }) ] - }; - - if (!this.isClosed() && nodes.length) { - json.coordinates[0].push(nodes[0].loc); - } - - var area = d3_geoArea(json); - - // Heuristic for detecting counterclockwise winding order. Assumes - // that OpenStreetMap polygons are not hemisphere-spanning. - if (area > 2 * Math.PI) { - json.coordinates[0] = json.coordinates[0].reverse(); - area = d3_geoArea(json); - } - - return isNaN(area) ? 0 : area; - }); - } - }); - - - // Filter function to eliminate consecutive duplicates. - function noRepeatNodes(node, i, arr) { - return i === 0 || node !== arr[i - 1]; - } - - // "Old" multipolyons, previously known as "simple" multipolygons, are as follows: - // - // 1. Relation tagged with `type=multipolygon` and no interesting tags. - // 2. One and only one member with the `outer` role. Must be a way with interesting tags. - // 3. No members without a role. - // - // Old multipolygons are no longer recommended but are still rendered as areas by iD. - - function osmOldMultipolygonOuterMemberOfRelation(entity, graph) { - if (entity.type !== 'relation' || - !entity.isMultipolygon() - || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) { - return false; - } - - var outerMember; - for (var memberIndex in entity.members) { - var member = entity.members[memberIndex]; - if (!member.role || member.role === 'outer') { - if (outerMember) return false; - if (member.type !== 'way') return false; - if (!graph.hasEntity(member.id)) return false; - - outerMember = graph.entity(member.id); - - if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) { - return false; - } - } - } - - return outerMember; - } - - // For fixing up rendering of multipolygons with tags on the outer member. - // https://github.com/openstreetmap/iD/issues/613 - function osmIsOldMultipolygonOuterMember(entity, graph) { - if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) - return false; - - var parents = graph.parentRelations(entity); - if (parents.length !== 1) - return false; - - var parent = parents[0]; - if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) - return false; - - var members = parent.members, member; - for (var i = 0; i < members.length; i++) { - member = members[i]; - if (member.id === entity.id && member.role && member.role !== 'outer') - return false; // Not outer member - if (member.id !== entity.id && (!member.role || member.role === 'outer')) - return false; // Not a simple multipolygon - } - - return parent; - } - - - function osmOldMultipolygonOuterMember(entity, graph) { - if (entity.type !== 'way') - return false; - - var parents = graph.parentRelations(entity); - if (parents.length !== 1) - return false; - - var parent = parents[0]; - if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) - return false; - - var members = parent.members, member, outerMember; - for (var i = 0; i < members.length; i++) { - member = members[i]; - if (!member.role || member.role === 'outer') { - if (outerMember) - return false; // Not a simple multipolygon - outerMember = member; - } - } - - if (!outerMember) - return false; - - var outerEntity = graph.hasEntity(outerMember.id); - if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) - return false; - - return outerEntity; - } - - - // Join `toJoin` array into sequences of connecting ways. - - // Segments which share identical start/end nodes will, as much as possible, - // be connected with each other. - // - // The return value is a nested array. Each constituent array contains elements - // of `toJoin` which have been determined to connect. - // - // Each consitituent array also has a `nodes` property whose value is an - // ordered array of member nodes, with appropriate order reversal and - // start/end coordinate de-duplication. - // - // Members of `toJoin` must have, at minimum, `type` and `id` properties. - // Thus either an array of `osmWay`s or a relation member array may be used. - // - // If an member is an `osmWay`, its tags and childnodes may be reversed via - // `actionReverse` in the output. - // - // The returned sequences array also has an `actions` array property, containing - // any reversal actions that should be applied to the graph, should the calling - // code attempt to actually join the given ways. - // - // Incomplete members (those for which `graph.hasEntity(element.id)` returns - // false) and non-way members are ignored. - // - function osmJoinWays(toJoin, graph) { - function resolve(member) { - return graph.childNodes(graph.entity(member.id)); - } - - function reverse(item) { - var action = actionReverse(item.id, { reverseOneway: true }); - sequences.actions.push(action); - return (item instanceof osmWay) ? action(graph).entity(item.id) : item; - } - - // make a copy containing only the items to join - toJoin = toJoin.filter(function(member) { - return member.type === 'way' && graph.hasEntity(member.id); - }); - - // Are the things we are joining relation members or `osmWays`? - // If `osmWays`, skip the "prefer a forward path" code below (see #4872) - var i; - var joinAsMembers = true; - for (i = 0; i < toJoin.length; i++) { - if (toJoin[i] instanceof osmWay) { - joinAsMembers = false; - break; - } - } - - var sequences = []; - sequences.actions = []; - - while (toJoin.length) { - // start a new sequence - var item = toJoin.shift(); - var currWays = [item]; - var currNodes = resolve(item).slice(); - var doneSequence = false; - - // add to it - while (toJoin.length && !doneSequence) { - var start = currNodes[0]; - var end = currNodes[currNodes.length - 1]; - var fn = null; - var nodes = null; - - // Find the next way/member to join. - for (i = 0; i < toJoin.length; i++) { - item = toJoin[i]; - nodes = resolve(item); - - // (for member ordering only, not way ordering - see #4872) - // Strongly prefer to generate a forward path that preserves the order - // of the members array. For multipolygons and most relations, member - // order does not matter - but for routes, it does. (see #4589) - // If we started this sequence backwards (i.e. next member way attaches to - // the start node and not the end node), reverse the initial way before continuing. - if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && - (nodes[nodes.length - 1] === start || nodes[0] === start) - ) { - currWays[0] = reverse(currWays[0]); - currNodes.reverse(); - start = currNodes[0]; - end = currNodes[currNodes.length - 1]; - } - - if (nodes[0] === end) { - fn = currNodes.push; // join to end - nodes = nodes.slice(1); - break; - } else if (nodes[nodes.length - 1] === end) { - fn = currNodes.push; // join to end - nodes = nodes.slice(0, -1).reverse(); - item = reverse(item); - break; - } else if (nodes[nodes.length - 1] === start) { - fn = currNodes.unshift; // join to beginning - nodes = nodes.slice(0, -1); - break; - } else if (nodes[0] === start) { - fn = currNodes.unshift; // join to beginning - nodes = nodes.slice(1).reverse(); - item = reverse(item); - break; - } else { - fn = nodes = null; - } - } - - if (!nodes) { // couldn't find a joinable way/member - doneSequence = true; - break; - } - - fn.apply(currWays, [item]); - fn.apply(currNodes, nodes); - - toJoin.splice(i, 1); - } - - currWays.nodes = currNodes; - sequences.push(currWays); - } - - return sequences; - } - - function actionAddMember(relationId, member, memberIndex, insertPair) { - - return function action(graph) { - var relation = graph.entity(relationId); - - // There are some special rules for Public Transport v2 routes. - var isPTv2 = /stop|platform/.test(member.role); - - if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) { - // Try to perform sensible inserts based on how the ways join together - graph = addWayMember(relation, graph); - } else { - // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes - // Stops and Platforms for PTv2 should be ordered first. - // hack: We do not currently have the ability to place them in the exactly correct order. - if (isPTv2 && isNaN(memberIndex)) { - memberIndex = 0; - } - - graph = graph.replace(relation.addMember(member, memberIndex)); - } - - return graph; - }; - - - // Add a way member into the relation "wherever it makes sense". - // In this situation we were not supplied a memberIndex. - function addWayMember(relation, graph) { - var groups, tempWay, item, i, j, k; - - // remove PTv2 stops and platforms before doing anything. - var PTv2members = []; - var members = []; - for (i = 0; i < relation.members.length; i++) { - var m = relation.members[i]; - if (/stop|platform/.test(m.role)) { - PTv2members.push(m); - } else { - members.push(m); - } - } - relation = relation.update({ members: members }); - - - if (insertPair) { - // We're adding a member that must stay paired with an existing member. - // (This feature is used by `actionSplit`) - // - // This is tricky because the members may exist multiple times in the - // member list, and with different A-B/B-A ordering and different roles. - // (e.g. a bus route that loops out and back - #4589). - // - // Replace the existing member with a temporary way, - // so that `osmJoinWays` can treat the pair like a single way. - tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes }); - graph = graph.replace(tempWay); - var tempMember = { id: tempWay.id, type: 'way', role: member.role }; - var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true); - groups = utilArrayGroupBy(tempRelation.members, 'type'); - groups.way = groups.way || []; - - } else { - // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it. - groups = utilArrayGroupBy(relation.members, 'type'); - groups.way = groups.way || []; - groups.way.push(member); - } - - members = withIndex(groups.way); - var joined = osmJoinWays(members, graph); - - // `joined` might not contain all of the way members, - // But will contain only the completed (downloaded) members - for (i = 0; i < joined.length; i++) { - var segment = joined[i]; - var nodes = segment.nodes.slice(); - var startIndex = segment[0].index; - - // j = array index in `members` where this segment starts - for (j = 0; j < members.length; j++) { - if (members[j].index === startIndex) { - break; - } - } - - // k = each member in segment - for (k = 0; k < segment.length; k++) { - item = segment[k]; - var way = graph.entity(item.id); - - // If this is a paired item, generate members in correct order and role - if (tempWay && item.id === tempWay.id) { - if (nodes[0].id === insertPair.nodes[0]) { - item.pair = [ - { id: insertPair.originalID, type: 'way', role: item.role }, - { id: insertPair.insertedID, type: 'way', role: item.role } - ]; - } else { - item.pair = [ - { id: insertPair.insertedID, type: 'way', role: item.role }, - { id: insertPair.originalID, type: 'way', role: item.role } - ]; - } - } - - // reorder `members` if necessary - if (k > 0) { - if (j+k >= members.length || item.index !== members[j+k].index) { - moveMember(members, item.index, j+k); - } - } - - nodes.splice(0, way.nodes.length - 1); - } - } - - if (tempWay) { - graph = graph.remove(tempWay); - } - - // Final pass: skip dead items, split pairs, remove index properties - var wayMembers = []; - for (i = 0; i < members.length; i++) { - item = members[i]; - if (item.index === -1) continue; - - if (item.pair) { - wayMembers.push(item.pair[0]); - wayMembers.push(item.pair[1]); - } else { - wayMembers.push(utilObjectOmit(item, ['index'])); - } - } - - // Put stops and platforms first, then nodes, ways, relations - // This is recommended for Public Transport v2 routes: - // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes - var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) ); - - return graph.replace(relation.update({ members: newMembers })); - - - // `moveMember()` changes the `members` array in place by splicing - // the item with `.index = findIndex` to where it belongs, - // and marking the old position as "dead" with `.index = -1` - // - // j=5, k=0 jk - // segment 5 4 7 6 - // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k - // - // j=5, k=1 j k - // segment 5 4 7 6 - // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k - // members 0 1 2 3 x 5 4 6 7 8 9 moved - // - // j=5, k=2 j k - // segment 5 4 7 6 - // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k - // members 0 1 2 3 x 5 4 7 6 x 8 9 moved - // - // j=5, k=3 j k - // segment 5 4 7 6 - // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k - // - function moveMember(arr, findIndex, toIndex) { - for (var i = 0; i < arr.length; i++) { - if (arr[i].index === findIndex) { - break; - } - } - - var item = Object.assign({}, arr[i]); // shallow copy - arr[i].index = -1; // mark as dead - item.index = toIndex; - arr.splice(toIndex, 0, item); - } - - - // This is the same as `Relation.indexedMembers`, - // Except we don't want to index all the members, only the ways - function withIndex(arr) { - var result = new Array(arr.length); - for (var i = 0; i < arr.length; i++) { - result[i] = Object.assign({}, arr[i]); // shallow copy - result[i].index = i; - } - return result; - } - } - - } - - function actionAddMidpoint(midpoint, node) { - return function(graph) { - graph = graph.replace(node.move(midpoint.loc)); - - var parents = utilArrayIntersection( - graph.parentWays(graph.entity(midpoint.edge[0])), - graph.parentWays(graph.entity(midpoint.edge[1])) - ); - - parents.forEach(function(way) { - for (var i = 0; i < way.nodes.length - 1; i++) { - if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) { - graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); - - // Add only one midpoint on doubled-back segments, - // turning them into self-intersections. - return; - } - } - }); - - return graph; - }; - } - - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as - function actionAddVertex(wayId, nodeId, index) { - return function(graph) { - return graph.replace(graph.entity(wayId).addNode(nodeId, index)); - }; - } - - function actionChangeMember(relationId, member, memberIndex) { - return function(graph) { - return graph.replace(graph.entity(relationId).updateMember(member, memberIndex)); - }; - } - - function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) { - return function action(graph) { - var entity = graph.entity(entityID); - var geometry = entity.geometry(graph); - var tags = entity.tags; - - if (oldPreset) tags = oldPreset.unsetTags(tags, geometry); - if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults); - - return graph.replace(entity.update({tags: tags})); - }; - } - - function actionChangeTags(entityId, tags) { - return function(graph) { - var entity = graph.entity(entityId); - return graph.replace(entity.update({tags: tags})); - }; - } - - function osmNode() { - if (!(this instanceof osmNode)) { - return (new osmNode()).initialize(arguments); - } else if (arguments.length) { - this.initialize(arguments); - } - } - - osmEntity.node = osmNode; - - osmNode.prototype = Object.create(osmEntity.prototype); - - Object.assign(osmNode.prototype, { - type: 'node', - loc: [9999, 9999], - - extent: function() { - return new geoExtent(this.loc); - }, - - - geometry: function(graph) { - return graph.transient(this, 'geometry', function() { - return graph.isPoi(this) ? 'point' : 'vertex'; - }); - }, - - - move: function(loc) { - return this.update({loc: loc}); - }, - - - isDegenerate: function() { - return !( - Array.isArray(this.loc) && this.loc.length === 2 && - this.loc[0] >= -180 && this.loc[0] <= 180 && - this.loc[1] >= -90 && this.loc[1] <= 90 - ); - }, - - - // Inspect tags and geometry to determine which direction(s) this node/vertex points - directions: function(resolver, projection) { - var val; - var i; - - // which tag to use? - if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') { - // all-way stop tag on a highway intersection - val = 'all'; - } else { - // generic direction tag - val = (this.tags.direction || '').toLowerCase(); - - // better suffix-style direction tag - var re = /:direction$/i; - var keys = Object.keys(this.tags); - for (i = 0; i < keys.length; i++) { - if (re.test(keys[i])) { - val = this.tags[keys[i]].toLowerCase(); - break; - } - } - } - - if (val === '') return []; - - var cardinal = { - north: 0, n: 0, - northnortheast: 22, nne: 22, - northeast: 45, ne: 45, - eastnortheast: 67, ene: 67, - east: 90, e: 90, - eastsoutheast: 112, ese: 112, - southeast: 135, se: 135, - southsoutheast: 157, sse: 157, - south: 180, s: 180, - southsouthwest: 202, ssw: 202, - southwest: 225, sw: 225, - westsouthwest: 247, wsw: 247, - west: 270, w: 270, - westnorthwest: 292, wnw: 292, - northwest: 315, nw: 315, - northnorthwest: 337, nnw: 337 - }; - - - var values = val.split(';'); - var results = []; - - values.forEach(function(v) { - // swap cardinal for numeric directions - if (cardinal[v] !== undefined) { - v = cardinal[v]; - } - - // numeric direction - just add to results - if (v !== '' && !isNaN(+v)) { - results.push(+v); - return; - } - - // string direction - inspect parent ways - var lookBackward = - (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all'); - var lookForward = - (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all'); - - if (!lookForward && !lookBackward) return; - - var nodeIds = {}; - resolver.parentWays(this).forEach(function(parent) { - var nodes = parent.nodes; - for (i = 0; i < nodes.length; i++) { - if (nodes[i] === this.id) { // match current entity - if (lookForward && i > 0) { - nodeIds[nodes[i - 1]] = true; // look back to prev node - } - if (lookBackward && i < nodes.length - 1) { - nodeIds[nodes[i + 1]] = true; // look ahead to next node - } - } - } - }, this); - - Object.keys(nodeIds).forEach(function(nodeId) { - // +90 because geoAngle returns angle from X axis, not Y (north) - results.push( - (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90 - ); - }, this); - - }, this); - - return utilArrayUniq(results); - }, - - - isEndpoint: function(resolver) { - return resolver.transient(this, 'isEndpoint', function() { - var id = this.id; - return resolver.parentWays(this).filter(function(parent) { - return !parent.isClosed() && !!parent.affix(id); - }).length > 0; - }); - }, - - - isConnected: function(resolver) { - return resolver.transient(this, 'isConnected', function() { - var parents = resolver.parentWays(this); - - if (parents.length > 1) { - // vertex is connected to multiple parent ways - for (var i in parents) { - if (parents[i].geometry(resolver) === 'line' && - parents[i].hasInterestingTags()) return true; - } - } else if (parents.length === 1) { - var way = parents[0]; - var nodes = way.nodes.slice(); - if (way.isClosed()) { nodes.pop(); } // ignore connecting node if closed - - // return true if vertex appears multiple times (way is self intersecting) - return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id); - } - - return false; - }); - }, - - - parentIntersectionWays: function(resolver) { - return resolver.transient(this, 'parentIntersectionWays', function() { - return resolver.parentWays(this).filter(function(parent) { - return (parent.tags.highway || - parent.tags.waterway || - parent.tags.railway || - parent.tags.aeroway) && - parent.geometry(resolver) === 'line'; - }); - }); - }, - - - isIntersection: function(resolver) { - return this.parentIntersectionWays(resolver).length > 1; - }, - - - isHighwayIntersection: function(resolver) { - return resolver.transient(this, 'isHighwayIntersection', function() { - return resolver.parentWays(this).filter(function(parent) { - return parent.tags.highway && parent.geometry(resolver) === 'line'; - }).length > 1; - }); - }, - - - isOnAddressLine: function(resolver) { - return resolver.transient(this, 'isOnAddressLine', function() { - return resolver.parentWays(this).filter(function(parent) { - return parent.tags.hasOwnProperty('addr:interpolation') && - parent.geometry(resolver) === 'line'; - }).length > 0; - }); - }, - - - asJXON: function(changeset_id) { - var r = { - node: { - '@id': this.osmId(), - '@lon': this.loc[0], - '@lat': this.loc[1], - '@version': (this.version || 0), - tag: Object.keys(this.tags).map(function(k) { - return { keyAttributes: { k: k, v: this.tags[k] } }; - }, this) - } - }; - if (changeset_id) r.node['@changeset'] = changeset_id; - return r; - }, - - - asGeoJSON: function() { - return { - type: 'Point', - coordinates: this.loc - }; - } - }); - - function actionCircularize(wayId, projection, maxAngle) { - maxAngle = (maxAngle || 20) * Math.PI / 180; - - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var way = graph.entity(wayId); - var origNodes = {}; - - graph.childNodes(way).forEach(function(node) { - if (!origNodes[node.id]) origNodes[node.id] = node; - }); - - if (!way.isConvex(graph)) { - graph = action.makeConvex(graph); - } - - var nodes = utilArrayUniq(graph.childNodes(way)); - var keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }); - var points = nodes.map(function(n) { return projection(n.loc); }); - var keyPoints = keyNodes.map(function(n) { return projection(n.loc); }); - var centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points); - var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); }); - var sign = d3_polygonArea(points) > 0 ? 1 : -1; - var ids, i, j, k; - - // we need atleast two key nodes for the algorithm to work - if (!keyNodes.length) { - keyNodes = [nodes[0]]; - keyPoints = [points[0]]; - } - - if (keyNodes.length === 1) { - var index = nodes.indexOf(keyNodes[0]); - var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length); - - keyNodes.push(nodes[oppositeIndex]); - keyPoints.push(points[oppositeIndex]); - } - - // key points and nodes are those connected to the ways, - // they are projected onto the circle, inbetween nodes are moved - // to constant intervals between key nodes, extra inbetween nodes are - // added if necessary. - for (i = 0; i < keyPoints.length; i++) { - var nextKeyNodeIndex = (i + 1) % keyNodes.length; - var startNode = keyNodes[i]; - var endNode = keyNodes[nextKeyNodeIndex]; - var startNodeIndex = nodes.indexOf(startNode); - var endNodeIndex = nodes.indexOf(endNode); - var numberNewPoints = -1; - var indexRange = endNodeIndex - startNodeIndex; - var nearNodes = {}; - var inBetweenNodes = []; - var startAngle, endAngle, totalAngle, eachAngle; - var angle, loc, node, origNode; - - if (indexRange < 0) { - indexRange += nodes.length; - } - - // position this key node - var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4; - keyPoints[i] = [ - centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, - centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius - ]; - loc = projection.invert(keyPoints[i]); - node = keyNodes[i]; - origNode = origNodes[node.id]; - node = node.move(geoVecInterp(origNode.loc, loc, t)); - graph = graph.replace(node); - - // figure out the between delta angle we want to match to - startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]); - endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]); - totalAngle = endAngle - startAngle; - - // detects looping around -pi/pi - if (totalAngle * sign > 0) { - totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle)); - } - - do { - numberNewPoints++; - eachAngle = totalAngle / (indexRange + numberNewPoints); - } while (Math.abs(eachAngle) > maxAngle); - - - // move existing nodes - for (j = 1; j < indexRange; j++) { - angle = startAngle + j * eachAngle; - loc = projection.invert([ - centroid[0] + Math.cos(angle) * radius, - centroid[1] + Math.sin(angle) * radius - ]); - - node = nodes[(j + startNodeIndex) % nodes.length]; - origNode = origNodes[node.id]; - nearNodes[node.id] = angle; - - node = node.move(geoVecInterp(origNode.loc, loc, t)); - graph = graph.replace(node); - } - - // add new inbetween nodes if necessary - for (j = 0; j < numberNewPoints; j++) { - angle = startAngle + (indexRange + j) * eachAngle; - loc = projection.invert([ - centroid[0] + Math.cos(angle) * radius, - centroid[1] + Math.sin(angle) * radius - ]); - - // choose a nearnode to use as the original - var min = Infinity; - for (var nodeId in nearNodes) { - var nearAngle = nearNodes[nodeId]; - var dist = Math.abs(nearAngle - angle); - if (dist < min) { - dist = min; - origNode = origNodes[nodeId]; - } - } - - node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) }); - graph = graph.replace(node); - - nodes.splice(endNodeIndex + j, 0, node); - inBetweenNodes.push(node.id); - } - - // Check for other ways that share these keyNodes.. - // If keyNodes are adjacent in both ways, - // we can add inBetween nodes to that shared way too.. - if (indexRange === 1 && inBetweenNodes.length) { - var startIndex1 = way.nodes.lastIndexOf(startNode.id); - var endIndex1 = way.nodes.lastIndexOf(endNode.id); - var wayDirection1 = (endIndex1 - startIndex1); - if (wayDirection1 < -1) { wayDirection1 = 1; } - - var parentWays = graph.parentWays(keyNodes[i]); - for (j = 0; j < parentWays.length; j++) { - var sharedWay = parentWays[j]; - if (sharedWay === way) continue; - - if (sharedWay.areAdjacent(startNode.id, endNode.id)) { - var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id); - var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id); - var wayDirection2 = (endIndex2 - startIndex2); - var insertAt = endIndex2; - if (wayDirection2 < -1) { wayDirection2 = 1; } - - if (wayDirection1 !== wayDirection2) { - inBetweenNodes.reverse(); - insertAt = startIndex2; - } - for (k = 0; k < inBetweenNodes.length; k++) { - sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k); - } - graph = graph.replace(sharedWay); - } - } - } - - } - - // update the way to have all the new nodes - ids = nodes.map(function(n) { return n.id; }); - ids.push(ids[0]); - - way = way.update({nodes: ids}); - graph = graph.replace(way); - - return graph; - }; - - - action.makeConvex = function(graph) { - var way = graph.entity(wayId); - var nodes = utilArrayUniq(graph.childNodes(way)); - var points = nodes.map(function(n) { return projection(n.loc); }); - var sign = d3_polygonArea(points) > 0 ? 1 : -1; - var hull = d3_polygonHull(points); - var i, j; - - // D3 convex hulls go counterclockwise.. - if (sign === -1) { - nodes.reverse(); - points.reverse(); - } - - for (i = 0; i < hull.length - 1; i++) { - var startIndex = points.indexOf(hull[i]); - var endIndex = points.indexOf(hull[i+1]); - var indexRange = (endIndex - startIndex); - - if (indexRange < 0) { - indexRange += nodes.length; - } - - // move interior nodes to the surface of the convex hull.. - for (j = 1; j < indexRange; j++) { - var point = geoVecInterp(hull[i], hull[i+1], j / indexRange); - var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point)); - graph = graph.replace(node); - } - } - return graph; - }; - - - action.disabled = function(graph) { - if (!graph.entity(wayId).isClosed()) { - return 'not_closed'; - } - - //disable when already circular - var way = graph.entity(wayId); - var nodes = utilArrayUniq(graph.childNodes(way)); - var points = nodes.map(function(n) { return projection(n.loc); }); - var hull = d3_polygonHull(points); - var epsilonAngle = Math.PI / 180; - if (hull.length !== points.length || hull.length < 3){ - return false; - } - var centroid = d3_polygonCentroid(points); - var radius = geoVecLengthSquare(centroid, points[0]); - - // compare distances between centroid and points - for (var i = 0; i 0.05*radius) { - return false; - } - } - - //check if central angles are smaller than maxAngle - for (i = 0; i Math.PI){ - angle = (2*Math.PI - angle); - } - - if (angle > maxAngle + epsilonAngle) { - return false; - } - } - return 'already_circular'; - }; - - - action.transitionable = true; - - - return action; - } - - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as - function actionDeleteWay(wayID) { - - function canDeleteNode(node, graph) { - // don't delete nodes still attached to ways or relations - if (graph.parentWays(node).length || - graph.parentRelations(node).length) return false; - - var geometries = osmNodeGeometriesForTags(node.tags); - // don't delete if this node can be a standalone point - if (geometries.point) return false; - // delete if this node only be a vertex - if (geometries.vertex) return true; - - // iD doesn't know if this should be a point or vertex, - // so only delete if there are no interesting tags - return !node.hasInterestingTags(); - } - - - var action = function(graph) { - var way = graph.entity(wayID); - - graph.parentRelations(way).forEach(function(parent) { - parent = parent.removeMembersWithID(wayID); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteRelation(parent.id)(graph); - } - }); - - (new Set(way.nodes)).forEach(function(nodeID) { - graph = graph.replace(way.removeNode(nodeID)); - - var node = graph.entity(nodeID); - if (canDeleteNode(node, graph)) { - graph = graph.remove(node); - } - }); - - return graph.remove(way); - }; - - - return action; - } - - function actionDeleteMultiple(ids) { - var actions = { - way: actionDeleteWay, - node: actionDeleteNode, - relation: actionDeleteRelation - }; - - - var action = function(graph) { - ids.forEach(function(id) { - if (graph.hasEntity(id)) { // It may have been deleted aready. - graph = actions[graph.entity(id).type](id)(graph); - } - }); - - return graph; - }; - - - return action; - } - - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as - function actionDeleteRelation(relationID, allowUntaggedMembers) { - - function canDeleteEntity(entity, graph) { - return !graph.parentWays(entity).length && - !graph.parentRelations(entity).length && - (!entity.hasInterestingTags() && !allowUntaggedMembers); - } - - - var action = function(graph) { - var relation = graph.entity(relationID); - - graph.parentRelations(relation) - .forEach(function(parent) { - parent = parent.removeMembersWithID(relationID); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteRelation(parent.id)(graph); - } - }); - - var memberIDs = utilArrayUniq(relation.members.map(function(m) { return m.id; })); - memberIDs.forEach(function(memberID) { - graph = graph.replace(relation.removeMembersWithID(memberID)); - - var entity = graph.entity(memberID); - if (canDeleteEntity(entity, graph)) { - graph = actionDeleteMultiple([memberID])(graph); - } - }); - - return graph.remove(relation); - }; - - - return action; - } - - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as - function actionDeleteNode(nodeId) { - var action = function(graph) { - var node = graph.entity(nodeId); - - graph.parentWays(node) - .forEach(function(parent) { - parent = parent.removeNode(nodeId); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteWay(parent.id)(graph); - } - }); - - graph.parentRelations(node) - .forEach(function(parent) { - parent = parent.removeMembersWithID(nodeId); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteRelation(parent.id)(graph); - } - }); - - return graph.remove(node); - }; - - - return action; - } - - // Connect the ways at the given nodes. - // - // First choose a node to be the survivor, with preference given - // to an existing (not new) node. - // - // Tags and relation memberships of of non-surviving nodes are merged - // to the survivor. - // - // This is the inverse of `iD.actionDisconnect`. - // - // Reference: - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as - // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java - // - function actionConnect(nodeIDs) { - var action = function(graph) { - var survivor; - var node; - var parents; - var i, j; - - // Choose a survivor node, prefer an existing (not new) node - #4974 - for (i = 0; i < nodeIDs.length; i++) { - survivor = graph.entity(nodeIDs[i]); - if (survivor.version) break; // found one - } - - // Replace all non-surviving nodes with the survivor and merge tags. - for (i = 0; i < nodeIDs.length; i++) { - node = graph.entity(nodeIDs[i]); - if (node.id === survivor.id) continue; - - parents = graph.parentWays(node); - for (j = 0; j < parents.length; j++) { - graph = graph.replace(parents[j].replaceNode(node.id, survivor.id)); - } - - parents = graph.parentRelations(node); - for (j = 0; j < parents.length; j++) { - graph = graph.replace(parents[j].replaceMember(node, survivor)); - } - - survivor = survivor.mergeTags(node.tags); - graph = actionDeleteNode(node.id)(graph); - } - - graph = graph.replace(survivor); - - // find and delete any degenerate ways created by connecting adjacent vertices - parents = graph.parentWays(survivor); - for (i = 0; i < parents.length; i++) { - if (parents[i].isDegenerate()) { - graph = actionDeleteWay(parents[i].id)(graph); - } - } - - return graph; - }; - - - action.disabled = function(graph) { - var seen = {}; - var restrictionIDs = []; - var survivor; - var node, way; - var relations, relation, role; - var i, j, k; - - // Choose a survivor node, prefer an existing (not new) node - #4974 - for (i = 0; i < nodeIDs.length; i++) { - survivor = graph.entity(nodeIDs[i]); - if (survivor.version) break; // found one - } - - // 1. disable if the nodes being connected have conflicting relation roles - for (i = 0; i < nodeIDs.length; i++) { - node = graph.entity(nodeIDs[i]); - relations = graph.parentRelations(node); - - for (j = 0; j < relations.length; j++) { - relation = relations[j]; - role = relation.memberById(node.id).role || ''; - - // if this node is a via node in a restriction, remember for later - if (relation.hasFromViaTo()) { - restrictionIDs.push(relation.id); - } - - if (seen[relation.id] !== undefined && seen[relation.id] !== role) { - return 'relation'; - } else { - seen[relation.id] = role; - } - } - } - - // gather restrictions for parent ways - for (i = 0; i < nodeIDs.length; i++) { - node = graph.entity(nodeIDs[i]); - - var parents = graph.parentWays(node); - for (j = 0; j < parents.length; j++) { - var parent = parents[j]; - relations = graph.parentRelations(parent); - - for (k = 0; k < relations.length; k++) { - relation = relations[k]; - if (relation.hasFromViaTo()) { - restrictionIDs.push(relation.id); - } - } - } - } - - - // test restrictions - restrictionIDs = utilArrayUniq(restrictionIDs); - for (i = 0; i < restrictionIDs.length; i++) { - relation = graph.entity(restrictionIDs[i]); - if (!relation.isComplete(graph)) continue; - - var memberWays = relation.members - .filter(function(m) { return m.type === 'way'; }) - .map(function(m) { return graph.entity(m.id); }); - - memberWays = utilArrayUniq(memberWays); - var f = relation.memberByRole('from'); - var t = relation.memberByRole('to'); - var isUturn = (f.id === t.id); - - // 2a. disable if connection would damage a restriction - // (a key node is a node at the junction of ways) - var nodes = { from: [], via: [], to: [], keyfrom: [], keyto: [] }; - for (j = 0; j < relation.members.length; j++) { - collectNodes(relation.members[j], nodes); - } - - nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates)); - nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates)); - - var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto); - nodes.from = nodes.from.filter(filter); - nodes.via = nodes.via.filter(filter); - nodes.to = nodes.to.filter(filter); - - var connectFrom = false; - var connectVia = false; - var connectTo = false; - var connectKeyFrom = false; - var connectKeyTo = false; - - for (j = 0; j < nodeIDs.length; j++) { - var n = nodeIDs[j]; - if (nodes.from.indexOf(n) !== -1) { connectFrom = true; } - if (nodes.via.indexOf(n) !== -1) { connectVia = true; } - if (nodes.to.indexOf(n) !== -1) { connectTo = true; } - if (nodes.keyfrom.indexOf(n) !== -1) { connectKeyFrom = true; } - if (nodes.keyto.indexOf(n) !== -1) { connectKeyTo = true; } - } - if (connectFrom && connectTo && !isUturn) { return 'restriction'; } - if (connectFrom && connectVia) { return 'restriction'; } - if (connectTo && connectVia) { return 'restriction'; } - - // connecting to a key node - - // if both nodes are on a member way (i.e. part of the turn restriction), - // the connecting node must be adjacent to the key node. - if (connectKeyFrom || connectKeyTo) { - if (nodeIDs.length !== 2) { return 'restriction'; } - - var n0 = null; - var n1 = null; - for (j = 0; j < memberWays.length; j++) { - way = memberWays[j]; - if (way.contains(nodeIDs[0])) { n0 = nodeIDs[0]; } - if (way.contains(nodeIDs[1])) { n1 = nodeIDs[1]; } - } - - if (n0 && n1) { // both nodes are part of the restriction - var ok = false; - for (j = 0; j < memberWays.length; j++) { - way = memberWays[j]; - if (way.areAdjacent(n0, n1)) { - ok = true; - break; - } - } - if (!ok) { - return 'restriction'; - } - } - } - - // 2b. disable if nodes being connected will destroy a member way in a restriction - // (to test, make a copy and try actually connecting the nodes) - for (j = 0; j < memberWays.length; j++) { - way = memberWays[j].update({}); // make copy - for (k = 0; k < nodeIDs.length; k++) { - if (nodeIDs[k] === survivor.id) continue; - - if (way.areAdjacent(nodeIDs[k], survivor.id)) { - way = way.removeNode(nodeIDs[k]); - } else { - way = way.replaceNode(nodeIDs[k], survivor.id); - } - } - if (way.isDegenerate()) { - return 'restriction'; - } - } - } - - return false; - - - // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction - function hasDuplicates(n, i, arr) { - return arr.indexOf(n) !== arr.lastIndexOf(n); - } - - function keyNodeFilter(froms, tos) { - return function(n) { - return froms.indexOf(n) === -1 && tos.indexOf(n) === -1; - }; - } - - function collectNodes(member, collection) { - var entity = graph.hasEntity(member.id); - if (!entity) return; - - var role = member.role || ''; - if (!collection[role]) { - collection[role] = []; - } - - if (member.type === 'node') { - collection[role].push(member.id); - if (role === 'via') { - collection.keyfrom.push(member.id); - collection.keyto.push(member.id); - } - - } else if (member.type === 'way') { - collection[role].push.apply(collection[role], entity.nodes); - if (role === 'from' || role === 'via') { - collection.keyfrom.push(entity.first()); - collection.keyfrom.push(entity.last()); - } - if (role === 'to' || role === 'via') { - collection.keyto.push(entity.first()); - collection.keyto.push(entity.last()); - } - } - } - }; - - - return action; - } - - function actionCopyEntities(ids, fromGraph) { - var _copies = {}; - - - var action = function(graph) { - ids.forEach(function(id) { - fromGraph.entity(id).copy(fromGraph, _copies); - }); - - for (var id in _copies) { - graph = graph.replace(_copies[id]); - } - - return graph; - }; - - - action.copies = function() { - return _copies; - }; - - - return action; - } - - function actionDeleteMember(relationId, memberIndex) { - return function(graph) { - var relation = graph.entity(relationId) - .removeMember(memberIndex); - - graph = graph.replace(relation); - - if (relation.isDegenerate()) - graph = actionDeleteRelation(relation.id)(graph); - - return graph; - }; - } - - function actionDiscardTags(difference, discardTags) { - discardTags = discardTags || {}; - - return (graph) => { - difference.modified().forEach(checkTags); - difference.created().forEach(checkTags); - return graph; - - function checkTags(entity) { - const keys = Object.keys(entity.tags); - let didDiscard = false; - let tags = {}; - - for (let i = 0; i < keys.length; i++) { - const k = keys[i]; - if (discardTags[k] || !entity.tags[k]) { - didDiscard = true; - } else { - tags[k] = entity.tags[k]; - } - } - if (didDiscard) { - graph = graph.replace(entity.update({ tags: tags })); - } - } - - }; - } - - // Disconect the ways at the given node. - // - // Optionally, disconnect only the given ways. - // - // For testing convenience, accepts an ID to assign to the (first) new node. - // Normally, this will be undefined and the way will automatically - // be assigned a new ID. - // - // This is the inverse of `iD.actionConnect`. - // - // Reference: - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as - // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java - // - function actionDisconnect(nodeId, newNodeId) { - var wayIds; - - - var action = function(graph) { - var node = graph.entity(nodeId); - var connections = action.connections(graph); - - connections.forEach(function(connection) { - var way = graph.entity(connection.wayID); - var newNode = osmNode({id: newNodeId, loc: node.loc, tags: node.tags}); - - graph = graph.replace(newNode); - if (connection.index === 0 && way.isArea()) { - // replace shared node with shared node.. - graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id)); - } else if (way.isClosed() && connection.index === way.nodes.length - 1) { - // replace closing node with new new node.. - graph = graph.replace(way.unclose().addNode(newNode.id)); - } else { - // replace shared node with multiple new nodes.. - graph = graph.replace(way.updateNode(newNode.id, connection.index)); - } - }); - - return graph; - }; - - - action.connections = function(graph) { - var candidates = []; - var keeping = false; - var parentWays = graph.parentWays(graph.entity(nodeId)); - var way, waynode; - for (var i = 0; i < parentWays.length; i++) { - way = parentWays[i]; - if (wayIds && wayIds.indexOf(way.id) === -1) { - keeping = true; - continue; - } - if (way.isArea() && (way.nodes[0] === nodeId)) { - candidates.push({ wayID: way.id, index: 0 }); - } else { - for (var j = 0; j < way.nodes.length; j++) { - waynode = way.nodes[j]; - if (waynode === nodeId) { - if (way.isClosed() && - parentWays.length > 1 && - wayIds && - wayIds.indexOf(way.id) !== -1 && - j === way.nodes.length - 1) { - continue; - } - candidates.push({ wayID: way.id, index: j }); - } - } - } - } - - return keeping ? candidates : candidates.slice(1); - }; - - - action.disabled = function(graph) { - var connections = action.connections(graph); - if (connections.length === 0) - return 'not_connected'; - - var parentWays = graph.parentWays(graph.entity(nodeId)); - var seenRelationIds = {}; - var sharedRelation; - - parentWays.forEach(function(way) { - var relations = graph.parentRelations(way); - relations.forEach(function(relation) { - if (relation.id in seenRelationIds) { - if (wayIds) { - if (wayIds.indexOf(way.id) !== -1 || - wayIds.indexOf(seenRelationIds[relation.id]) !== -1) { - sharedRelation = relation; - } - } else { - sharedRelation = relation; - } - } else { - seenRelationIds[relation.id] = way.id; - } - }); - }); - - if (sharedRelation) - return 'relation'; - }; - - - action.limitWays = function(val) { - if (!arguments.length) return wayIds; - wayIds = val; - return action; - }; - - - return action; - } - - function actionExtract(entityID) { - - var extractedNodeID; - - var action = function(graph) { - var entity = graph.entity(entityID); - - if (entity.type === 'node') { - return extractFromNode(entity, graph); - } - - return extractFromWayOrRelation(entity, graph); - }; - - function extractFromNode(node, graph) { - - extractedNodeID = node.id; - - // Create a new node to replace the one we will detach - var replacement = osmNode({ loc: node.loc }); - graph = graph.replace(replacement); - - // Process each way in turn, updating the graph as we go - graph = graph.parentWays(node) - .reduce(function(accGraph, parentWay) { - return accGraph.replace(parentWay.replaceNode(entityID, replacement.id)); - }, graph); - - // Process any relations too - return graph.parentRelations(node) - .reduce(function(accGraph, parentRel) { - return accGraph.replace(parentRel.replaceMember(node, replacement)); - }, graph); - } - - function extractFromWayOrRelation(entity, graph) { - - var fromGeometry = entity.geometry(graph); - - var keysToCopyAndRetain = ['source', 'wheelchair']; - var keysToRetain = ['area']; - var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; - - var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph)); - if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) { - extractedLoc = entity.extent(graph).center(); - } - - var isBuilding = entity.tags.building && entity.tags.building !== 'no'; - - var entityTags = Object.assign({}, entity.tags); // shallow copy - var pointTags = {}; - for (var key in entityTags) { - - if (entity.type === 'relation' && - key === 'type') { - continue; - } - - if (keysToRetain.indexOf(key) !== -1) { - continue; - } - - if (isBuilding) { - // don't transfer building-related tags - if (buildingKeysToRetain.indexOf(key) !== -1 || - key.match(/^building:.{1,}/) || - key.match(/^roof:.{1,}/)) continue; - } - - // copy the tag from the entity to the point - pointTags[key] = entityTags[key]; - - // leave addresses and some other tags so they're on both features - if (keysToCopyAndRetain.indexOf(key) !== -1 || - key.match(/^addr:.{1,}/)) { - continue; - } - - // remove the tag from the entity - delete entityTags[key]; - } - - if (!isBuilding && fromGeometry === 'area') { - // ensure that areas keep area geometry - entityTags.area = 'yes'; - } - - var replacement = osmNode({ loc: extractedLoc, tags: pointTags }); - graph = graph.replace(replacement); - - extractedNodeID = replacement.id; - - return graph.replace(entity.update({tags: entityTags})); - } - - action.getExtractedNodeID = function() { - return extractedNodeID; - }; - - return action; - } - - // Join ways at the end node they share. - // - // This is the inverse of `iD.actionSplit`. - // - // Reference: - // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as - // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java - // - function actionJoin(ids) { - - function groupEntitiesByGeometry(graph) { - var entities = ids.map(function(id) { return graph.entity(id); }); - return Object.assign( - { line: [] }, - utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); }) - ); - } - - - var action = function(graph) { - var ways = ids.map(graph.entity, graph); - var survivorID = ways[0].id; - - // if any of the ways are sided (e.g. coastline, cliff, kerb) - // sort them first so they establish the overall order - #6033 - ways.sort(function(a, b) { - var aSided = a.isSided(); - var bSided = b.isSided(); - return (aSided && !bSided) ? -1 - : (bSided && !aSided) ? 1 - : 0; - }); - - // Prefer to keep an existing way. - for (var i = 0; i < ways.length; i++) { - if (!ways[i].isNew()) { - survivorID = ways[i].id; - break; - } - } - - var sequences = osmJoinWays(ways, graph); - var joined = sequences[0]; - - // We might need to reverse some of these ways before joining them. #4688 - // `joined.actions` property will contain any actions we need to apply. - graph = sequences.actions.reduce(function(g, action) { return action(g); }, graph); - - var survivor = graph.entity(survivorID); - survivor = survivor.update({ nodes: joined.nodes.map(function(n) { return n.id; }) }); - graph = graph.replace(survivor); - - joined.forEach(function(way) { - if (way.id === survivorID) return; - - graph.parentRelations(way).forEach(function(parent) { - graph = graph.replace(parent.replaceMember(way, survivor)); - }); - - survivor = survivor.mergeTags(way.tags); - - graph = graph.replace(survivor); - graph = actionDeleteWay(way.id)(graph); - }); - - // Finds if the join created a single-member multipolygon, - // and if so turns it into a basic area instead - function checkForSimpleMultipolygon() { - if (!survivor.isClosed()) return; - - var multipolygons = graph.parentMultipolygons(survivor).filter(function(multipolygon) { - // find multipolygons where the survivor is the only member - return multipolygon.members.length === 1; - }); - - // skip if this is the single member of multiple multipolygons - if (multipolygons.length !== 1) return; - - var multipolygon = multipolygons[0]; - - for (var key in survivor.tags) { - if (multipolygon.tags[key] && - // don't collapse if tags cannot be cleanly merged - multipolygon.tags[key] !== survivor.tags[key]) return; - } - - survivor = survivor.mergeTags(multipolygon.tags); - graph = graph.replace(survivor); - graph = actionDeleteRelation(multipolygon.id, true /* allow untagged members */)(graph); - - var tags = Object.assign({}, survivor.tags); - if (survivor.geometry(graph) !== 'area') { - // ensure the feature persists as an area - tags.area = 'yes'; - } - delete tags.type; // remove type=multipolygon - survivor = survivor.update({ tags: tags }); - graph = graph.replace(survivor); - } - checkForSimpleMultipolygon(); - - return graph; - }; - - // Returns the number of nodes the resultant way is expected to have - action.resultingWayNodesLength = function(graph) { - return ids.reduce(function(count, id) { - return count + graph.entity(id).nodes.length; - }, 0) - ids.length - 1; - }; - - - action.disabled = function(graph) { - var geometries = groupEntitiesByGeometry(graph); - if (ids.length < 2 || ids.length !== geometries.line.length) { - return 'not_eligible'; - } - - var joined = osmJoinWays(ids.map(graph.entity, graph), graph); - if (joined.length > 1) { - return 'not_adjacent'; - } - - // Loop through all combinations of path-pairs - // to check potential intersections between all pairs - for (var i = 0; i < ids.length - 1; i++) { - for (var j = i + 1; j < ids.length; j++) { - var path1 = graph.childNodes(graph.entity(ids[i])) - .map(function(e) { return e.loc; }); - var path2 = graph.childNodes(graph.entity(ids[j])) - .map(function(e) { return e.loc; }); - var intersections = geoPathIntersections(path1, path2); - - // Check if intersections are just nodes lying on top of - // each other/the line, as opposed to crossing it - var common = utilArrayIntersection( - joined[0].nodes.map(function(n) { return n.loc.toString(); }), - intersections.map(function(n) { return n.toString(); }) - ); - if (common.length !== intersections.length) { - return 'paths_intersect'; - } - } - } - - var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1); - var relation; - var tags = {}; - var conflicting = false; - - joined[0].forEach(function(way) { - var parents = graph.parentRelations(way); - parents.forEach(function(parent) { - if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) { - relation = parent; - } - }); - - for (var k in way.tags) { - if (!(k in tags)) { - tags[k] = way.tags[k]; - } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) { - conflicting = true; - } - } - }); - - if (relation) { - return 'restriction'; - } - - if (conflicting) { - return 'conflicting_tags'; - } - }; - - - return action; - } - - function actionMerge(ids) { - - function groupEntitiesByGeometry(graph) { - var entities = ids.map(function(id) { return graph.entity(id); }); - return Object.assign( - { point: [], area: [], line: [], relation: [] }, - utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); }) - ); - } - - - var action = function(graph) { - var geometries = groupEntitiesByGeometry(graph); - var target = geometries.area[0] || geometries.line[0]; - var points = geometries.point; - - points.forEach(function(point) { - target = target.mergeTags(point.tags); - graph = graph.replace(target); - - graph.parentRelations(point).forEach(function(parent) { - graph = graph.replace(parent.replaceMember(point, target)); - }); - - var nodes = utilArrayUniq(graph.childNodes(target)); - var removeNode = point; - - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - if (graph.parentWays(node).length > 1 || - graph.parentRelations(node).length || - node.hasInterestingTags()) { - continue; - } - - // Found an uninteresting child node on the target way. - // Move orig point into its place to preserve point's history. #3683 - graph = graph.replace(point.update({ tags: {}, loc: node.loc })); - target = target.replaceNode(node.id, point.id); - graph = graph.replace(target); - removeNode = node; - break; - } - - graph = graph.remove(removeNode); - }); - - if (target.tags.area === 'yes') { - var tags = Object.assign({}, target.tags); // shallow copy - delete tags.area; - if (osmTagSuggestingArea(tags)) { - // remove the `area` tag if area geometry is now implied - #3851 - target = target.update({ tags: tags }); - graph = graph.replace(target); - } - } - - return graph; - }; - - - action.disabled = function(graph) { - var geometries = groupEntitiesByGeometry(graph); - if (geometries.point.length === 0 || - (geometries.area.length + geometries.line.length) !== 1 || - geometries.relation.length !== 0) { - return 'not_eligible'; - } - }; - - - return action; - } - - // `actionMergeNodes` is just a combination of: - // - // 1. move all the nodes to a common location - // 2. `actionConnect` them - - function actionMergeNodes(nodeIDs, loc) { - - // If there is a single "interesting" node, use that as the location. - // Otherwise return the average location of all the nodes. - function chooseLoc(graph) { - if (!nodeIDs.length) return null; - var sum = [0,0]; - var interestingCount = 0; - var interestingLoc; - - for (var i = 0; i < nodeIDs.length; i++) { - var node = graph.entity(nodeIDs[i]); - if (node.hasInterestingTags()) { - interestingLoc = (++interestingCount === 1) ? node.loc : null; - } - sum = geoVecAdd(sum, node.loc); - } - - return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length); - } - - - var action = function(graph) { - if (nodeIDs.length < 2) return graph; - var toLoc = loc; - if (!toLoc) { - toLoc = chooseLoc(graph); - } - - for (var i = 0; i < nodeIDs.length; i++) { - var node = graph.entity(nodeIDs[i]); - if (node.loc !== toLoc) { - graph = graph.replace(node.move(toLoc)); - } - } - - return actionConnect(nodeIDs)(graph); - }; - - - action.disabled = function(graph) { - if (nodeIDs.length < 2) return 'not_eligible'; - - for (var i = 0; i < nodeIDs.length; i++) { - var entity = graph.entity(nodeIDs[i]); - if (entity.type !== 'node') return 'not_eligible'; - } - - return actionConnect(nodeIDs).disabled(graph); - }; - - return action; - } - - function osmChangeset() { - if (!(this instanceof osmChangeset)) { - return (new osmChangeset()).initialize(arguments); - } else if (arguments.length) { - this.initialize(arguments); - } - } - - - osmEntity.changeset = osmChangeset; - - osmChangeset.prototype = Object.create(osmEntity.prototype); - - Object.assign(osmChangeset.prototype, { - - type: 'changeset', - - - extent: function() { - return new geoExtent(); - }, - - - geometry: function() { - return 'changeset'; - }, - - - asJXON: function() { - return { - osm: { - changeset: { - tag: Object.keys(this.tags).map(function(k) { - return { '@k': k, '@v': this.tags[k] }; - }, this), - '@version': 0.6, - '@generator': 'iD' - } - } - }; - }, - - - // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange) - // XML. Returns a string. - osmChangeJXON: function(changes) { - var changeset_id = this.id; - - function nest(x, order) { - var groups = {}; - for (var i = 0; i < x.length; i++) { - var tagName = Object.keys(x[i])[0]; - if (!groups[tagName]) groups[tagName] = []; - groups[tagName].push(x[i][tagName]); - } - var ordered = {}; - order.forEach(function(o) { - if (groups[o]) ordered[o] = groups[o]; - }); - return ordered; - } - - - // sort relations in a changeset by dependencies - function sort(changes) { - - // find a referenced relation in the current changeset - function resolve(item) { - return relations.find(function(relation) { - return item.keyAttributes.type === 'relation' - && item.keyAttributes.ref === relation['@id']; - }); - } - - // a new item is an item that has not been already processed - function isNew(item) { - return !sorted[ item['@id'] ] && !processing.find(function(proc) { - return proc['@id'] === item['@id']; - }); - } - - var processing = []; - var sorted = {}; - var relations = changes.relation; - - if (!relations) return changes; - - for (var i = 0; i < relations.length; i++) { - var relation = relations[i]; - - // skip relation if already sorted - if (!sorted[relation['@id']]) { - processing.push(relation); - } - - while (processing.length > 0) { - var next = processing[0], - deps = next.member.map(resolve).filter(Boolean).filter(isNew); - if (deps.length === 0) { - sorted[next['@id']] = next; - processing.shift(); - } else { - processing = deps.concat(processing); - } - } - } - - changes.relation = Object.values(sorted); - return changes; - } - - function rep(entity) { - return entity.asJXON(changeset_id); - } - - return { - osmChange: { - '@version': 0.6, - '@generator': 'iD', - 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])), - 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']), - 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true }) - } - }; - }, - - - asGeoJSON: function() { - return {}; - } - - }); - - function osmNote() { - if (!(this instanceof osmNote)) { - return (new osmNote()).initialize(arguments); - } else if (arguments.length) { - this.initialize(arguments); - } - } - - - osmNote.id = function() { - return osmNote.id.next--; - }; - - - osmNote.id.next = -1; - - - Object.assign(osmNote.prototype, { - - type: 'note', - - initialize: function(sources) { - for (var i = 0; i < sources.length; ++i) { - var source = sources[i]; - for (var prop in source) { - if (Object.prototype.hasOwnProperty.call(source, prop)) { - if (source[prop] === undefined) { - delete this[prop]; - } else { - this[prop] = source[prop]; - } - } - } - } - - if (!this.id) { - this.id = osmNote.id() + ''; // as string - } - - return this; - }, - - extent: function() { - return new geoExtent(this.loc); - }, - - update: function(attrs) { - return osmNote(this, attrs); // {v: 1 + (this.v || 0)} - }, - - isNew: function() { - return this.id < 0; - }, - - move: function(loc) { - return this.update({ loc: loc }); - } - - }); - - function osmRelation() { - if (!(this instanceof osmRelation)) { - return (new osmRelation()).initialize(arguments); - } else if (arguments.length) { - this.initialize(arguments); - } - } - - - osmEntity.relation = osmRelation; - - osmRelation.prototype = Object.create(osmEntity.prototype); - - - osmRelation.creationOrder = function(a, b) { - var aId = parseInt(osmEntity.id.toOSM(a.id), 10); - var bId = parseInt(osmEntity.id.toOSM(b.id), 10); - - if (aId < 0 || bId < 0) return aId - bId; - return bId - aId; - }; - - - Object.assign(osmRelation.prototype, { - type: 'relation', - members: [], - - - copy: function(resolver, copies) { - if (copies[this.id]) return copies[this.id]; - - var copy = osmEntity.prototype.copy.call(this, resolver, copies); - - var members = this.members.map(function(member) { - return Object.assign({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id }); - }); - - copy = copy.update({members: members}); - copies[this.id] = copy; - - return copy; - }, - - - extent: function(resolver, memo) { - return resolver.transient(this, 'extent', function() { - if (memo && memo[this.id]) return geoExtent(); - memo = memo || {}; - memo[this.id] = true; - - var extent = geoExtent(); - for (var i = 0; i < this.members.length; i++) { - var member = resolver.hasEntity(this.members[i].id); - if (member) { - extent._extend(member.extent(resolver, memo)); - } - } - return extent; - }); - }, - - - geometry: function(graph) { - return graph.transient(this, 'geometry', function() { - return this.isMultipolygon() ? 'area' : 'relation'; - }); - }, - - - isDegenerate: function() { - return this.members.length === 0; - }, - - - // Return an array of members, each extended with an 'index' property whose value - // is the member index. - indexedMembers: function() { - var result = new Array(this.members.length); - for (var i = 0; i < this.members.length; i++) { - result[i] = Object.assign({}, this.members[i], {index: i}); - } - return result; - }, - - - // Return the first member with the given role. A copy of the member object - // is returned, extended with an 'index' property whose value is the member index. - memberByRole: function(role) { - for (var i = 0; i < this.members.length; i++) { - if (this.members[i].role === role) { - return Object.assign({}, this.members[i], {index: i}); - } - } - }, - - // Same as memberByRole, but returns all members with the given role - membersByRole: function(role) { - var result = []; - for (var i = 0; i < this.members.length; i++) { - if (this.members[i].role === role) { - result.push(Object.assign({}, this.members[i], {index: i})); - } - } - return result; - }, - - // Return the first member with the given id. A copy of the member object - // is returned, extended with an 'index' property whose value is the member index. - memberById: function(id) { - for (var i = 0; i < this.members.length; i++) { - if (this.members[i].id === id) { - return Object.assign({}, this.members[i], {index: i}); - } - } - }, - - - // Return the first member with the given id and role. A copy of the member object - // is returned, extended with an 'index' property whose value is the member index. - memberByIdAndRole: function(id, role) { - for (var i = 0; i < this.members.length; i++) { - if (this.members[i].id === id && this.members[i].role === role) { - return Object.assign({}, this.members[i], {index: i}); - } - } - }, - - - addMember: function(member, index) { - var members = this.members.slice(); - members.splice(index === undefined ? members.length : index, 0, member); - return this.update({members: members}); - }, - - - updateMember: function(member, index) { - var members = this.members.slice(); - members.splice(index, 1, Object.assign({}, members[index], member)); - return this.update({members: members}); - }, - - - removeMember: function(index) { - var members = this.members.slice(); - members.splice(index, 1); - return this.update({members: members}); - }, - - - removeMembersWithID: function(id) { - var members = this.members.filter(function(m) { return m.id !== id; }); - return this.update({members: members}); - }, - - moveMember: function(fromIndex, toIndex) { - var members = this.members.slice(); - members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]); - return this.update({members: members}); - }, - - - // Wherever a member appears with id `needle.id`, replace it with a member - // with id `replacement.id`, type `replacement.type`, and the original role, - // By default, adding a duplicate member (by id and role) is prevented. - // Return an updated relation. - replaceMember: function(needle, replacement, keepDuplicates) { - if (!this.memberById(needle.id)) return this; - - var members = []; - - for (var i = 0; i < this.members.length; i++) { - var member = this.members[i]; - if (member.id !== needle.id) { - members.push(member); - } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) { - members.push({ id: replacement.id, type: replacement.type, role: member.role }); - } - } - - return this.update({ members: members }); - }, - - - asJXON: function(changeset_id) { - var r = { - relation: { - '@id': this.osmId(), - '@version': this.version || 0, - member: this.members.map(function(member) { - return { - keyAttributes: { - type: member.type, - role: member.role, - ref: osmEntity.id.toOSM(member.id) - } - }; - }, this), - tag: Object.keys(this.tags).map(function(k) { - return { keyAttributes: { k: k, v: this.tags[k] } }; - }, this) - } - }; - if (changeset_id) { - r.relation['@changeset'] = changeset_id; - } - return r; - }, - - - asGeoJSON: function(resolver) { - return resolver.transient(this, 'GeoJSON', function () { - if (this.isMultipolygon()) { - return { - type: 'MultiPolygon', - coordinates: this.multipolygon(resolver) - }; - } else { - return { - type: 'FeatureCollection', - properties: this.tags, - features: this.members.map(function (member) { - return Object.assign({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver)); - }) - }; - } - }); - }, - - - area: function(resolver) { - return resolver.transient(this, 'area', function() { - return d3_geoArea(this.asGeoJSON(resolver)); - }); - }, - - - isMultipolygon: function() { - return this.tags.type === 'multipolygon'; - }, - - - isComplete: function(resolver) { - for (var i = 0; i < this.members.length; i++) { - if (!resolver.hasEntity(this.members[i].id)) { - return false; - } - } - return true; - }, - - - hasFromViaTo: function() { - return ( - this.members.some(function(m) { return m.role === 'from'; }) && - this.members.some(function(m) { return m.role === 'via'; }) && - this.members.some(function(m) { return m.role === 'to'; }) - ); - }, - - - isRestriction: function() { - return !!(this.tags.type && this.tags.type.match(/^restriction:?/)); - }, - - - isValidRestriction: function() { - if (!this.isRestriction()) return false; - - var froms = this.members.filter(function(m) { return m.role === 'from'; }); - var vias = this.members.filter(function(m) { return m.role === 'via'; }); - var tos = this.members.filter(function(m) { return m.role === 'to'; }); - - if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false; - if (froms.some(function(m) { return m.type !== 'way'; })) return false; - - if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false; - if (tos.some(function(m) { return m.type !== 'way'; })) return false; - - if (vias.length === 0) return false; - if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) return false; - - return true; - }, - - - // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm], - // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings. - // - // This corresponds to the structure needed for rendering a multipolygon path using a - // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry. - // - // In the case of invalid geometries, this function will still return a result which - // includes the nodes of all way members, but some Nds may be unclosed and some inner - // rings not matched with the intended outer ring. - // - multipolygon: function(resolver) { - var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }); - var inners = this.members.filter(function(m) { return 'inner' === m.role; }); - - outers = osmJoinWays(outers, resolver); - inners = osmJoinWays(inners, resolver); - - var sequenceToLineString = function(sequence) { - if (sequence.nodes.length > 2 && - sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) { - // close unclosed parts to ensure correct area rendering - #2945 - sequence.nodes.push(sequence.nodes[0]); - } - return sequence.nodes.map(function(node) { return node.loc; }); - }; - - outers = outers.map(sequenceToLineString); - inners = inners.map(sequenceToLineString); - - var result = outers.map(function(o) { - // Heuristic for detecting counterclockwise winding order. Assumes - // that OpenStreetMap polygons are not hemisphere-spanning. - return [d3_geoArea({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o]; - }); - - function findOuter(inner) { - var o, outer; - - for (o = 0; o < outers.length; o++) { - outer = outers[o]; - if (geoPolygonContainsPolygon(outer, inner)) - return o; - } - - for (o = 0; o < outers.length; o++) { - outer = outers[o]; - if (geoPolygonIntersectsPolygon(outer, inner, false)) - return o; - } - } - - for (var i = 0; i < inners.length; i++) { - var inner = inners[i]; - - if (d3_geoArea({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) { - inner = inner.reverse(); - } - - var o = findOuter(inners[i]); - if (o !== undefined) { - result[o].push(inners[i]); - } else { - result.push([inners[i]]); // Invalid geometry - } - } - - return result; - } - }); - - class QAItem { - constructor(loc, service, itemType, id, props) { - // Store required properties - this.loc = loc; - this.service = service.title; - this.itemType = itemType; - - // All issues must have an ID for selection, use generic if none specified - this.id = id ? id : `${QAItem.id()}`; - - this.update(props); - - // Some QA services have marker icons to differentiate issues - if (service && typeof service.getIcon === 'function') { - this.icon = service.getIcon(itemType); - } - - return this; - } - - update(props) { - // You can't override this inital information - const { loc, service, itemType, id } = this; - - Object.keys(props).forEach(prop => this[prop] = props[prop]); - - this.loc = loc; - this.service = service; - this.itemType = itemType; - this.id = id; - - return this; - } - - // Generic handling for newly created QAItems - static id() { - return this.nextId--; - } - } - QAItem.nextId = -1; - - // Split a way at the given node. - // - // Optionally, split only the given ways, if multiple ways share - // the given node. - // - // This is the inverse of `iD.actionJoin`. - // - // For testing convenience, accepts an ID to assign to the new way. - // Normally, this will be undefined and the way will automatically - // be assigned a new ID. - // - // Reference: - // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as - // - function actionSplit(nodeId, newWayIds) { - var _wayIDs; - - // The IDs of the ways actually created by running this action - var createdWayIDs = []; - - // If the way is closed, we need to search for a partner node - // to split the way at. - // - // The following looks for a node that is both far away from - // the initial node in terms of way segment length and nearby - // in terms of beeline-distance. This assures that areas get - // split on the most "natural" points (independent of the number - // of nodes). - // For example: bone-shaped areas get split across their waist - // line, circles across the diameter. - function splitArea(nodes, idxA, graph) { - var lengths = new Array(nodes.length); - var length; - var i; - var best = 0; - var idxB; - - function wrap(index) { - return utilWrap(index, nodes.length); - } - - function dist(nA, nB) { - var locA = graph.entity(nA).loc; - var locB = graph.entity(nB).loc; - var epsilon = 1e-6; - return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon; - } - - // calculate lengths - length = 0; - for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) { - length += dist(nodes[i], nodes[wrap(i - 1)]); - lengths[i] = length; - } - - length = 0; - for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) { - length += dist(nodes[i], nodes[wrap(i + 1)]); - if (length < lengths[i]) { - lengths[i] = length; - } - } - - // determine best opposite node to split - for (i = 0; i < nodes.length; i++) { - var cost = lengths[i] / dist(nodes[idxA], nodes[i]); - if (cost > best) { - idxB = i; - best = cost; - } - } - - return idxB; - } - - - function split(graph, wayA, newWayId) { - var wayB = osmWay({ id: newWayId, tags: wayA.tags }); // `wayB` is the NEW way - var origNodes = wayA.nodes.slice(); - var nodesA; - var nodesB; - var isArea = wayA.isArea(); - var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph); - - if (wayA.isClosed()) { - var nodes = wayA.nodes.slice(0, -1); - var idxA = nodes.indexOf(nodeId); - var idxB = splitArea(nodes, idxA, graph); - - if (idxB < idxA) { - nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1)); - nodesB = nodes.slice(idxB, idxA + 1); - } else { - nodesA = nodes.slice(idxA, idxB + 1); - nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1)); - } - } else { - var idx = wayA.nodes.indexOf(nodeId, 1); - nodesA = wayA.nodes.slice(0, idx + 1); - nodesB = wayA.nodes.slice(idx); - } - - wayA = wayA.update({ nodes: nodesA }); - wayB = wayB.update({ nodes: nodesB }); - - graph = graph.replace(wayA); - graph = graph.replace(wayB); - - graph.parentRelations(wayA).forEach(function(relation) { - var member; - - // Turn restrictions - make sure: - // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation - // (whichever one is connected to the VIA node/ways) - // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way - if (relation.hasFromViaTo()) { - var f = relation.memberByRole('from'); - var v = relation.membersByRole('via'); - var t = relation.memberByRole('to'); - var i; - - // 1. split a FROM/TO - if (f.id === wayA.id || t.id === wayA.id) { - var keepB = false; - if (v.length === 1 && v[0].type === 'node') { // check via node - keepB = wayB.contains(v[0].id); - } else { // check via way(s) - for (i = 0; i < v.length; i++) { - if (v[i].type === 'way') { - var wayVia = graph.hasEntity(v[i].id); - if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) { - keepB = true; - break; - } - } - } - } - - if (keepB) { - relation = relation.replaceMember(wayA, wayB); - graph = graph.replace(relation); - } - - // 2. split a VIA - } else { - for (i = 0; i < v.length; i++) { - if (v[i].type === 'way' && v[i].id === wayA.id) { - member = { - id: wayB.id, - type: 'way', - role: 'via' - }; - graph = actionAddMember(relation.id, member, v[i].index + 1)(graph); - break; - } - } - } - - // All other relations (Routes, Multipolygons, etc): - // 1. Both `wayA` and `wayB` remain in the relation - // 2. But must be inserted as a pair (see `actionAddMember` for details) - } else { - if (relation === isOuter) { - graph = graph.replace(relation.mergeTags(wayA.tags)); - graph = graph.replace(wayA.update({ tags: {} })); - graph = graph.replace(wayB.update({ tags: {} })); - } - - member = { - id: wayB.id, - type: 'way', - role: relation.memberById(wayA.id).role - }; - - var insertPair = { - originalID: wayA.id, - insertedID: wayB.id, - nodes: origNodes - }; - - graph = actionAddMember(relation.id, member, undefined, insertPair)(graph); - } - }); - - if (!isOuter && isArea) { - var multipolygon = osmRelation({ - tags: Object.assign({}, wayA.tags, { type: 'multipolygon' }), - members: [ - { id: wayA.id, role: 'outer', type: 'way' }, - { id: wayB.id, role: 'outer', type: 'way' } - ] - }); - - graph = graph.replace(multipolygon); - graph = graph.replace(wayA.update({ tags: {} })); - graph = graph.replace(wayB.update({ tags: {} })); - } - - createdWayIDs.push(wayB.id); - - return graph; - } - - var action = function(graph) { - var candidates = action.ways(graph); - createdWayIDs = []; - for (var i = 0; i < candidates.length; i++) { - graph = split(graph, candidates[i], newWayIds && newWayIds[i]); - } - return graph; - }; - - action.getCreatedWayIDs = function() { - return createdWayIDs; - }; - - action.ways = function(graph) { - var node = graph.entity(nodeId); - var parents = graph.parentWays(node); - var hasLines = parents.some(function(parent) { - return parent.geometry(graph) === 'line'; - }); - - return parents.filter(function(parent) { - if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) - return false; - - if (!_wayIDs && hasLines && parent.geometry(graph) !== 'line') - return false; - - if (parent.isClosed()) { - return true; - } - - for (var i = 1; i < parent.nodes.length - 1; i++) { - if (parent.nodes[i] === nodeId) { - return true; - } - } - - return false; - }); - }; - - - action.disabled = function(graph) { - var candidates = action.ways(graph); - if (candidates.length === 0 || (_wayIDs && _wayIDs.length !== candidates.length)) { - return 'not_eligible'; - } - }; - - - action.limitWays = function(val) { - if (!arguments.length) return _wayIDs; - _wayIDs = val; - return action; - }; - - - return action; - } - - function coreGraph(other, mutable) { - if (!(this instanceof coreGraph)) return new coreGraph(other, mutable); - - if (other instanceof coreGraph) { - var base = other.base(); - this.entities = Object.assign(Object.create(base.entities), other.entities); - this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays); - this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels); - - } else { - this.entities = Object.create({}); - this._parentWays = Object.create({}); - this._parentRels = Object.create({}); - this.rebase(other || [], [this]); - } - - this.transients = {}; - this._childNodes = {}; - this.frozen = !mutable; - } - - - coreGraph.prototype = { - - hasEntity: function(id) { - return this.entities[id]; - }, - - - entity: function(id) { - var entity = this.entities[id]; - - //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376 - if (!entity) { - entity = this.entities.__proto__[id]; // eslint-disable-line no-proto - } - - if (!entity) { - throw new Error('entity ' + id + ' not found'); - } - return entity; - }, - - - geometry: function(id) { - return this.entity(id).geometry(this); - }, - - - transient: function(entity, key, fn) { - var id = entity.id; - var transients = this.transients[id] || (this.transients[id] = {}); - - if (transients[key] !== undefined) { - return transients[key]; - } - - transients[key] = fn.call(entity); - - return transients[key]; - }, - - - parentWays: function(entity) { - var parents = this._parentWays[entity.id]; - var result = []; - if (parents) { - parents.forEach(function(id) { - result.push(this.entity(id)); - }, this); - } - return result; - }, - - - isPoi: function(entity) { - var parents = this._parentWays[entity.id]; - return !parents || parents.size === 0; - }, - - - isShared: function(entity) { - var parents = this._parentWays[entity.id]; - return parents && parents.size > 1; - }, - - - parentRelations: function(entity) { - var parents = this._parentRels[entity.id]; - var result = []; - if (parents) { - parents.forEach(function(id) { - result.push(this.entity(id)); - }, this); - } - return result; - }, - - parentMultipolygons: function(entity) { - return this.parentRelations(entity).filter(function(relation) { - return relation.isMultipolygon(); - }); - }, - - - childNodes: function(entity) { - if (this._childNodes[entity.id]) return this._childNodes[entity.id]; - if (!entity.nodes) return []; - - var nodes = []; - for (var i = 0; i < entity.nodes.length; i++) { - nodes[i] = this.entity(entity.nodes[i]); - } - - this._childNodes[entity.id] = nodes; - return this._childNodes[entity.id]; - }, - - - base: function() { - return { - 'entities': Object.getPrototypeOf(this.entities), - 'parentWays': Object.getPrototypeOf(this._parentWays), - 'parentRels': Object.getPrototypeOf(this._parentRels) - }; - }, - - - // Unlike other graph methods, rebase mutates in place. This is because it - // is used only during the history operation that merges newly downloaded - // data into each state. To external consumers, it should appear as if the - // graph always contained the newly downloaded data. - rebase: function(entities, stack, force) { - var base = this.base(); - var i, j, k, id; - - for (i = 0; i < entities.length; i++) { - var entity = entities[i]; - - if (!entity.visible || (!force && base.entities[entity.id])) - continue; - - // Merging data into the base graph - base.entities[entity.id] = entity; - this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); - - // Restore provisionally-deleted nodes that are discovered to have an extant parent - if (entity.type === 'way') { - for (j = 0; j < entity.nodes.length; j++) { - id = entity.nodes[j]; - for (k = 1; k < stack.length; k++) { - var ents = stack[k].entities; - if (ents.hasOwnProperty(id) && ents[id] === undefined) { - delete ents[id]; - } - } - } - } - } - - for (i = 0; i < stack.length; i++) { - stack[i]._updateRebased(); - } - }, - - - _updateRebased: function() { - var base = this.base(); - - Object.keys(this._parentWays).forEach(function(child) { - if (base.parentWays[child]) { - base.parentWays[child].forEach(function(id) { - if (!this.entities.hasOwnProperty(id)) { - this._parentWays[child].add(id); - } - }, this); - } - }, this); - - Object.keys(this._parentRels).forEach(function(child) { - if (base.parentRels[child]) { - base.parentRels[child].forEach(function(id) { - if (!this.entities.hasOwnProperty(id)) { - this._parentRels[child].add(id); - } - }, this); - } - }, this); - - this.transients = {}; - - // this._childNodes is not updated, under the assumption that - // ways are always downloaded with their child nodes. - }, - - - // Updates calculated properties (parentWays, parentRels) for the specified change - _updateCalculated: function(oldentity, entity, parentWays, parentRels) { - parentWays = parentWays || this._parentWays; - parentRels = parentRels || this._parentRels; - - var type = entity && entity.type || oldentity && oldentity.type; - var removed, added, i; - - if (type === 'way') { // Update parentWays - if (oldentity && entity) { - removed = utilArrayDifference(oldentity.nodes, entity.nodes); - added = utilArrayDifference(entity.nodes, oldentity.nodes); - } else if (oldentity) { - removed = oldentity.nodes; - added = []; - } else if (entity) { - removed = []; - added = entity.nodes; - } - for (i = 0; i < removed.length; i++) { - // make a copy of prototype property, store as own property, and update.. - parentWays[removed[i]] = new Set(parentWays[removed[i]]); - parentWays[removed[i]].delete(oldentity.id); - } - for (i = 0; i < added.length; i++) { - // make a copy of prototype property, store as own property, and update.. - parentWays[added[i]] = new Set(parentWays[added[i]]); - parentWays[added[i]].add(entity.id); - } - - } else if (type === 'relation') { // Update parentRels - - // diff only on the IDs since the same entity can be a member multiple times with different roles - var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) { return m.id; }) : []; - var entityMemberIDs = entity ? entity.members.map(function(m) { return m.id; }) : []; - - if (oldentity && entity) { - removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs); - added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs); - } else if (oldentity) { - removed = oldentityMemberIDs; - added = []; - } else if (entity) { - removed = []; - added = entityMemberIDs; - } - for (i = 0; i < removed.length; i++) { - // make a copy of prototype property, store as own property, and update.. - parentRels[removed[i]] = new Set(parentRels[removed[i]]); - parentRels[removed[i]].delete(oldentity.id); - } - for (i = 0; i < added.length; i++) { - // make a copy of prototype property, store as own property, and update.. - parentRels[added[i]] = new Set(parentRels[added[i]]); - parentRels[added[i]].add(entity.id); - } - } - }, - - - replace: function(entity) { - if (this.entities[entity.id] === entity) return this; - - return this.update(function() { - this._updateCalculated(this.entities[entity.id], entity); - this.entities[entity.id] = entity; - }); - }, - - - remove: function(entity) { - return this.update(function() { - this._updateCalculated(entity, undefined); - this.entities[entity.id] = undefined; - }); - }, - - - revert: function(id) { - var baseEntity = this.base().entities[id]; - var headEntity = this.entities[id]; - if (headEntity === baseEntity) return this; - - return this.update(function() { - this._updateCalculated(headEntity, baseEntity); - delete this.entities[id]; - }); - }, - - - update: function() { - var graph = this.frozen ? coreGraph(this, true) : this; - for (var i = 0; i < arguments.length; i++) { - arguments[i].call(graph, graph); - } - - if (this.frozen) graph.frozen = true; - - return graph; - }, - - - // Obliterates any existing entities - load: function(entities) { - var base = this.base(); - this.entities = Object.create(base.entities); - - for (var i in entities) { - this.entities[i] = entities[i]; - this._updateCalculated(base.entities[i], this.entities[i]); - } - - return this; - } - }; - - function osmTurn(turn) { - if (!(this instanceof osmTurn)) { - return new osmTurn(turn); - } - Object.assign(this, turn); - } - - - function osmIntersection(graph, startVertexId, maxDistance) { - maxDistance = maxDistance || 30; // in meters - var vgraph = coreGraph(); // virtual graph - var i, j, k; - - - function memberOfRestriction(entity) { - return graph.parentRelations(entity) - .some(function(r) { return r.isRestriction(); }); - } - - function isRoad(way) { - if (way.isArea() || way.isDegenerate()) return false; - var roads = { - 'motorway': true, - 'motorway_link': true, - 'trunk': true, - 'trunk_link': true, - 'primary': true, - 'primary_link': true, - 'secondary': true, - 'secondary_link': true, - 'tertiary': true, - 'tertiary_link': true, - 'residential': true, - 'unclassified': true, - 'living_street': true, - 'service': true, - 'road': true, - 'track': true - }; - return roads[way.tags.highway]; - } - - - var startNode = graph.entity(startVertexId); - var checkVertices = [startNode]; - var checkWays; - var vertices = []; - var vertexIds = []; - var vertex; - var ways = []; - var wayIds = []; - var way; - var nodes = []; - var node; - var parents = []; - var parent; - - // `actions` will store whatever actions must be performed to satisfy - // preconditions for adding a turn restriction to this intersection. - // - Remove any existing degenerate turn restrictions (missing from/to, etc) - // - Reverse oneways so that they are drawn in the forward direction - // - Split ways on key vertices - var actions = []; - - - // STEP 1: walk the graph outwards from starting vertex to search - // for more key vertices and ways to include in the intersection.. - - while (checkVertices.length) { - vertex = checkVertices.pop(); - - // check this vertex for parent ways that are roads - checkWays = graph.parentWays(vertex); - var hasWays = false; - for (i = 0; i < checkWays.length; i++) { - way = checkWays[i]; - if (!isRoad(way) && !memberOfRestriction(way)) continue; - - ways.push(way); // it's a road, or it's already in a turn restriction - hasWays = true; - - // check the way's children for more key vertices - nodes = utilArrayUniq(graph.childNodes(way)); - for (j = 0; j < nodes.length; j++) { - node = nodes[j]; - if (node === vertex) continue; // same thing - if (vertices.indexOf(node) !== -1) continue; // seen it already - if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start - - // a key vertex will have parents that are also roads - var hasParents = false; - parents = graph.parentWays(node); - for (k = 0; k < parents.length; k++) { - parent = parents[k]; - if (parent === way) continue; // same thing - if (ways.indexOf(parent) !== -1) continue; // seen it already - if (!isRoad(parent)) continue; // not a road - hasParents = true; - break; - } - - if (hasParents) { - checkVertices.push(node); - } - } - } - - if (hasWays) { - vertices.push(vertex); - } - } - - vertices = utilArrayUniq(vertices); - ways = utilArrayUniq(ways); - - - // STEP 2: Build a virtual graph containing only the entities in the intersection.. - // Everything done after this step should act on the virtual graph - // Any actions that must be performed later to the main graph go in `actions` array - ways.forEach(function(way) { - graph.childNodes(way).forEach(function(node) { - vgraph = vgraph.replace(node); - }); - - vgraph = vgraph.replace(way); - - graph.parentRelations(way).forEach(function(relation) { - if (relation.isRestriction()) { - if (relation.isValidRestriction(graph)) { - vgraph = vgraph.replace(relation); - } else if (relation.isComplete(graph)) { - actions.push(actionDeleteRelation(relation.id)); - } - } - }); - }); - - - // STEP 3: Force all oneways to be drawn in the forward direction - ways.forEach(function(w) { - var way = vgraph.entity(w.id); - if (way.tags.oneway === '-1') { - var action = actionReverse(way.id, { reverseOneway: true }); - actions.push(action); - vgraph = action(vgraph); - } - }); - - - // STEP 4: Split ways on key vertices - var origCount = osmEntity.id.next.way; - vertices.forEach(function(v) { - // This is an odd way to do it, but we need to find all the ways that - // will be split here, then split them one at a time to ensure that these - // actions can be replayed on the main graph exactly in the same order. - // (It is unintuitive, but the order of ways returned from graph.parentWays() - // is arbitrary, depending on how the main graph and vgraph were built) - var splitAll = actionSplit(v.id); - if (!splitAll.disabled(vgraph)) { - splitAll.ways(vgraph).forEach(function(way) { - var splitOne = actionSplit(v.id).limitWays([way.id]); - actions.push(splitOne); - vgraph = splitOne(vgraph); - }); - } - }); - - // In here is where we should also split the intersection at nearby junction. - // for https://github.com/mapbox/iD-internal/issues/31 - // nearbyVertices.forEach(function(v) { - // }); - - // Reasons why we reset the way id count here: - // 1. Continuity with way ids created by the splits so that we can replay - // these actions later if the user decides to create a turn restriction - // 2. Avoids churning way ids just by hovering over a vertex - // and displaying the turn restriction editor - osmEntity.id.next.way = origCount; - - - // STEP 5: Update arrays to point to vgraph entities - vertexIds = vertices.map(function(v) { return v.id; }); - vertices = []; - ways = []; - - vertexIds.forEach(function(id) { - var vertex = vgraph.entity(id); - var parents = vgraph.parentWays(vertex); - vertices.push(vertex); - ways = ways.concat(parents); - }); - - vertices = utilArrayUniq(vertices); - ways = utilArrayUniq(ways); - - vertexIds = vertices.map(function(v) { return v.id; }); - wayIds = ways.map(function(w) { return w.id; }); - - - // STEP 6: Update the ways with some metadata that will be useful for - // walking the intersection graph later and rendering turn arrows. - - function withMetadata(way, vertexIds) { - var __oneWay = way.isOneWay(); - - // which affixes are key vertices? - var __first = (vertexIds.indexOf(way.first()) !== -1); - var __last = (vertexIds.indexOf(way.last()) !== -1); - - // what roles is this way eligible for? - var __via = (__first && __last); - var __from = ((__first && !__oneWay) || __last); - var __to = (__first || (__last && !__oneWay)); - - return way.update({ - __first: __first, - __last: __last, - __from: __from, - __via: __via, - __to: __to, - __oneWay: __oneWay - }); - } - - ways = []; - wayIds.forEach(function(id) { - var way = withMetadata(vgraph.entity(id), vertexIds); - vgraph = vgraph.replace(way); - ways.push(way); - }); - - - // STEP 7: Simplify - This is an iterative process where we: - // 1. Find trivial vertices with only 2 parents - // 2. trim off the leaf way from those vertices and remove from vgraph - - var keepGoing; - var removeWayIds = []; - var removeVertexIds = []; - - do { - keepGoing = false; - checkVertices = vertexIds.slice(); - - for (i = 0; i < checkVertices.length; i++) { - var vertexId = checkVertices[i]; - vertex = vgraph.hasEntity(vertexId); - - if (!vertex) { - if (vertexIds.indexOf(vertexId) !== -1) { - vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one - } - removeVertexIds.push(vertexId); - continue; - } - - parents = vgraph.parentWays(vertex); - if (parents.length < 3) { - if (vertexIds.indexOf(vertexId) !== -1) { - vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one - } - } - - if (parents.length === 2) { // vertex with 2 parents is trivial - var a = parents[0]; - var b = parents[1]; - var aIsLeaf = a && !a.__via; - var bIsLeaf = b && !b.__via; - var leaf, survivor; - - if (aIsLeaf && !bIsLeaf) { - leaf = a; - survivor = b; - } else if (!aIsLeaf && bIsLeaf) { - leaf = b; - survivor = a; - } - - if (leaf && survivor) { - survivor = withMetadata(survivor, vertexIds); // update survivor way - vgraph = vgraph.replace(survivor).remove(leaf); // update graph - removeWayIds.push(leaf.id); - keepGoing = true; - } - } - - parents = vgraph.parentWays(vertex); - - if (parents.length < 2) { // vertex is no longer a key vertex - if (vertexIds.indexOf(vertexId) !== -1) { - vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one - } - removeVertexIds.push(vertexId); - keepGoing = true; - } - - if (parents.length < 1) { // vertex is no longer attached to anything - vgraph = vgraph.remove(vertex); - } - - } - } while (keepGoing); - - - vertices = vertices - .filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; }) - .map(function(vertex) { return vgraph.entity(vertex.id); }); - ways = ways - .filter(function(way) { return removeWayIds.indexOf(way.id) === -1; }) - .map(function(way) { return vgraph.entity(way.id); }); - - - // OK! Here is our intersection.. - var intersection = { - graph: vgraph, - actions: actions, - vertices: vertices, - ways: ways, - }; - - - - // Get all the valid turns through this intersection given a starting way id. - // This operates on the virtual graph for everything. - // - // Basically, walk through all possible paths from starting way, - // honoring the existing turn restrictions as we go (watch out for loops!) - // - // For each path found, generate and return a `osmTurn` datastructure. - // - intersection.turns = function(fromWayId, maxViaWay) { - if (!fromWayId) return []; - if (!maxViaWay) maxViaWay = 0; - - var vgraph = intersection.graph; - var keyVertexIds = intersection.vertices.map(function(v) { return v.id; }); - - var start = vgraph.entity(fromWayId); - if (!start || !(start.__from || start.__via)) return []; - - // maxViaWay=0 from-*-to (0 vias) - // maxViaWay=1 from-*-via-*-to (1 via max) - // maxViaWay=2 from-*-via-*-via-*-to (2 vias max) - var maxPathLength = (maxViaWay * 2) + 3; - var turns = []; - - step(start); - return turns; - - - // traverse the intersection graph and find all the valid paths - function step(entity, currPath, currRestrictions, matchedRestriction) { - currPath = (currPath || []).slice(); // shallow copy - if (currPath.length >= maxPathLength) return; - currPath.push(entity.id); - currRestrictions = (currRestrictions || []).slice(); // shallow copy - var i, j; - - if (entity.type === 'node') { - var parents = vgraph.parentWays(entity); - var nextWays = []; - - // which ways can we step into? - for (i = 0; i < parents.length; i++) { - var way = parents[i]; - - // if next way is a oneway incoming to this vertex, skip - if (way.__oneWay && way.nodes[0] !== entity.id) continue; - - // if we have seen it before (allowing for an initial u-turn), skip - if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; - - // Check all "current" restrictions (where we've already walked the `FROM`) - var restrict = undefined; - for (j = 0; j < currRestrictions.length; j++) { - var restriction = currRestrictions[j]; - var f = restriction.memberByRole('from'); - var v = restriction.membersByRole('via'); - var t = restriction.memberByRole('to'); - var isOnly = /^only_/.test(restriction.tags.restriction); - - // Does the current path match this turn restriction? - var matchesFrom = (f.id === fromWayId); - var matchesViaTo = false; - var isAlongOnlyPath = false; - - if (t.id === way.id) { // match TO - - if (v.length === 1 && v[0].type === 'node') { // match VIA node - matchesViaTo = (v[0].id === entity.id && ( - (matchesFrom && currPath.length === 2) || - (!matchesFrom && currPath.length > 2) - )); - - } else { // match all VIA ways - var pathVias = []; - for (k = 2; k < currPath.length; k +=2 ) { // k = 2 skips FROM - pathVias.push(currPath[k]); // (path goes way-node-way...) - } - var restrictionVias = []; - for (k = 0; k < v.length; k++) { - if (v[k].type === 'way') { - restrictionVias.push(v[k].id); - } - } - var diff = utilArrayDifference(pathVias, restrictionVias); - matchesViaTo = !diff.length; - } - - } else if (isOnly) { - for (k = 0; k < v.length; k++) { - // way doesn't match TO, but is one of the via ways along the path of an "only" - if (v[k].type === 'way' && v[k].id === way.id) { - isAlongOnlyPath = true; - break; - } - } - } - - if (matchesViaTo) { - if (isOnly) { - restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true }; - } else { - restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true }; - } - } else { // indirect - caused by a different nearby restriction - if (isAlongOnlyPath) { - restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false }; - } else if (isOnly) { - restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true }; - } - } - - // stop looking if we find a "direct" restriction (matching FROM, VIA, TO) - if (restrict && restrict.direct) - break; - } - - nextWays.push({ way: way, restrict: restrict }); - } - - nextWays.forEach(function(nextWay) { - step(nextWay.way, currPath, currRestrictions, nextWay.restrict); - }); - - - } else { // entity.type === 'way' - if (currPath.length >= 3) { // this is a "complete" path.. - var turnPath = currPath.slice(); // shallow copy - - // an indirect restriction - only include the partial path (starting at FROM) - if (matchedRestriction && matchedRestriction.direct === false) { - for (i = 0; i < turnPath.length; i++) { - if (turnPath[i] === matchedRestriction.from) { - turnPath = turnPath.slice(i); - break; - } - } - } - - var turn = pathToTurn(turnPath); - if (turn) { - if (matchedRestriction) { - turn.restrictionID = matchedRestriction.id; - turn.no = matchedRestriction.no; - turn.only = matchedRestriction.only; - turn.direct = matchedRestriction.direct; - } - turns.push(osmTurn(turn)); - } - - if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here - } - - if (matchedRestriction && matchedRestriction.end) return; // don't advance any further - - // which nodes can we step into? - var n1 = vgraph.entity(entity.first()); - var n2 = vgraph.entity(entity.last()); - var dist = geoSphericalDistance(n1.loc, n2.loc); - var nextNodes = []; - - if (currPath.length > 1) { - if (dist > maxDistance) return; // the next node is too far - if (!entity.__via) return; // this way is a leaf / can't be a via - } - - if (!entity.__oneWay && // bidirectional.. - keyVertexIds.indexOf(n1.id) !== -1 && // key vertex.. - currPath.indexOf(n1.id) === -1) { // haven't seen it yet.. - nextNodes.push(n1); // can advance to first node - } - if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex.. - currPath.indexOf(n2.id) === -1) { // haven't seen it yet.. - nextNodes.push(n2); // can advance to last node - } - - nextNodes.forEach(function(nextNode) { - // gather restrictions FROM this way - var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) { - if (!r.isRestriction()) return false; - - var f = r.memberByRole('from'); - if (!f || f.id !== entity.id) return false; - - var isOnly = /^only_/.test(r.tags.restriction); - if (!isOnly) return true; - - // `only_` restrictions only matter along the direction of the VIA - #4849 - var isOnlyVia = false; - var v = r.membersByRole('via'); - if (v.length === 1 && v[0].type === 'node') { // via node - isOnlyVia = (v[0].id === nextNode.id); - } else { // via way(s) - for (var i = 0; i < v.length; i++) { - if (v[i].type !== 'way') continue; - var viaWay = vgraph.entity(v[i].id); - if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) { - isOnlyVia = true; - break; - } - } - } - return isOnlyVia; - }); - - step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false); - }); - } - } - - - // assumes path is alternating way-node-way of odd length - function pathToTurn(path) { - if (path.length < 3) return; - var fromWayId, fromNodeId, fromVertexId; - var toWayId, toNodeId, toVertexId; - var viaWayIds, viaNodeId, isUturn; - - fromWayId = path[0]; - toWayId = path[path.length - 1]; - - if (path.length === 3 && fromWayId === toWayId) { // u turn - var way = vgraph.entity(fromWayId); - if (way.__oneWay) return null; - - isUturn = true; - viaNodeId = fromVertexId = toVertexId = path[1]; - fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId); - - } else { - isUturn = false; - fromVertexId = path[1]; - fromNodeId = adjacentNode(fromWayId, fromVertexId); - toVertexId = path[path.length - 2]; - toNodeId = adjacentNode(toWayId, toVertexId); - - if (path.length === 3) { - viaNodeId = path[1]; - } else { - viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; }); - viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last - } - } - - return { - key: path.join('_'), - path: path, - from: { node: fromNodeId, way: fromWayId, vertex: fromVertexId }, - via: { node: viaNodeId, ways: viaWayIds }, - to: { node: toNodeId, way: toWayId, vertex: toVertexId }, - u: isUturn - }; - - - function adjacentNode(wayId, affixId) { - var nodes = vgraph.entity(wayId).nodes; - return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2]; - } - } - - }; - - return intersection; - } - - - function osmInferRestriction(graph, turn, projection) { - var fromWay = graph.entity(turn.from.way); - var fromNode = graph.entity(turn.from.node); - var fromVertex = graph.entity(turn.from.vertex); - var toWay = graph.entity(turn.to.way); - var toNode = graph.entity(turn.to.node); - var toVertex = graph.entity(turn.to.vertex); - - var fromOneWay = (fromWay.tags.oneway === 'yes'); - var toOneWay = (toWay.tags.oneway === 'yes'); - var angle = (geoAngle(fromVertex, fromNode, projection) - - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI; - - while (angle < 0) - angle += 360; - - if (fromNode === toNode) - return 'no_u_turn'; - if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) - return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway - if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) - return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to) - if (angle < 158) - return 'no_right_turn'; - if (angle > 202) - return 'no_left_turn'; - - return 'no_straight_on'; - } - - function actionMergePolygon(ids, newRelationId) { - - function groupEntities(graph) { - var entities = ids.map(function (id) { return graph.entity(id); }); - var geometryGroups = utilArrayGroupBy(entities, function(entity) { - if (entity.type === 'way' && entity.isClosed()) { - return 'closedWay'; - } else if (entity.type === 'relation' && entity.isMultipolygon()) { - return 'multipolygon'; - } else { - return 'other'; - } - }); - - return Object.assign( - { closedWay: [], multipolygon: [], other: [] }, - geometryGroups - ); - } - - - var action = function(graph) { - var entities = groupEntities(graph); - - // An array representing all the polygons that are part of the multipolygon. - // - // Each element is itself an array of objects with an id property, and has a - // locs property which is an array of the locations forming the polygon. - var polygons = entities.multipolygon.reduce(function(polygons, m) { - return polygons.concat(osmJoinWays(m.members, graph)); - }, []).concat(entities.closedWay.map(function(d) { - var member = [{id: d.id}]; - member.nodes = graph.childNodes(d); - return member; - })); - - // contained is an array of arrays of boolean values, - // where contained[j][k] is true iff the jth way is - // contained by the kth way. - var contained = polygons.map(function(w, i) { - return polygons.map(function(d, n) { - if (i === n) return null; - return geoPolygonContainsPolygon( - d.nodes.map(function(n) { return n.loc; }), - w.nodes.map(function(n) { return n.loc; }) - ); - }); - }); - - // Sort all polygons as either outer or inner ways - var members = []; - var outer = true; - - while (polygons.length) { - extractUncontained(polygons); - polygons = polygons.filter(isContained); - contained = contained.filter(isContained).map(filterContained); - } - - function isContained(d, i) { - return contained[i].some(function(val) { return val; }); - } - - function filterContained(d) { - return d.filter(isContained); - } - - function extractUncontained(polygons) { - polygons.forEach(function(d, i) { - if (!isContained(d, i)) { - d.forEach(function(member) { - members.push({ - type: 'way', - id: member.id, - role: outer ? 'outer' : 'inner' - }); - }); - } - }); - outer = !outer; - } - - // Move all tags to one relation - var relation = entities.multipolygon[0] || - osmRelation({ id: newRelationId, tags: { type: 'multipolygon' }}); - - entities.multipolygon.slice(1).forEach(function(m) { - relation = relation.mergeTags(m.tags); - graph = graph.remove(m); - }); - - entities.closedWay.forEach(function(way) { - function isThisOuter(m) { - return m.id === way.id && m.role !== 'inner'; - } - if (members.some(isThisOuter)) { - relation = relation.mergeTags(way.tags); - graph = graph.replace(way.update({ tags: {} })); - } - }); - - return graph.replace(relation.update({ - members: members, - tags: utilObjectOmit(relation.tags, ['area']) - })); - }; - - - action.disabled = function(graph) { - var entities = groupEntities(graph); - if (entities.other.length > 0 || - entities.closedWay.length + entities.multipolygon.length < 2) { - return 'not_eligible'; - } - if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); })) { - return 'incomplete_relation'; - } - - if (!entities.multipolygon.length) { - var sharedMultipolygons = []; - entities.closedWay.forEach(function(way, i) { - if (i === 0) { - sharedMultipolygons = graph.parentMultipolygons(way); - } else { - sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way)); - } - }); - sharedMultipolygons = sharedMultipolygons.filter(function(relation) { - return relation.members.length === entities.closedWay.length; - }); - if (sharedMultipolygons.length) { - // don't create a new multipolygon if it'd be redundant - return 'not_eligible'; - } - } else if (entities.closedWay.some(function(way) { - return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length; - })) { - // don't add a way to a multipolygon again if it's already a member - return 'not_eligible'; - } - }; - - - return action; - } - - // do not edit .js files directly - edit src/index.jst - - - - var fastDeepEqual = function equal(a, b) { - if (a === b) return true; - - if (a && b && typeof a == 'object' && typeof b == 'object') { - if (a.constructor !== b.constructor) return false; - - var length, i, keys; - if (Array.isArray(a)) { - length = a.length; - if (length != b.length) return false; - for (i = length; i-- !== 0;) - if (!equal(a[i], b[i])) return false; - return true; - } - - - - if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; - if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); - if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); - - keys = Object.keys(a); - length = keys.length; - if (length !== Object.keys(b).length) return false; - - for (i = length; i-- !== 0;) - if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; - - for (i = length; i-- !== 0;) { - var key = keys[i]; - - if (!equal(a[key], b[key])) return false; - } - - return true; - } - - // true if both NaN, false otherwise - return a!==a && b!==b; - }; - - // Text diff algorithm following Hunt and McIlroy 1976. - // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer - // comparison, Bell Telephone Laboratories CSTR #41 (1976) - // http://www.cs.dartmouth.edu/~doug/ - // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem - // - // Expects two arrays, finds longest common sequence - function LCS(buffer1, buffer2) { - - let equivalenceClasses = {}; - for (let j = 0; j < buffer2.length; j++) { - const item = buffer2[j]; - if (equivalenceClasses[item]) { - equivalenceClasses[item].push(j); - } else { - equivalenceClasses[item] = [j]; - } - } - - const NULLRESULT = { buffer1index: -1, buffer2index: -1, chain: null }; - let candidates = [NULLRESULT]; - - for (let i = 0; i < buffer1.length; i++) { - const item = buffer1[i]; - const buffer2indices = equivalenceClasses[item] || []; - let r = 0; - let c = candidates[0]; - - for (let jx = 0; jx < buffer2indices.length; jx++) { - const j = buffer2indices[jx]; - - let s; - for (s = r; s < candidates.length; s++) { - if ((candidates[s].buffer2index < j) && ((s === candidates.length - 1) || (candidates[s + 1].buffer2index > j))) { - break; - } - } - - if (s < candidates.length) { - const newCandidate = { buffer1index: i, buffer2index: j, chain: candidates[s] }; - if (r === candidates.length) { - candidates.push(c); - } else { - candidates[r] = c; - } - r = s + 1; - c = newCandidate; - if (r === candidates.length) { - break; // no point in examining further (j)s - } - } - } - - candidates[r] = c; - } - - // At this point, we know the LCS: it's in the reverse of the - // linked-list through .chain of candidates[candidates.length - 1]. - - return candidates[candidates.length - 1]; - } - - - // We apply the LCS to give a simple representation of the - // offsets and lengths of mismatched chunks in the input - // buffers. This is used by diff3MergeRegions. - function diffIndices(buffer1, buffer2) { - const lcs = LCS(buffer1, buffer2); - let result = []; - let tail1 = buffer1.length; - let tail2 = buffer2.length; - - for (let candidate = lcs; candidate !== null; candidate = candidate.chain) { - const mismatchLength1 = tail1 - candidate.buffer1index - 1; - const mismatchLength2 = tail2 - candidate.buffer2index - 1; - tail1 = candidate.buffer1index; - tail2 = candidate.buffer2index; - - if (mismatchLength1 || mismatchLength2) { - result.push({ - buffer1: [tail1 + 1, mismatchLength1], - buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1), - buffer2: [tail2 + 1, mismatchLength2], - buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2) - }); - } - } - - result.reverse(); - return result; - } - - - // Given three buffers, A, O, and B, where both A and B are - // independently derived from O, returns a fairly complicated - // internal representation of merge decisions it's taken. The - // interested reader may wish to consult - // - // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. - // 'A Formal Investigation of ' In Arvind and Prasad, - // editors, Foundations of Software Technology and Theoretical - // Computer Science (FSTTCS), December 2007. - // - // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf) - // - function diff3MergeRegions(a, o, b) { - - // "hunks" are array subsets where `a` or `b` are different from `o` - // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html - let hunks = []; - function addHunk(h, ab) { - hunks.push({ - ab: ab, - oStart: h.buffer1[0], - oLength: h.buffer1[1], // length of o to remove - abStart: h.buffer2[0], - abLength: h.buffer2[1] // length of a/b to insert - // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1]) - }); - } - - diffIndices(o, a).forEach(item => addHunk(item, 'a')); - diffIndices(o, b).forEach(item => addHunk(item, 'b')); - hunks.sort((x,y) => x.oStart - y.oStart); - - let results = []; - let currOffset = 0; - - function advanceTo(endOffset) { - if (endOffset > currOffset) { - results.push({ - stable: true, - buffer: 'o', - bufferStart: currOffset, - bufferLength: endOffset - currOffset, - bufferContent: o.slice(currOffset, endOffset) - }); - currOffset = endOffset; - } - } - - while (hunks.length) { - let hunk = hunks.shift(); - let regionStart = hunk.oStart; - let regionEnd = hunk.oStart + hunk.oLength; - let regionHunks = [hunk]; - advanceTo(regionStart); - - // Try to pull next overlapping hunk into this region - while (hunks.length) { - const nextHunk = hunks[0]; - const nextHunkStart = nextHunk.oStart; - if (nextHunkStart > regionEnd) break; // no overlap - - regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength); - regionHunks.push(hunks.shift()); - } - - if (regionHunks.length === 1) { - // Only one hunk touches this region, meaning that there is no conflict here. - // Either `a` or `b` is inserting into a region of `o` unchanged by the other. - if (hunk.abLength > 0) { - const buffer = (hunk.ab === 'a' ? a : b); - results.push({ - stable: true, - buffer: hunk.ab, - bufferStart: hunk.abStart, - bufferLength: hunk.abLength, - bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength) - }); - } - } else { - // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`. - // Effectively merge all the `a` hunks into one giant hunk, then do the - // same for the `b` hunks; then, correct for skew in the regions of `o` - // that each side changed, and report appropriate spans for the three sides. - let bounds = { - a: [a.length, -1, o.length, -1], - b: [b.length, -1, o.length, -1] - }; - while (regionHunks.length) { - hunk = regionHunks.shift(); - const oStart = hunk.oStart; - const oEnd = oStart + hunk.oLength; - const abStart = hunk.abStart; - const abEnd = abStart + hunk.abLength; - let b = bounds[hunk.ab]; - b[0] = Math.min(abStart, b[0]); - b[1] = Math.max(abEnd, b[1]); - b[2] = Math.min(oStart, b[2]); - b[3] = Math.max(oEnd, b[3]); - } - - const aStart = bounds.a[0] + (regionStart - bounds.a[2]); - const aEnd = bounds.a[1] + (regionEnd - bounds.a[3]); - const bStart = bounds.b[0] + (regionStart - bounds.b[2]); - const bEnd = bounds.b[1] + (regionEnd - bounds.b[3]); - - let result = { - stable: false, - aStart: aStart, - aLength: aEnd - aStart, - aContent: a.slice(aStart, aEnd), - oStart: regionStart, - oLength: regionEnd - regionStart, - oContent: o.slice(regionStart, regionEnd), - bStart: bStart, - bLength: bEnd - bStart, - bContent: b.slice(bStart, bEnd) - }; - results.push(result); - } - currOffset = regionEnd; - } - - advanceTo(o.length); - - return results; - } - - - // Applies the output of diff3MergeRegions to actually - // construct the merged buffer; the returned result alternates - // between 'ok' and 'conflict' blocks. - // A "false conflict" is where `a` and `b` both change the same from `o` - function diff3Merge(a, o, b, options) { - let defaults = { - excludeFalseConflicts: true, - stringSeparator: /\s+/ - }; - options = Object.assign(defaults, options); - - const aString = (typeof a === 'string'); - const oString = (typeof o === 'string'); - const bString = (typeof b === 'string'); - - if (aString) a = a.split(options.stringSeparator); - if (oString) o = o.split(options.stringSeparator); - if (bString) b = b.split(options.stringSeparator); - - let results = []; - const regions = diff3MergeRegions(a, o, b); - - let okBuffer = []; - function flushOk() { - if (okBuffer.length) { - results.push({ ok: okBuffer }); - } - okBuffer = []; - } - - function isFalseConflict(a, b) { - if (a.length !== b.length) return false; - for (let i = 0; i < a.length; i++) { - if (a[i] !== b[i]) return false; - } - return true; - } - - regions.forEach(region => { - if (region.stable) { - okBuffer.push(...region.bufferContent); - } else { - if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) { - okBuffer.push(...region.aContent); - } else { - flushOk(); - results.push({ - conflict: { - a: region.aContent, - aIndex: region.aStart, - o: region.oContent, - oIndex: region.oStart, - b: region.bContent, - bIndex: region.bStart - } - }); - } - } - }); - - flushOk(); - return results; - } - - function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) { - discardTags = discardTags || {}; - var _option = 'safe'; // 'safe', 'force_local', 'force_remote' - var _conflicts = []; - - - function user(d) { - return (typeof formatUser === 'function') ? formatUser(d) : d; - } - - - function mergeLocation(remote, target) { - function pointEqual(a, b) { - var epsilon = 1e-6; - return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon); - } - - if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) { - return target; - } - if (_option === 'force_remote') { - return target.update({loc: remote.loc}); - } - - _conflicts.push(_t('merge_remote_changes.conflict.location', { user: user(remote.user) })); - return target; - } - - - function mergeNodes(base, remote, target) { - if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) { - return target; - } - if (_option === 'force_remote') { - return target.update({nodes: remote.nodes}); - } - - var ccount = _conflicts.length; - var o = base.nodes || []; - var a = target.nodes || []; - var b = remote.nodes || []; - var nodes = []; - var hunks = diff3Merge(a, o, b, { excludeFalseConflicts: true }); - - for (var i = 0; i < hunks.length; i++) { - var hunk = hunks[i]; - if (hunk.ok) { - nodes.push.apply(nodes, hunk.ok); - } else { - // for all conflicts, we can assume c.a !== c.b - // because `diff3Merge` called with `true` option to exclude false conflicts.. - var c = hunk.conflict; - if (fastDeepEqual(c.o, c.a)) { // only changed remotely - nodes.push.apply(nodes, c.b); - } else if (fastDeepEqual(c.o, c.b)) { // only changed locally - nodes.push.apply(nodes, c.a); - } else { // changed both locally and remotely - _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) })); - break; - } - } - } - - return (_conflicts.length === ccount) ? target.update({nodes: nodes}) : target; - } - - - function mergeChildren(targetWay, children, updates, graph) { - function isUsed(node, targetWay) { - var hasInterestingParent = graph.parentWays(node) - .some(function(way) { return way.id !== targetWay.id; }); - - return node.hasInterestingTags() || - hasInterestingParent || - graph.parentRelations(node).length > 0; - } - - var ccount = _conflicts.length; - - for (var i = 0; i < children.length; i++) { - var id = children[i]; - var node = graph.hasEntity(id); - - // remove unused childNodes.. - if (targetWay.nodes.indexOf(id) === -1) { - if (node && !isUsed(node, targetWay)) { - updates.removeIds.push(id); - } - continue; - } - - // restore used childNodes.. - var local = localGraph.hasEntity(id); - var remote = remoteGraph.hasEntity(id); - var target; - - if (_option === 'force_remote' && remote && remote.visible) { - updates.replacements.push(remote); - - } else if (_option === 'force_local' && local) { - target = osmEntity(local); - if (remote) { - target = target.update({ version: remote.version }); - } - updates.replacements.push(target); - - } else if (_option === 'safe' && local && remote && local.version !== remote.version) { - target = osmEntity(local, { version: remote.version }); - if (remote.visible) { - target = mergeLocation(remote, target); - } else { - _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) })); - } - - if (_conflicts.length !== ccount) break; - updates.replacements.push(target); - } - } - - return targetWay; - } - - - function updateChildren(updates, graph) { - for (var i = 0; i < updates.replacements.length; i++) { - graph = graph.replace(updates.replacements[i]); - } - if (updates.removeIds.length) { - graph = actionDeleteMultiple(updates.removeIds)(graph); - } - return graph; - } - - - function mergeMembers(remote, target) { - if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) { - return target; - } - if (_option === 'force_remote') { - return target.update({members: remote.members}); - } - - _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) })); - return target; - } - - - function mergeTags(base, remote, target) { - if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) { - return target; - } - if (_option === 'force_remote') { - return target.update({tags: remote.tags}); - } - - var ccount = _conflicts.length; - var o = base.tags || {}; - var a = target.tags || {}; - var b = remote.tags || {}; - var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)) - .filter(function(k) { return !discardTags[k]; }); - var tags = Object.assign({}, a); // shallow copy - var changed = false; - - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - - if (o[k] !== b[k] && a[k] !== b[k]) { // changed remotely.. - if (o[k] !== a[k]) { // changed locally.. - _conflicts.push(_t('merge_remote_changes.conflict.tags', - { tag: k, local: a[k], remote: b[k], user: user(remote.user) })); - - } else { // unchanged locally, accept remote change.. - if (b.hasOwnProperty(k)) { - tags[k] = b[k]; - } else { - delete tags[k]; - } - changed = true; - } - } - } - - return (changed && _conflicts.length === ccount) ? target.update({tags: tags}) : target; - } - - - // `graph.base()` is the common ancestor of the two graphs. - // `localGraph` contains user's edits up to saving - // `remoteGraph` contains remote edits to modified nodes - // `graph` must be a descendent of `localGraph` and may include - // some conflict resolution actions performed on it. - // - // --- ... --- `localGraph` -- ... -- `graph` - // / - // `graph.base()` --- ... --- `remoteGraph` - // - var action = function(graph) { - var updates = { replacements: [], removeIds: [] }; - var base = graph.base().entities[id]; - var local = localGraph.entity(id); - var remote = remoteGraph.entity(id); - var target = osmEntity(local, { version: remote.version }); - - // delete/undelete - if (!remote.visible) { - if (_option === 'force_remote') { - return actionDeleteMultiple([id])(graph); - - } else if (_option === 'force_local') { - if (target.type === 'way') { - target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph); - graph = updateChildren(updates, graph); - } - return graph.replace(target); - - } else { - _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) })); - return graph; // do nothing - } - } - - // merge - if (target.type === 'node') { - target = mergeLocation(remote, target); - - } else if (target.type === 'way') { - // pull in any child nodes that may not be present locally.. - graph.rebase(remoteGraph.childNodes(remote), [graph], false); - target = mergeNodes(base, remote, target); - target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph); - - } else if (target.type === 'relation') { - target = mergeMembers(remote, target); - } - - target = mergeTags(base, remote, target); - - if (!_conflicts.length) { - graph = updateChildren(updates, graph).replace(target); - } - - return graph; - }; - - - action.withOption = function(opt) { - _option = opt; - return action; - }; - - - action.conflicts = function() { - return _conflicts; - }; - - - return action; - } - - // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java - // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as - function actionMove(moveIDs, tryDelta, projection, cache) { - var _delta = tryDelta; - - function setupCache(graph) { - function canMove(nodeID) { - // Allow movement of any node that is in the selectedIDs list.. - if (moveIDs.indexOf(nodeID) !== -1) return true; - - // Allow movement of a vertex where 2 ways meet.. - var parents = graph.parentWays(graph.entity(nodeID)); - if (parents.length < 3) return true; - - // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too.. - var parentsMoving = parents.every(function(way) { return cache.moving[way.id]; }); - if (!parentsMoving) delete cache.moving[nodeID]; - - return parentsMoving; - } - - function cacheEntities(ids) { - for (var i = 0; i < ids.length; i++) { - var id = ids[i]; - if (cache.moving[id]) continue; - cache.moving[id] = true; - - var entity = graph.hasEntity(id); - if (!entity) continue; - - if (entity.type === 'node') { - cache.nodes.push(id); - cache.startLoc[id] = entity.loc; - } else if (entity.type === 'way') { - cache.ways.push(id); - cacheEntities(entity.nodes); - } else { - cacheEntities(entity.members.map(function(member) { - return member.id; - })); - } - } - } - - function cacheIntersections(ids) { - function isEndpoint(way, id) { - return !way.isClosed() && !!way.affix(id); - } - - for (var i = 0; i < ids.length; i++) { - var id = ids[i]; - - // consider only intersections with 1 moved and 1 unmoved way. - var childNodes = graph.childNodes(graph.entity(id)); - for (var j = 0; j < childNodes.length; j++) { - var node = childNodes[j]; - var parents = graph.parentWays(node); - if (parents.length !== 2) continue; - - var moved = graph.entity(id); - var unmoved = null; - for (var k = 0; k < parents.length; k++) { - var way = parents[k]; - if (!cache.moving[way.id]) { - unmoved = way; - break; - } - } - if (!unmoved) continue; - - // exclude ways that are overly connected.. - if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue; - if (moved.isArea() || unmoved.isArea()) continue; - - cache.intersections.push({ - nodeId: node.id, - movedId: moved.id, - unmovedId: unmoved.id, - movedIsEP: isEndpoint(moved, node.id), - unmovedIsEP: isEndpoint(unmoved, node.id) - }); - } - } - } - - - if (!cache) { - cache = {}; - } - if (!cache.ok) { - cache.moving = {}; - cache.intersections = []; - cache.replacedVertex = {}; - cache.startLoc = {}; - cache.nodes = []; - cache.ways = []; - - cacheEntities(moveIDs); - cacheIntersections(cache.ways); - cache.nodes = cache.nodes.filter(canMove); - - cache.ok = true; - } - } - - - // Place a vertex where the moved vertex used to be, to preserve way shape.. - // - // Start: - // b ---- e - // / \ - // / \ - // / \ - // a c - // - // * node '*' added to preserve shape - // / \ - // / b ---- e way `b,e` moved here: - // / \ - // a c - // - // - function replaceMovedVertex(nodeId, wayId, graph, delta) { - var way = graph.entity(wayId); - var moved = graph.entity(nodeId); - var movedIndex = way.nodes.indexOf(nodeId); - var len, prevIndex, nextIndex; - - if (way.isClosed()) { - len = way.nodes.length - 1; - prevIndex = (movedIndex + len - 1) % len; - nextIndex = (movedIndex + len + 1) % len; - } else { - len = way.nodes.length; - prevIndex = movedIndex - 1; - nextIndex = movedIndex + 1; - } - - var prev = graph.hasEntity(way.nodes[prevIndex]); - var next = graph.hasEntity(way.nodes[nextIndex]); - - // Don't add orig vertex at endpoint.. - if (!prev || !next) return graph; - - var key = wayId + '_' + nodeId; - var orig = cache.replacedVertex[key]; - if (!orig) { - orig = osmNode(); - cache.replacedVertex[key] = orig; - cache.startLoc[orig.id] = cache.startLoc[nodeId]; - } - - var start, end; - if (delta) { - start = projection(cache.startLoc[nodeId]); - end = projection.invert(geoVecAdd(start, delta)); - } else { - end = cache.startLoc[nodeId]; - } - orig = orig.move(end); - - var angle = Math.abs(geoAngle(orig, prev, projection) - - geoAngle(orig, next, projection)) * 180 / Math.PI; - - // Don't add orig vertex if it would just make a straight line.. - if (angle > 175 && angle < 185) return graph; - - // moving forward or backward along way? - var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection); - var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection); - var d1 = geoPathLength(p1); - var d2 = geoPathLength(p2); - var insertAt = (d1 <= d2) ? movedIndex : nextIndex; - - // moving around closed loop? - if (way.isClosed() && insertAt === 0) insertAt = len; - - way = way.addNode(orig.id, insertAt); - return graph.replace(orig).replace(way); - } - - - // Remove duplicate vertex that might have been added by - // replaceMovedVertex. This is done after the unzorro checks. - function removeDuplicateVertices(wayId, graph) { - var way = graph.entity(wayId); - var epsilon = 1e-6; - var prev, curr; - - function isInteresting(node, graph) { - return graph.parentWays(node).length > 1 || - graph.parentRelations(node).length || - node.hasInterestingTags(); - } - - for (var i = 0; i < way.nodes.length; i++) { - curr = graph.entity(way.nodes[i]); - - if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) { - if (!isInteresting(prev, graph)) { - way = way.removeNode(prev.id); - graph = graph.replace(way).remove(prev); - } else if (!isInteresting(curr, graph)) { - way = way.removeNode(curr.id); - graph = graph.replace(way).remove(curr); - } - } - - prev = curr; - } - - return graph; - } - - - // Reorder nodes around intersections that have moved.. - // - // Start: way1.nodes: b,e (moving) - // a - b - c ----- d way2.nodes: a,b,c,d (static) - // | vertex: b - // e isEP1: true, isEP2, false - // - // way1 `b,e` moved here: - // a ----- c = b - d - // | - // e - // - // reorder nodes way1.nodes: b,e - // a ----- c - b - d way2.nodes: a,c,b,d - // | - // e - // - function unZorroIntersection(intersection, graph) { - var vertex = graph.entity(intersection.nodeId); - var way1 = graph.entity(intersection.movedId); - var way2 = graph.entity(intersection.unmovedId); - var isEP1 = intersection.movedIsEP; - var isEP2 = intersection.unmovedIsEP; - - // don't move the vertex if it is the endpoint of both ways. - if (isEP1 && isEP2) return graph; - - var nodes1 = graph.childNodes(way1).filter(function(n) { return n !== vertex; }); - var nodes2 = graph.childNodes(way2).filter(function(n) { return n !== vertex; }); - - if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]); - if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]); - - var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection); - var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection); - var loc; - - // snap vertex to nearest edge (or some point between them).. - if (!isEP1 && !isEP2) { - var epsilon = 1e-6, maxIter = 10; - for (var i = 0; i < maxIter; i++) { - loc = geoVecInterp(edge1.loc, edge2.loc, 0.5); - edge1 = geoChooseEdge(nodes1, projection(loc), projection); - edge2 = geoChooseEdge(nodes2, projection(loc), projection); - if (Math.abs(edge1.distance - edge2.distance) < epsilon) break; - } - } else if (!isEP1) { - loc = edge1.loc; - } else { - loc = edge2.loc; - } - - graph = graph.replace(vertex.move(loc)); - - // if zorro happened, reorder nodes.. - if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) { - way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index); - graph = graph.replace(way1); - } - if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) { - way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index); - graph = graph.replace(way2); - } - - return graph; - } - - - function cleanupIntersections(graph) { - for (var i = 0; i < cache.intersections.length; i++) { - var obj = cache.intersections[i]; - graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta); - graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null); - graph = unZorroIntersection(obj, graph); - graph = removeDuplicateVertices(obj.movedId, graph); - graph = removeDuplicateVertices(obj.unmovedId, graph); - } - - return graph; - } - - - // check if moving way endpoint can cross an unmoved way, if so limit delta.. - function limitDelta(graph) { - function moveNode(loc) { - return geoVecAdd(projection(loc), _delta); - } - - for (var i = 0; i < cache.intersections.length; i++) { - var obj = cache.intersections[i]; - - // Don't limit movement if this is vertex joins 2 endpoints.. - if (obj.movedIsEP && obj.unmovedIsEP) continue; - // Don't limit movement if this vertex is not an endpoint anyway.. - if (!obj.movedIsEP) continue; - - var node = graph.entity(obj.nodeId); - var start = projection(node.loc); - var end = geoVecAdd(start, _delta); - var movedNodes = graph.childNodes(graph.entity(obj.movedId)); - var movedPath = movedNodes.map(function(n) { return moveNode(n.loc); }); - var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId)); - var unmovedPath = unmovedNodes.map(function(n) { return projection(n.loc); }); - var hits = geoPathIntersections(movedPath, unmovedPath); - - for (var j = 0; i < hits.length; i++) { - if (geoVecEqual(hits[j], end)) continue; - var edge = geoChooseEdge(unmovedNodes, end, projection); - _delta = geoVecSubtract(projection(edge.loc), start); - } - } - } - - - var action = function(graph) { - if (_delta[0] === 0 && _delta[1] === 0) return graph; - - setupCache(graph); - - if (cache.intersections.length) { - limitDelta(graph); - } - - for (var i = 0; i < cache.nodes.length; i++) { - var node = graph.entity(cache.nodes[i]); - var start = projection(node.loc); - var end = geoVecAdd(start, _delta); - graph = graph.replace(node.move(projection.invert(end))); - } - - if (cache.intersections.length) { - graph = cleanupIntersections(graph); - } - - return graph; - }; - - - action.delta = function() { - return _delta; - }; - - - return action; - } - - function actionMoveMember(relationId, fromIndex, toIndex) { - return function(graph) { - return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex)); - }; - } - - function actionMoveNode(nodeID, toLoc) { - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var node = graph.entity(nodeID); - return graph.replace( - node.move(geoVecInterp(node.loc, toLoc, t)) - ); - }; - - action.transitionable = true; - - return action; - } - - function actionNoop() { - return function(graph) { - return graph; - }; - } - - function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) { - var epsilon = ep || 1e-4; - var threshold = degThresh || 13; // degrees within right or straight to alter - - // We test normalized dot products so we can compare as cos(angle) - var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); - var upperThreshold = Math.cos(threshold * Math.PI / 180); - - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var way = graph.entity(wayID); - way = way.removeNode(''); // sanity check - remove any consecutive duplicates - - if (way.tags.nonsquare) { - var tags = Object.assign({}, way.tags); - // since we're squaring, remove indication that this is physically unsquare - delete tags.nonsquare; - way = way.update({tags: tags}); - } - - graph = graph.replace(way); - - var isClosed = way.isClosed(); - var nodes = graph.childNodes(way).slice(); // shallow copy - if (isClosed) nodes.pop(); - - if (vertexID !== undefined) { - nodes = nodeSubset(nodes, vertexID, isClosed); - if (nodes.length !== 3) return graph; - } - - // note: all geometry functions here use the unclosed node/point/coord list - - var nodeCount = {}; - var points = []; - var corner = { i: 0, dotp: 1 }; - var node, point, loc, score, motions, i, j; - - for (i = 0; i < nodes.length; i++) { - node = nodes[i]; - nodeCount[node.id] = (nodeCount[node.id] || 0) + 1; - points.push({ id: node.id, coord: projection(node.loc) }); - } - - - if (points.length === 3) { // move only one vertex for right triangle - for (i = 0; i < 1000; i++) { - motions = points.map(calcMotion); - - points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]); - score = corner.dotp; - if (score < epsilon) { - break; - } - } - - node = graph.entity(nodes[corner.i].id); - loc = projection.invert(points[corner.i].coord); - graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); - - } else { - var straights = []; - var simplified = []; - - // Remove points from nearly straight sections.. - // This produces a simplified shape to orthogonalize - for (i = 0; i < points.length; i++) { - point = points[i]; - var dotp = 0; - if (isClosed || (i > 0 && i < points.length - 1)) { - var a = points[(i - 1 + points.length) % points.length]; - var b = points[(i + 1) % points.length]; - dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord)); - } - - if (dotp > upperThreshold) { - straights.push(point); - } else { - simplified.push(point); - } - } - - // Orthogonalize the simplified shape - var bestPoints = clonePoints(simplified); - var originalPoints = clonePoints(simplified); - - score = Infinity; - for (i = 0; i < 1000; i++) { - motions = simplified.map(calcMotion); - - for (j = 0; j < motions.length; j++) { - simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]); - } - var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold); - if (newScore < score) { - bestPoints = clonePoints(simplified); - score = newScore; - } - if (score < epsilon) { - break; - } - } - - var bestCoords = bestPoints.map(function(p) { return p.coord; }); - if (isClosed) bestCoords.push(bestCoords[0]); - - // move the nodes that should move - for (i = 0; i < bestPoints.length; i++) { - point = bestPoints[i]; - if (!geoVecEqual(originalPoints[i].coord, point.coord)) { - node = graph.entity(point.id); - loc = projection.invert(point.coord); - graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); - } - } - - // move the nodes along straight segments - for (i = 0; i < straights.length; i++) { - point = straights[i]; - if (nodeCount[point.id] > 1) continue; // skip self-intersections - - node = graph.entity(point.id); - - if (t === 1 && - graph.parentWays(node).length === 1 && - graph.parentRelations(node).length === 0 && - !node.hasInterestingTags() - ) { - // remove uninteresting points.. - graph = actionDeleteNode(node.id)(graph); - - } else { - // move interesting points to the nearest edge.. - var choice = geoVecProject(point.coord, bestCoords); - if (choice) { - loc = projection.invert(choice.target); - graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); - } - } - } - } - - return graph; - - - function clonePoints(array) { - return array.map(function(p) { - return { id: p.id, coord: [p.coord[0], p.coord[1]] }; - }); - } - - - function calcMotion(point, i, array) { - // don't try to move the endpoints of a non-closed way. - if (!isClosed && (i === 0 || i === array.length - 1)) return [0, 0]; - // don't try to move a node that appears more than once (self intersection) - if (nodeCount[array[i].id] > 1) return [0, 0]; - - var a = array[(i - 1 + array.length) % array.length].coord; - var origin = point.coord; - var b = array[(i + 1) % array.length].coord; - var p = geoVecSubtract(a, origin); - var q = geoVecSubtract(b, origin); - - var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q)); - p = geoVecNormalize(p); - q = geoVecNormalize(q); - - var dotp = (p[0] * q[0] + p[1] * q[1]); - var val = Math.abs(dotp); - - if (val < lowerThreshold) { // nearly orthogonal - corner.i = i; - corner.dotp = val; - var vec = geoVecNormalize(geoVecAdd(p, q)); - return geoVecScale(vec, 0.1 * dotp * scale); - } - - return [0, 0]; // do nothing - } - }; - - - // if we are only orthogonalizing one vertex, - // get that vertex and the previous and next - function nodeSubset(nodes, vertexID, isClosed) { - var first = isClosed ? 0 : 1; - var last = isClosed ? nodes.length : nodes.length - 1; - - for (var i = first; i < last; i++) { - if (nodes[i].id === vertexID) { - return [ - nodes[(i - 1 + nodes.length) % nodes.length], - nodes[i], - nodes[(i + 1) % nodes.length] - ]; - } - } - - return []; - } - - - action.disabled = function(graph) { - var way = graph.entity(wayID); - way = way.removeNode(''); // sanity check - remove any consecutive duplicates - graph = graph.replace(way); - - var isClosed = way.isClosed(); - var nodes = graph.childNodes(way).slice(); // shallow copy - if (isClosed) nodes.pop(); - - var allowStraightAngles = false; - if (vertexID !== undefined) { - allowStraightAngles = true; - nodes = nodeSubset(nodes, vertexID, isClosed); - if (nodes.length !== 3) return 'end_vertex'; - } - - var coords = nodes.map(function(n) { return projection(n.loc); }); - var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles); - - if (score === null) { - return 'not_squarish'; - } else if (score === 0) { - return 'square_enough'; - } else { - return false; - } - }; - - - action.transitionable = true; - - return action; - } - - // `actionRestrictTurn` creates a turn restriction relation. - // - // `turn` must be an `osmTurn` object - // see osm/intersection.js, pathToTurn() - // - // This specifies a restriction of type `restriction` when traveling from - // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`. - // (The action does not check that these entities form a valid intersection.) - // - // From, to, and via ways should be split before calling this action. - // (old versions of the code would split the ways here, but we no longer do it) - // - // For testing convenience, accepts a restrictionID to assign to the new - // relation. Normally, this will be undefined and the relation will - // automatically be assigned a new ID. - // - function actionRestrictTurn(turn, restrictionType, restrictionID) { - - return function(graph) { - var fromWay = graph.entity(turn.from.way); - var toWay = graph.entity(turn.to.way); - var viaNode = turn.via.node && graph.entity(turn.via.node); - var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); }); - var members = []; - - members.push({ id: fromWay.id, type: 'way', role: 'from' }); - - if (viaNode) { - members.push({ id: viaNode.id, type: 'node', role: 'via' }); - } else if (viaWays) { - viaWays.forEach(function(viaWay) { - members.push({ id: viaWay.id, type: 'way', role: 'via' }); - }); - } - - members.push({ id: toWay.id, type: 'way', role: 'to' }); - - return graph.replace(osmRelation({ - id: restrictionID, - tags: { - type: 'restriction', - restriction: restrictionType - }, - members: members - })); - }; - } - - function actionRevert(id) { - var action = function(graph) { - var entity = graph.hasEntity(id), - base = graph.base().entities[id]; - - if (entity && !base) { // entity will be removed.. - if (entity.type === 'node') { - graph.parentWays(entity) - .forEach(function(parent) { - parent = parent.removeNode(id); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteWay(parent.id)(graph); - } - }); - } - - graph.parentRelations(entity) - .forEach(function(parent) { - parent = parent.removeMembersWithID(id); - graph = graph.replace(parent); - - if (parent.isDegenerate()) { - graph = actionDeleteRelation(parent.id)(graph); - } - }); - } - - return graph.revert(id); - }; - - return action; - } - - function actionRotate(rotateIds, pivot, angle, projection) { - - var action = function(graph) { - return graph.update(function(graph) { - utilGetAllNodes(rotateIds, graph).forEach(function(node) { - var point = geoRotate([projection(node.loc)], angle, pivot)[0]; - graph = graph.replace(node.move(projection.invert(point))); - }); - }); - }; - - return action; - } - - /* Align nodes along their common axis */ - function actionStraightenNodes(nodeIDs, projection) { - - function positionAlongWay(a, o, b) { - return geoVecDot(a, b, o) / geoVecDot(b, b, o); - } - - // returns the endpoints of the long axis of symmetry of the `points` bounding rect - function getEndpoints(points) { - var ssr = geoGetSmallestSurroundingRectangle(points); - - // Choose line pq = axis of symmetry. - // The shape's surrounding rectangle has 2 axes of symmetry. - // Snap points to the long axis - var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ]; - var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ]; - var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ]; - var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ]; - - var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2)); - if (isLong) { - return [p1, q1]; - } - return [p2, q2]; - } - - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var nodes = nodeIDs.map(function(id) { return graph.entity(id); }); - var points = nodes.map(function(n) { return projection(n.loc); }); - var endpoints = getEndpoints(points); - var startPoint = endpoints[0]; - var endPoint = endpoints[1]; - - // Move points onto the line connecting the endpoints - for (var i = 0; i < points.length; i++) { - var node = nodes[i]; - var point = points[i]; - var u = positionAlongWay(point, startPoint, endPoint); - var point2 = geoVecInterp(startPoint, endPoint, u); - var loc2 = projection.invert(point2); - graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t))); - } - - return graph; - }; - - - action.disabled = function(graph) { - - var nodes = nodeIDs.map(function(id) { return graph.entity(id); }); - var points = nodes.map(function(n) { return projection(n.loc); }); - var endpoints = getEndpoints(points); - var startPoint = endpoints[0]; - var endPoint = endpoints[1]; - - var maxDistance = 0; - - for (var i = 0; i < points.length; i++) { - var point = points[i]; - var u = positionAlongWay(point, startPoint, endPoint); - var p = geoVecInterp(startPoint, endPoint, u); - var dist = geoVecLength(p, point); - - if (!isNaN(dist) && dist > maxDistance) { - maxDistance = dist; - } - } - - if (maxDistance < 0.0001) { - return 'straight_enough'; - } - }; - - - action.transitionable = true; - - - return action; - } - - /* - * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as - */ - function actionStraightenWay(selectedIDs, projection) { - - function positionAlongWay(a, o, b) { - return geoVecDot(a, b, o) / geoVecDot(b, b, o); - } - - // Return all selected ways as a continuous, ordered array of nodes - function allNodes(graph) { - var nodes = []; - var startNodes = []; - var endNodes = []; - var remainingWays = []; - var selectedWays = selectedIDs.filter(function(w) { - return graph.entity(w).type === 'way'; - }); - var selectedNodes = selectedIDs.filter(function(n) { - return graph.entity(n).type === 'node'; - }); - - for (var i = 0; i < selectedWays.length; i++) { - var way = graph.entity(selectedWays[i]); - nodes = way.nodes.slice(0); - remainingWays.push(nodes); - startNodes.push(nodes[0]); - endNodes.push(nodes[nodes.length-1]); - } - - // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end, - // and need to be removed so currNode difference calculation below works) - // i.e. ["n-1", "n-1", "n-2"] => ["n-2"] - startNodes = startNodes.filter(function(n) { - return startNodes.indexOf(n) === startNodes.lastIndexOf(n); - }); - endNodes = endNodes.filter(function(n) { - return endNodes.indexOf(n) === endNodes.lastIndexOf(n); - }); - - // Choose the initial endpoint to start from - var currNode = utilArrayDifference(startNodes, endNodes) - .concat(utilArrayDifference(endNodes, startNodes))[0]; - var nextWay = []; - nodes = []; - - // Create nested function outside of loop to avoid "function in loop" lint error - var getNextWay = function(currNode, remainingWays) { - return remainingWays.filter(function(way) { - return way[0] === currNode || way[way.length-1] === currNode; - })[0]; - }; - - // Add nodes to end of nodes array, until all ways are added - while (remainingWays.length) { - nextWay = getNextWay(currNode, remainingWays); - remainingWays = utilArrayDifference(remainingWays, [nextWay]); - - if (nextWay[0] !== currNode) { - nextWay.reverse(); - } - nodes = nodes.concat(nextWay); - currNode = nodes[nodes.length-1]; - } - - // If user selected 2 nodes to straighten between, then slice nodes array to those nodes - if (selectedNodes.length === 2) { - var startNodeIdx = nodes.indexOf(selectedNodes[0]); - var endNodeIdx = nodes.indexOf(selectedNodes[1]); - var sortedStartEnd = [startNodeIdx, endNodeIdx]; - - sortedStartEnd.sort(function(a, b) { return a - b; }); - nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1]+1); - } - - return nodes.map(function(n) { return graph.entity(n); }); - } - - function shouldKeepNode(node, graph) { - return graph.parentWays(node).length > 1 || - graph.parentRelations(node).length || - node.hasInterestingTags(); - } - - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var nodes = allNodes(graph); - var points = nodes.map(function(n) { return projection(n.loc); }); - var startPoint = points[0]; - var endPoint = points[points.length-1]; - var toDelete = []; - var i; - - for (i = 1; i < points.length-1; i++) { - var node = nodes[i]; - var point = points[i]; - - if (t < 1 || shouldKeepNode(node, graph)) { - var u = positionAlongWay(point, startPoint, endPoint); - var p = geoVecInterp(startPoint, endPoint, u); - var loc2 = projection.invert(p); - graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t))); - - } else { - // safe to delete - if (toDelete.indexOf(node) === -1) { - toDelete.push(node); - } - } - } - - for (i = 0; i < toDelete.length; i++) { - graph = actionDeleteNode(toDelete[i].id)(graph); - } - - return graph; - }; - - - action.disabled = function(graph) { - // check way isn't too bendy - var nodes = allNodes(graph); - var points = nodes.map(function(n) { return projection(n.loc); }); - var startPoint = points[0]; - var endPoint = points[points.length-1]; - var threshold = 0.2 * geoVecLength(startPoint, endPoint); - var i; - - if (threshold === 0) { - return 'too_bendy'; - } - - var maxDistance = 0; - - for (i = 1; i < points.length - 1; i++) { - var point = points[i]; - var u = positionAlongWay(point, startPoint, endPoint); - var p = geoVecInterp(startPoint, endPoint, u); - var dist = geoVecLength(p, point); - - // to bendy if point is off by 20% of total start/end distance in projected space - if (isNaN(dist) || dist > threshold) { - return 'too_bendy'; - } else if (dist > maxDistance) { - maxDistance = dist; - } - } - - var keepingAllNodes = nodes.every(function(node, i) { - return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph); - }); - - if (maxDistance < 0.0001 && - // Allow straightening even if already straight in order to remove extraneous nodes - keepingAllNodes) { - return 'straight_enough'; - } - }; - - action.transitionable = true; - - - return action; - } - - // `actionUnrestrictTurn` deletes a turn restriction relation. - // - // `turn` must be an `osmTurn` object with a `restrictionID` property. - // see osm/intersection.js, pathToTurn() - // - function actionUnrestrictTurn(turn) { - return function(graph) { - return actionDeleteRelation(turn.restrictionID)(graph); - }; - } - - /* Reflect the given area around its axis of symmetry */ - function actionReflect(reflectIds, projection) { - var _useLongAxis = true; - - - var action = function(graph, t) { - if (t === null || !isFinite(t)) t = 1; - t = Math.min(Math.max(+t, 0), 1); - - var nodes = utilGetAllNodes(reflectIds, graph); - var points = nodes.map(function(n) { return projection(n.loc); }); - var ssr = geoGetSmallestSurroundingRectangle(points); - - // Choose line pq = axis of symmetry. - // The shape's surrounding rectangle has 2 axes of symmetry. - // Reflect across the longer axis by default. - var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ]; - var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ]; - var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ]; - var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ]; - var p, q; - - var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2)); - if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) { - p = p1; - q = q1; - } else { - p = p2; - q = q2; - } - - // reflect c across pq - // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line - var dx = q[0] - p[0]; - var dy = q[1] - p[1]; - var a = (dx * dx - dy * dy) / (dx * dx + dy * dy); - var b = 2 * dx * dy / (dx * dx + dy * dy); - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var c = projection(node.loc); - var c2 = [ - a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0], - b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1] - ]; - var loc2 = projection.invert(c2); - node = node.move(geoVecInterp(node.loc, loc2, t)); - graph = graph.replace(node); - } - - return graph; - }; - - - action.useLongAxis = function(val) { - if (!arguments.length) return _useLongAxis; - _useLongAxis = val; - return action; - }; - - - action.transitionable = true; - - - return action; - } - - function actionUpgradeTags(entityId, oldTags, replaceTags) { - - return function(graph) { - var entity = graph.entity(entityId); - var tags = Object.assign({}, entity.tags); // shallow copy - var transferValue; - var semiIndex; - - for (var oldTagKey in oldTags) { - if (oldTags[oldTagKey] === '*') { - transferValue = tags[oldTagKey]; - delete tags[oldTagKey]; - } else { - var vals = tags[oldTagKey].split(';').filter(Boolean); - var oldIndex = vals.indexOf(oldTags[oldTagKey]); - if (vals.length === 1 || oldIndex === -1) { - delete tags[oldTagKey]; - } else { - if (replaceTags && replaceTags[oldTagKey]) { - // replacing a value within a semicolon-delimited value, note the index - semiIndex = oldIndex; - } - vals.splice(oldIndex, 1); - tags[oldTagKey] = vals.join(';'); - } - } - } - - if (replaceTags) { - for (var replaceKey in replaceTags) { - var replaceValue = replaceTags[replaceKey]; - if (replaceValue === '*') { - if (tags[replaceKey] && tags[replaceKey] !== 'no') { - // allow any pre-existing value except `no` (troll tag) - continue; - } else { - // otherwise assume `yes` is okay - tags[replaceKey] = 'yes'; - } - } else if (replaceValue === '$1') { - tags[replaceKey] = transferValue; - } else { - if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) { - // don't override preexisting values - var existingVals = tags[replaceKey].split(';').filter(Boolean); - if (existingVals.indexOf(replaceValue) === -1) { - existingVals.splice(semiIndex, 0, replaceValue); - tags[replaceKey] = existingVals.join(';'); - } - } else { - tags[replaceKey] = replaceValue; - } - } - } - } - - return graph.replace(entity.update({ tags: tags })); - }; - } - - function behaviorEdit(context) { - - function behavior() { - context.map() - .minzoom(context.minEditableZoom()); - } - - - behavior.off = function() { - context.map() - .minzoom(0); - }; - - return behavior; - } - - /* - The hover behavior adds the `.hover` class on pointerover to all elements to which - the identical datum is bound, and removes it on pointerout. - - The :hover pseudo-class is insufficient for iD's purposes because a datum's visual - representation may consist of several elements scattered throughout the DOM hierarchy. - Only one of these elements can have the :hover pseudo-class, but all of them will - have the .hover class. - */ - function behaviorHover(context) { - var dispatch$1 = dispatch('hover'); - var _selection = select(null); - var _newNodeId = null; - var _initialNodeID = null; - var _altDisables; - var _ignoreVertex; - var _targets = []; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - - function keydown() { - if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) { - _selection.selectAll('.hover') - .classed('hover-suppressed', true) - .classed('hover', false); - - _selection - .classed('hover-disabled', true); - - dispatch$1.call('hover', this, null); - } - } - - - function keyup() { - if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) { - _selection.selectAll('.hover-suppressed') - .classed('hover-suppressed', false) - .classed('hover', true); - - _selection - .classed('hover-disabled', false); - - dispatch$1.call('hover', this, _targets); - } - } - - - function behavior(selection) { - _selection = selection; - - _targets = []; - - if (_initialNodeID) { - _newNodeId = _initialNodeID; - _initialNodeID = null; - } else { - _newNodeId = null; - } - - _selection - .on(_pointerPrefix + 'over.hover', pointerover) - .on(_pointerPrefix + 'out.hover', pointerout) - // treat pointerdown as pointerover for touch devices - .on(_pointerPrefix + 'down.hover', pointerover); - - select(window) - .on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true) - .on('keydown.hover', keydown) - .on('keyup.hover', keyup); - - - function eventTarget() { - var datum = event.target && event.target.__data__; - if (typeof datum !== 'object') return null; - if (!(datum instanceof osmEntity) && datum.properties && (datum.properties.entity instanceof osmEntity)) { - return datum.properties.entity; - } - return datum; - } - - function pointerover() { - // ignore mouse hovers with buttons pressed unless dragging - if (context.mode().id.indexOf('drag') === -1 && - (!event.pointerType || event.pointerType === 'mouse') && - event.buttons) return; - - var target = eventTarget(); - if (target && _targets.indexOf(target) === -1) { - _targets.push(target); - updateHover(_targets); - } - } - - function pointerout() { - - var target = eventTarget(); - var index = _targets.indexOf(target); - if (index !== -1) { - _targets.splice(index); - updateHover(_targets); - } - } - - function allowsVertex(d) { - return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); - } - - function modeAllowsHover(target) { - var mode = context.mode(); - if (mode.id === 'add-point') { - return mode.preset.matchGeometry('vertex') || - (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex'); - } - return true; - } - - function updateHover(targets) { - - _selection.selectAll('.hover') - .classed('hover', false); - _selection.selectAll('.hover-suppressed') - .classed('hover-suppressed', false); - - var mode = context.mode(); - - if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) { - var node = targets.find(function(target) { - return target instanceof osmEntity && target.type === 'node'; - }); - _newNodeId = node && node.id; - } - - targets = targets.filter(function(datum) { - if (datum instanceof osmEntity) { - // If drawing a way, don't hover on a node that was just placed. #3974 - return datum.id !== _newNodeId && - (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && - modeAllowsHover(datum); - } - return true; - }); - - var selector = ''; - - for (var i in targets) { - var datum = targets[i]; - - // What are we hovering over? - if (datum.__featurehash__) { - // hovering custom data - selector += ', .data' + datum.__featurehash__; - - } else if (datum instanceof QAItem) { - selector += ', .' + datum.service + '.itemId-' + datum.id; - - } else if (datum instanceof osmNote) { - selector += ', .note-' + datum.id; - - } else if (datum instanceof osmEntity) { - selector += ', .' + datum.id; - if (datum.type === 'relation') { - for (var j in datum.members) { - selector += ', .' + datum.members[j].id; - } - } - } - } - - var suppressed = _altDisables && event && event.altKey; - - if (selector.trim().length) { - // remove the first comma - selector = selector.slice(1); - _selection.selectAll(selector) - .classed(suppressed ? 'hover-suppressed' : 'hover', true); - } - - dispatch$1.call('hover', this, !suppressed && targets); - } - } - - - behavior.off = function(selection) { - selection.selectAll('.hover') - .classed('hover', false); - selection.selectAll('.hover-suppressed') - .classed('hover-suppressed', false); - selection - .classed('hover-disabled', false); - - selection - .on(_pointerPrefix + 'over.hover', null) - .on(_pointerPrefix + 'out.hover', null) - .on(_pointerPrefix + 'down.hover', null); - - select(window) - .on(_pointerPrefix + 'up.hover pointercancel.hover', null, true) - .on('keydown.hover', null) - .on('keyup.hover', null); - }; - - - behavior.altDisables = function(val) { - if (!arguments.length) return _altDisables; - _altDisables = val; - return behavior; - }; - - behavior.ignoreVertex = function(val) { - if (!arguments.length) return _ignoreVertex; - _ignoreVertex = val; - return behavior; - }; - - behavior.initialNodeID = function(nodeId) { - _initialNodeID = nodeId; - return behavior; - }; - - return utilRebind(behavior, dispatch$1, 'on'); - } - - var _disableSpace = false; - var _lastSpace = null; - - - function behaviorDraw(context) { - var dispatch$1 = dispatch( - 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish' - ); - - var keybinding = utilKeybinding('draw'); - - var _hover = behaviorHover(context) - .altDisables(true) - .ignoreVertex(true) - .on('hover', context.ui().sidebar.hover); - var _edit = behaviorEdit(context); - - var _closeTolerance = 4; - var _tolerance = 12; - var _mouseLeave = false; - var _lastMouse = null; - var _lastPointerUpEvent; - - var _downPointer; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - - // related code - // - `mode/drag_node.js` `datum()` - function datum() { - var mode = context.mode(); - var isNote = mode && (mode.id.indexOf('note') !== -1); - if (event.altKey || isNote) return {}; - - var element; - if (event.type === 'keydown') { - element = _lastMouse && _lastMouse.target; - } else { - element = event.target; - } - - // When drawing, snap only to touch targets.. - // (this excludes area fills and active drawing elements) - var d = element.__data__; - return (d && d.properties && d.properties.target) ? d : {}; - } - - function pointerdown() { - - if (_downPointer) return; - - var pointerLocGetter = utilFastMouse(this); - _downPointer = { - id: event.pointerId || 'mouse', - pointerLocGetter: pointerLocGetter, - downTime: +new Date(), - downLoc: pointerLocGetter(event) - }; - - dispatch$1.call('down', this, datum()); - } - - function pointerup() { - - if (!_downPointer || _downPointer.id !== (event.pointerId || 'mouse')) return; - - var downPointer = _downPointer; - _downPointer = null; - - _lastPointerUpEvent = event; - - if (downPointer.isCancelled) return; - - var t2 = +new Date(); - var p2 = downPointer.pointerLocGetter(event); - var dist = geoVecLength(downPointer.downLoc, p2); - - if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) { - // Prevent a quick second click - select(window).on('click.draw-block', function() { - event.stopPropagation(); - }, true); - - context.map().dblclickZoomEnable(false); - - window.setTimeout(function() { - context.map().dblclickZoomEnable(true); - select(window).on('click.draw-block', null); - }, 500); - - click(p2); - } - } - - function pointermove() { - if (_downPointer && - _downPointer.id === (event.pointerId || 'mouse') && - !_downPointer.isCancelled) { - var p2 = _downPointer.pointerLocGetter(event); - var dist = geoVecLength(_downPointer.downLoc, p2); - if (dist >= _closeTolerance) { - _downPointer.isCancelled = true; - dispatch$1.call('downcancel', this); - } - } - - if ((event.pointerType && event.pointerType !== 'mouse') || - event.buttons || - _downPointer) return; - - // HACK: Mobile Safari likes to send one or more `mouse` type pointermove - // events immediately after non-mouse pointerup events; detect and ignore them. - if (_lastPointerUpEvent && - _lastPointerUpEvent.pointerType !== 'mouse' && - event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return; - - _lastMouse = event; - dispatch$1.call('move', this, datum()); - } - - function pointercancel() { - if (_downPointer && - _downPointer.id === (event.pointerId || 'mouse')) { - - if (!_downPointer.isCancelled) { - dispatch$1.call('downcancel', this); - } - _downPointer = null; - } - } - - function mouseenter() { - _mouseLeave = false; - } - - function mouseleave() { - _mouseLeave = true; - } - - function allowsVertex(d) { - return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); - } - - // related code - // - `mode/drag_node.js` `doMove()` - // - `behavior/draw.js` `click()` - // - `behavior/draw_way.js` `move()` - function click(loc) { - var d = datum(); - var target = d && d.properties && d.properties.entity; - - var mode = context.mode(); - - if (target && target.type === 'node' && allowsVertex(target)) { // Snap to a node - dispatch$1.call('clickNode', this, target, d); - return; - - } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) { // Snap to a way - var choice = geoChooseEdge( - context.graph().childNodes(target), loc, context.projection, context.activeID() - ); - if (choice) { - var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]]; - dispatch$1.call('clickWay', this, choice.loc, edge, d); - return; - } - } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) { - var locLatLng = context.projection.invert(loc); - dispatch$1.call('click', this, locLatLng, d); - } - - } - - // treat a spacebar press like a click - function space() { - event.preventDefault(); - event.stopPropagation(); - - var currSpace = context.map().mouse(); - if (_disableSpace && _lastSpace) { - var dist = geoVecLength(_lastSpace, currSpace); - if (dist > _tolerance) { - _disableSpace = false; - } - } - - if (_disableSpace || _mouseLeave || !_lastMouse) return; - - // user must move mouse or release space bar to allow another click - _lastSpace = currSpace; - _disableSpace = true; - - select(window).on('keyup.space-block', function() { - event.preventDefault(); - event.stopPropagation(); - _disableSpace = false; - select(window).on('keyup.space-block', null); - }); - - // get the current mouse position - var loc = context.map().mouse() || - // or the map center if the mouse has never entered the map - context.projection(context.map().center()); - click(loc); - } - - - function backspace() { - event.preventDefault(); - dispatch$1.call('undo'); - } - - - function del() { - event.preventDefault(); - dispatch$1.call('cancel'); - } - - - function ret() { - event.preventDefault(); - dispatch$1.call('finish'); - } - - - function behavior(selection) { - context.install(_hover); - context.install(_edit); - - _downPointer = null; - - keybinding - .on('⌫', backspace) - .on('⌦', del) - .on('⎋', ret) - .on('↩', ret) - .on('space', space) - .on('⌥space', space); - - selection - .on('mouseenter.draw', mouseenter) - .on('mouseleave.draw', mouseleave) - .on(_pointerPrefix + 'down.draw', pointerdown) - .on(_pointerPrefix + 'move.draw', pointermove); - - select(window) - .on(_pointerPrefix + 'up.draw', pointerup, true) - .on('pointercancel.draw', pointercancel, true); - - select(document) - .call(keybinding); - - return behavior; - } - - - behavior.off = function(selection) { - context.ui().sidebar.hover.cancel(); - context.uninstall(_hover); - context.uninstall(_edit); - - selection - .on('mouseenter.draw', null) - .on('mouseleave.draw', null) - .on(_pointerPrefix + 'down.draw', null) - .on(_pointerPrefix + 'move.draw', null); - - select(window) - .on(_pointerPrefix + 'up.draw', null) - .on('pointercancel.draw', null); - // note: keyup.space-block, click.draw-block should remain - - select(document) - .call(keybinding.unbind); - }; - - - behavior.hover = function() { - return _hover; - }; - - - return utilRebind(behavior, dispatch$1, 'on'); - } - - function initRange(domain, range) { - switch (arguments.length) { - case 0: break; - case 1: this.range(domain); break; - default: this.range(range).domain(domain); break; - } - return this; - } - - var prefix = "$"; - - function Map$1() {} - - Map$1.prototype = map$2.prototype = { - constructor: Map$1, - has: function(key) { - return (prefix + key) in this; - }, - get: function(key) { - return this[prefix + key]; - }, - set: function(key, value) { - this[prefix + key] = value; - return this; - }, - remove: function(key) { - var property = prefix + key; - return property in this && delete this[property]; - }, - clear: function() { - for (var property in this) if (property[0] === prefix) delete this[property]; - }, - keys: function() { - var keys = []; - for (var property in this) if (property[0] === prefix) keys.push(property.slice(1)); - return keys; - }, - values: function() { - var values = []; - for (var property in this) if (property[0] === prefix) values.push(this[property]); - return values; - }, - entries: function() { - var entries = []; - for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]}); - return entries; - }, - size: function() { - var size = 0; - for (var property in this) if (property[0] === prefix) ++size; - return size; - }, - empty: function() { - for (var property in this) if (property[0] === prefix) return false; - return true; - }, - each: function(f) { - for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this); - } - }; - - function map$2(object, f) { - var map = new Map$1; - - // Copy constructor. - if (object instanceof Map$1) object.each(function(value, key) { map.set(key, value); }); - - // Index array by numeric index or specified key function. - else if (Array.isArray(object)) { - var i = -1, - n = object.length, - o; - - if (f == null) while (++i < n) map.set(i, object[i]); - else while (++i < n) map.set(f(o = object[i], i, object), o); - } - - // Convert object to map. - else if (object) for (var key in object) map.set(key, object[key]); - - return map; - } - - function Set$1() {} - - var proto = map$2.prototype; - - Set$1.prototype = set$2.prototype = { - constructor: Set$1, - has: proto.has, - add: function(value) { - value += ""; - this[prefix + value] = value; - return this; - }, - remove: proto.remove, - clear: proto.clear, - values: proto.keys, - size: proto.size, - empty: proto.empty, - each: proto.each - }; - - function set$2(object, f) { - var set = new Set$1; - - // Copy constructor. - if (object instanceof Set$1) object.each(function(value) { set.add(value); }); - - // Otherwise, assume it’s an array. - else if (object) { - var i = -1, n = object.length; - if (f == null) while (++i < n) set.add(object[i]); - else while (++i < n) set.add(f(object[i], i, object)); - } - - return set; - } - - var array$1 = Array.prototype; - - var map$3 = array$1.map; - var slice$4 = array$1.slice; - - function constant$4(x) { - return function() { - return x; - }; - } - - function number$1(x) { - return +x; - } - - var unit = [0, 1]; - - function identity$3(x) { - return x; - } - - function normalize(a, b) { - return (b -= (a = +a)) - ? function(x) { return (x - a) / b; } - : constant$4(isNaN(b) ? NaN : 0.5); - } - - function clamper(domain) { - var a = domain[0], b = domain[domain.length - 1], t; - if (a > b) t = a, a = b, b = t; - return function(x) { return Math.max(a, Math.min(b, x)); }; - } - - // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. - // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b]. - function bimap(domain, range, interpolate) { - var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1]; - if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0); - else d0 = normalize(d0, d1), r0 = interpolate(r0, r1); - return function(x) { return r0(d0(x)); }; - } - - function polymap(domain, range, interpolate) { - var j = Math.min(domain.length, range.length) - 1, - d = new Array(j), - r = new Array(j), - i = -1; - - // Reverse descending domains. - if (domain[j] < domain[0]) { - domain = domain.slice().reverse(); - range = range.slice().reverse(); - } - - while (++i < j) { - d[i] = normalize(domain[i], domain[i + 1]); - r[i] = interpolate(range[i], range[i + 1]); - } - - return function(x) { - var i = bisectRight(domain, x, 1, j) - 1; - return r[i](d[i](x)); - }; - } - - function copy$1(source, target) { - return target - .domain(source.domain()) - .range(source.range()) - .interpolate(source.interpolate()) - .clamp(source.clamp()) - .unknown(source.unknown()); - } - - function transformer$1() { - var domain = unit, - range = unit, - interpolate$1 = interpolate, - transform, - untransform, - unknown, - clamp = identity$3, - piecewise, - output, - input; - - function rescale() { - piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap; - output = input = null; - return scale; - } - - function scale(x) { - return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x))); - } - - scale.invert = function(y) { - return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y))); - }; - - scale.domain = function(_) { - return arguments.length ? (domain = map$3.call(_, number$1), clamp === identity$3 || (clamp = clamper(domain)), rescale()) : domain.slice(); - }; - - scale.range = function(_) { - return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice(); - }; - - scale.rangeRound = function(_) { - return range = slice$4.call(_), interpolate$1 = interpolateRound, rescale(); - }; - - scale.clamp = function(_) { - return arguments.length ? (clamp = _ ? clamper(domain) : identity$3, scale) : clamp !== identity$3; - }; - - scale.interpolate = function(_) { - return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1; - }; - - scale.unknown = function(_) { - return arguments.length ? (unknown = _, scale) : unknown; - }; - - return function(t, u) { - transform = t, untransform = u; - return rescale(); - }; - } - - function continuous(transform, untransform) { - return transformer$1()(transform, untransform); - } - - // 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, formatDecimal(1.23) returns ["123", 0]. - function formatDecimal(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 = formatDecimal(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; - this.precision = specifier.precision === undefined ? undefined : +specifier.precision; - this.trim = !!specifier.trim; - this.type = specifier.type === undefined ? "" : specifier.type + ""; - } - - FormatSpecifier.prototype.toString = function() { - return this.fill - + this.align - + this.sign - + this.symbol - + (this.zero ? "0" : "") - + (this.width === undefined ? "" : Math.max(1, this.width | 0)) - + (this.comma ? "," : "") - + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0)) - + (this.trim ? "~" : "") - + this.type; - }; - - // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k. - function formatTrim(s) { - out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) { - switch (s[i]) { - case ".": i0 = i1 = i; break; - case "0": if (i0 === 0) i0 = i; i1 = i; break; - default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break; - } - } - return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s; - } - - var prefixExponent; - - function formatPrefixAuto(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1], - i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, - n = coefficient.length; - return i === n ? coefficient - : i > n ? coefficient + new Array(i - n + 1).join("0") - : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) - : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! - } - - function formatRounded(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1]; - return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient - : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) - : coefficient + new Array(exponent - coefficient.length + 2).join("0"); - } - - var formatTypes = { - "%": function(x, p) { return (x * 100).toFixed(p); }, - "b": function(x) { return Math.round(x).toString(2); }, - "c": function(x) { return x + ""; }, - "d": function(x) { return Math.round(x).toString(10); }, - "e": function(x, p) { return x.toExponential(p); }, - "f": function(x, p) { return x.toFixed(p); }, - "g": function(x, p) { return x.toPrecision(p); }, - "o": function(x) { return Math.round(x).toString(8); }, - "p": function(x, p) { return formatRounded(x * 100, p); }, - "r": formatRounded, - "s": formatPrefixAuto, - "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, - "x": function(x) { return Math.round(x).toString(16); } - }; - - function identity$4(x) { - return x; - } - - var map$4 = Array.prototype.map, - prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"]; - - function formatLocale(locale) { - var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$4.call(locale.grouping, Number), locale.thousands + ""), - currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "", - currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "", - decimal = locale.decimal === undefined ? "." : locale.decimal + "", - numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$4.call(locale.numerals, String)), - percent = locale.percent === undefined ? "%" : locale.percent + "", - minus = locale.minus === undefined ? "-" : locale.minus + "", - nan = locale.nan === undefined ? "NaN" : locale.nan + ""; - - function newFormat(specifier) { - specifier = formatSpecifier(specifier); - - var fill = specifier.fill, - align = specifier.align, - sign = specifier.sign, - symbol = specifier.symbol, - zero = specifier.zero, - width = specifier.width, - comma = specifier.comma, - precision = specifier.precision, - trim = specifier.trim, - type = specifier.type; - - // The "n" type is an alias for ",g". - if (type === "n") comma = true, type = "g"; - - // The "" type, and any invalid type, is an alias for ".12~g". - else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; - - // If zero fill is specified, padding goes after sign and before digits. - if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; - - // Compute the prefix and suffix. - // For SI-prefix, the suffix is lazily computed. - var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", - suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; - - // What format function should we use? - // Is this an integer type? - // Can this type generate exponential notation? - var formatType = formatTypes[type], - maybeSuffix = /[defgprs%]/.test(type); - - // Set the default precision if not specified, - // or clamp the specified precision to the supported range. - // For significant precision, it must be in [1, 21]. - // For fixed precision, it must be in [0, 20]. - precision = precision === undefined ? 6 - : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) - : Math.max(0, Math.min(20, precision)); - - function format(value) { - var valuePrefix = prefix, - valueSuffix = suffix, - i, n, c; - - if (type === "c") { - valueSuffix = formatType(value) + valueSuffix; - value = ""; - } else { - value = +value; - - // Determine the sign. -0 is not less than 0, but 1 / -0 is! - var valueNegative = value < 0 || 1 / value < 0; - - // Perform the initial formatting. - value = isNaN(value) ? nan : formatType(Math.abs(value), precision); - - // Trim insignificant zeros. - if (trim) value = formatTrim(value); - - // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign. - if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; - - // Compute the prefix and suffix. - valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); - - // Break the formatted value into the integer “value” part that can be - // grouped, and fractional or exponential “suffix” part that is not. - if (maybeSuffix) { - i = -1, n = value.length; - while (++i < n) { - if (c = value.charCodeAt(i), 48 > c || c > 57) { - valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; - value = value.slice(0, i); - break; - } - } - } - } - - // If the fill character is not "0", grouping is applied before padding. - if (comma && !zero) value = group(value, Infinity); - - // Compute the padding. - var length = valuePrefix.length + value.length + valueSuffix.length, - padding = length < width ? new Array(width - length + 1).join(fill) : ""; - - // If the fill character is "0", grouping is applied after padding. - if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; - - // Reconstruct the final output based on the desired alignment. - switch (align) { - case "<": value = valuePrefix + value + valueSuffix + padding; break; - case "=": value = valuePrefix + padding + value + valueSuffix; break; - case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; - default: value = padding + valuePrefix + value + valueSuffix; break; - } - - return numerals(value); - } - - format.toString = function() { - return specifier + ""; - }; - - return format; - } - - function formatPrefix(specifier, value) { - var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), - e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, - k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3]; - return function(value) { - return f(k * value) + prefix; - }; - } - - return { - format: newFormat, - formatPrefix: formatPrefix - }; - } - - var locale; - var format; - var formatPrefix; - - defaultLocale({ - decimal: ".", - thousands: ",", - grouping: [3], - currency: ["$", ""], - minus: "-" - }); - - function defaultLocale(definition) { - locale = formatLocale(definition); - format = locale.format; - formatPrefix = locale.formatPrefix; - return locale; - } - - function precisionFixed(step) { - return Math.max(0, -exponent(Math.abs(step))); - } - - function precisionPrefix(step, value) { - return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step))); - } - - function precisionRound(step, max) { - step = Math.abs(step), max = Math.abs(max) - step; - return Math.max(0, exponent(max) - exponent(step)) + 1; - } - - function tickFormat(start, stop, count, specifier) { - var step = tickStep(start, stop, count), - precision; - specifier = formatSpecifier(specifier == null ? ",f" : specifier); - switch (specifier.type) { - case "s": { - var value = Math.max(Math.abs(start), Math.abs(stop)); - if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; - return formatPrefix(specifier, value); - } - case "": - case "e": - case "g": - case "p": - case "r": { - if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); - break; - } - case "f": - case "%": { - if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; - break; - } - } - return format(specifier); - } - - function linearish(scale) { - var domain = scale.domain; - - scale.ticks = function(count) { - var d = domain(); - return ticks(d[0], d[d.length - 1], count == null ? 10 : count); - }; - - scale.tickFormat = function(count, specifier) { - var d = domain(); - return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier); - }; - - scale.nice = function(count) { - if (count == null) count = 10; - - var d = domain(), - i0 = 0, - i1 = d.length - 1, - start = d[i0], - stop = d[i1], - step; - - if (stop < start) { - step = start, start = stop, stop = step; - step = i0, i0 = i1, i1 = step; - } - - step = tickIncrement(start, stop, count); - - if (step > 0) { - start = Math.floor(start / step) * step; - stop = Math.ceil(stop / step) * step; - step = tickIncrement(start, stop, count); - } else if (step < 0) { - start = Math.ceil(start * step) / step; - stop = Math.floor(stop * step) / step; - step = tickIncrement(start, stop, count); - } - - if (step > 0) { - d[i0] = Math.floor(start / step) * step; - d[i1] = Math.ceil(stop / step) * step; - domain(d); - } else if (step < 0) { - d[i0] = Math.ceil(start * step) / step; - d[i1] = Math.floor(stop * step) / step; - domain(d); - } - - return scale; - }; - - return scale; - } - - function linear$2() { - var scale = continuous(identity$3, identity$3); - - scale.copy = function() { - return copy$1(scale, linear$2()); - }; - - initRange.apply(scale, arguments); - - return linearish(scale); - } - - function quantize() { - var x0 = 0, - x1 = 1, - n = 1, - domain = [0.5], - range = [0, 1], - unknown; - - function scale(x) { - return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown; - } - - function rescale() { - var i = -1; - domain = new Array(n); - while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); - return scale; - } - - scale.domain = function(_) { - return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1]; - }; - - scale.range = function(_) { - return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice(); - }; - - scale.invertExtent = function(y) { - var i = range.indexOf(y); - return i < 0 ? [NaN, NaN] - : i < 1 ? [x0, domain[0]] - : i >= n ? [domain[n - 1], x1] - : [domain[i - 1], domain[i]]; - }; - - scale.unknown = function(_) { - return arguments.length ? (unknown = _, scale) : scale; - }; - - scale.thresholds = function() { - return domain.slice(); - }; - - scale.copy = function() { - return quantize() - .domain([x0, x1]) - .range(range) - .unknown(unknown); - }; - - return initRange.apply(linearish(scale), arguments); - } - - function behaviorBreathe() { - var duration = 800; - var steps = 4; - var selector = '.selected.shadow, .selected .shadow'; - var _selected = select(null); - var _classed = ''; - var _params = {}; - var _done = false; - var _timer; - - - function ratchetyInterpolator(a, b, steps, units) { - a = parseFloat(a); - b = parseFloat(b); - var sample = quantize() - .domain([0, 1]) - .range(d3_quantize(d3_interpolateNumber(a, b), steps)); - - return function(t) { - return String(sample(t)) + (units || ''); - }; - } - - - function reset(selection) { - selection - .style('stroke-opacity', null) - .style('stroke-width', null) - .style('fill-opacity', null) - .style('r', null); - } - - - function setAnimationParams(transition, fromTo) { - var toFrom = (fromTo === 'from' ? 'to' : 'from'); - - transition - .styleTween('stroke-opacity', function(d) { - return ratchetyInterpolator( - _params[d.id][toFrom].opacity, - _params[d.id][fromTo].opacity, - steps - ); - }) - .styleTween('stroke-width', function(d) { - return ratchetyInterpolator( - _params[d.id][toFrom].width, - _params[d.id][fromTo].width, - steps, - 'px' - ); - }) - .styleTween('fill-opacity', function(d) { - return ratchetyInterpolator( - _params[d.id][toFrom].opacity, - _params[d.id][fromTo].opacity, - steps - ); - }) - .styleTween('r', function(d) { - return ratchetyInterpolator( - _params[d.id][toFrom].width, - _params[d.id][fromTo].width, - steps, - 'px' - ); - }); - } - - - function calcAnimationParams(selection) { - selection - .call(reset) - .each(function(d) { - var s = select(this); - var tag = s.node().tagName; - var p = {'from': {}, 'to': {}}; - var opacity; - var width; - - // determine base opacity and width - if (tag === 'circle') { - opacity = parseFloat(s.style('fill-opacity') || 0.5); - width = parseFloat(s.style('r') || 15.5); - } else { - opacity = parseFloat(s.style('stroke-opacity') || 0.7); - width = parseFloat(s.style('stroke-width') || 10); - } - - // calculate from/to interpolation params.. - p.tag = tag; - p.from.opacity = opacity * 0.6; - p.to.opacity = opacity * 1.25; - p.from.width = width * 0.7; - p.to.width = width * (tag === 'circle' ? 1.5 : 1); - _params[d.id] = p; - }); - } - - - function run(surface, fromTo) { - var toFrom = (fromTo === 'from' ? 'to' : 'from'); - var currSelected = surface.selectAll(selector); - var currClassed = surface.attr('class'); - - if (_done || currSelected.empty()) { - _selected.call(reset); - _selected = select(null); - return; - } - - if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) { - _selected.call(reset); - _classed = currClassed; - _selected = currSelected.call(calcAnimationParams); - } - - var didCallNextRun = false; - - _selected - .transition() - .duration(duration) - .call(setAnimationParams, fromTo) - .on('end', function() { - // `end` event is called for each selected element, but we want - // it to run only once - if (!didCallNextRun) { - surface.call(run, toFrom); - didCallNextRun = true; - } - - // if entity was deselected, remove breathe styling - if (!select(this).classed('selected')) { - reset(select(this)); - } - }); - } - - function behavior(surface) { - _done = false; - _timer = timer(function() { - // wait for elements to actually become selected - if (surface.selectAll(selector).empty()) { - return false; - } - - surface.call(run, 'from'); - _timer.stop(); - return true; - }, 20); - } - - behavior.restartIfNeeded = function(surface) { - if (_selected.empty()) { - surface.call(run, 'from'); - if (_timer) { - _timer.stop(); - } - } - }; - - behavior.off = function() { - _done = true; - if (_timer) { - _timer.stop(); - } - _selected - .interrupt() - .call(reset); - }; - - - return behavior; - } - - /* Creates a keybinding behavior for an operation */ - function behaviorOperation(context) { - var _operation; - - function keypress() { - // prevent operations during low zoom selection - if (!context.map().withinEditableZoom()) return; - - event.preventDefault(); - var disabled = _operation.disabled(); - - if (disabled) { - context.ui().flash - .duration(4000) - .iconName('#iD-operation-' + _operation.id) - .iconClass('operation disabled') - .text(_operation.tooltip)(); - - } else { - context.ui().flash - .duration(2000) - .iconName('#iD-operation-' + _operation.id) - .iconClass('operation') - .text(_operation.annotation() || _operation.title)(); - - if (_operation.point) _operation.point(null); - _operation(); - } - } - - - function behavior() { - if (_operation && _operation.available()) { - context.keybinding() - .on(_operation.keys, keypress); - } - - return behavior; - } - - - behavior.off = function() { - context.keybinding() - .off(_operation.keys); - }; - - - behavior.which = function (_) { - if (!arguments.length) return _operation; - _operation = _; - return behavior; - }; - - - return behavior; - } - - function operationCircularize(context, selectedIDs) { - var _extent; - var _actions = selectedIDs.map(getAction).filter(Boolean); - var _amount = _actions.length === 1 ? 'single' : 'multiple'; - var _coords = utilGetAllNodes(selectedIDs, context.graph()) - .map(function(n) { return n.loc; }); - - function getAction(entityID) { - - var entity = context.entity(entityID); - - if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null; - - if (!_extent) { - _extent = entity.extent(context.graph()); - } else { - _extent = _extent.extend(entity.extent(context.graph())); - } - - return actionCircularize(entityID, context.projection); - } - - var operation = function() { - if (!_actions.length) return; - - var combinedAction = function(graph, t) { - _actions.forEach(function(action) { - if (!action.disabled(graph)) { - graph = action(graph, t); - } - }); - return graph; - }; - combinedAction.transitionable = true; - - context.perform(combinedAction, operation.annotation()); - - window.setTimeout(function() { - context.validator().validate(); - }, 300); // after any transition - }; - - - operation.available = function() { - return _actions.length && selectedIDs.length === _actions.length; - }; - - - // don't cache this because the visible extent could change - operation.disabled = function() { - if (!_actions.length) return ''; - - var actionDisableds = _actions.map(function(action) { - return action.disabled(context.graph()); - }).filter(Boolean); - - if (actionDisableds.length === _actions.length) { - // none of the features can be circularized - - if (new Set(actionDisableds).size > 1) { - return 'multiple_blockers'; - } - return actionDisableds[0]; - } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.circularize.' + disable + '.' + _amount) : - _t('operations.circularize.description.' + _amount); - }; - - - operation.annotation = function() { - return _t('operations.circularize.annotation.' + _amount); - }; - - - operation.id = 'circularize'; - operation.keys = [_t('operations.circularize.key')]; - operation.title = _t('operations.circularize.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - // Translate a MacOS key command into the appropriate Windows/Linux equivalent. - // For example, ⌘Z -> Ctrl+Z - var uiCmd = function (code) { - var detected = utilDetect(); - - if (detected.os === 'mac') { - return code; - } - - if (detected.os === 'win') { - if (code === '⌘⇧Z') return 'Ctrl+Y'; - } - - var result = '', - replacements = { - '⌘': 'Ctrl', - '⇧': 'Shift', - '⌥': 'Alt', - '⌫': 'Backspace', - '⌦': 'Delete' - }; - - for (var i = 0; i < code.length; i++) { - if (code[i] in replacements) { - result += replacements[code[i]] + (i < code.length - 1 ? '+' : ''); - } else { - result += code[i]; - } - } - - return result; - }; - - - // return a display-focused string for a given keyboard code - uiCmd.display = function(code) { - if (code.length !== 1) return code; - - var detected = utilDetect(); - var mac = (detected.os === 'mac'); - var replacements = { - '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'), - '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'), - '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'), - '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'), - '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'), - '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'), - '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'), - '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'), - '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'), - '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'), - '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'), - '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'), - '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu'), - }; - - return replacements[code] || code; - }; - - function operationDelete(context, selectedIDs) { - var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'); - var action = actionDeleteMultiple(selectedIDs); - var nodes = utilGetAllNodes(selectedIDs, context.graph()); - var coords = nodes.map(function(n) { return n.loc; }); - var extent = utilTotalExtent(selectedIDs, context.graph()); - - - var operation = function() { - var nextSelectedID; - var nextSelectedLoc; - - if (selectedIDs.length === 1) { - var id = selectedIDs[0]; - var entity = context.entity(id); - var geometry = entity.geometry(context.graph()); - var parents = context.graph().parentWays(entity); - var parent = parents[0]; - - // Select the next closest node in the way. - if (geometry === 'vertex') { - var nodes = parent.nodes; - var i = nodes.indexOf(id); - - if (i === 0) { - i++; - } else if (i === nodes.length - 1) { - i--; - } else { - var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc); - var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc); - i = a < b ? i - 1 : i + 1; - } - - nextSelectedID = nodes[i]; - nextSelectedLoc = context.entity(nextSelectedID).loc; - } - } - - context.perform(action, operation.annotation()); - context.validator().validate(); - - if (nextSelectedID && nextSelectedLoc) { - if (context.hasEntity(nextSelectedID)) { - context.enter(modeSelect(context, [nextSelectedID]).follow(true)); - } else { - context.map().centerEase(nextSelectedLoc); - context.enter(modeBrowse(context)); - } - } else { - context.enter(modeBrowse(context)); - } - - }; - - - operation.available = function() { - return true; - }; - - - operation.disabled = function() { - if (extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } else if (selectedIDs.some(protectedMember)) { - return 'part_of_relation'; - } else if (selectedIDs.some(incompleteRelation)) { - return 'incomplete_relation'; - } else if (selectedIDs.some(hasWikidataTag)) { - return 'has_wikidata_tag'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - - function hasWikidataTag(id) { - var entity = context.entity(id); - return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0; - } - - function incompleteRelation(id) { - var entity = context.entity(id); - return entity.type === 'relation' && !entity.isComplete(context.graph()); - } - - function protectedMember(id) { - var entity = context.entity(id); - if (entity.type !== 'way') return false; - - var parents = context.graph().parentRelations(entity); - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; - var type = parent.tags.type; - var role = parent.memberById(id).role || 'outer'; - if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) { - return true; - } - } - return false; - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.delete.' + disable + '.' + multi) : - _t('operations.delete.description' + '.' + multi); - }; - - - operation.annotation = function() { - return selectedIDs.length === 1 ? - _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : - _t('operations.delete.annotation.multiple', { n: selectedIDs.length }); - }; - - - operation.id = 'delete'; - operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')]; - operation.title = _t('operations.delete.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationOrthogonalize(context, selectedIDs) { - var _extent; - var _type; - var _actions = selectedIDs.map(chooseAction).filter(Boolean); - var _amount = _actions.length === 1 ? 'single' : 'multiple'; - var _coords = utilGetAllNodes(selectedIDs, context.graph()) - .map(function(n) { return n.loc; }); - - - function chooseAction(entityID) { - - var entity = context.entity(entityID); - var geometry = entity.geometry(context.graph()); - - if (!_extent) { - _extent = entity.extent(context.graph()); - } else { - _extent = _extent.extend(entity.extent(context.graph())); - } - - // square a line/area - if (entity.type === 'way' && new Set(entity.nodes).size > 2 ) { - if (_type && _type !== 'feature') return null; - _type = 'feature'; - return actionOrthogonalize(entityID, context.projection); - - // square a single vertex - } else if (geometry === 'vertex') { - if (_type && _type !== 'corner') return null; - _type = 'corner'; - var graph = context.graph(); - var parents = graph.parentWays(entity); - if (parents.length === 1) { - var way = parents[0]; - if (way.nodes.indexOf(entityID) !== -1) { - return actionOrthogonalize(way.id, context.projection, entityID); - } - } - } - - return null; - } - - - var operation = function() { - if (!_actions.length) return; - - var combinedAction = function(graph, t) { - _actions.forEach(function(action) { - if (!action.disabled(graph)) { - graph = action(graph, t); - } - }); - return graph; - }; - combinedAction.transitionable = true; - - context.perform(combinedAction, operation.annotation()); - - window.setTimeout(function() { - context.validator().validate(); - }, 300); // after any transition - }; - - - operation.available = function() { - return _actions.length && selectedIDs.length === _actions.length; - }; - - - // don't cache this because the visible extent could change - operation.disabled = function() { - if (!_actions.length) return ''; - - var actionDisableds = _actions.map(function(action) { - return action.disabled(context.graph()); - }).filter(Boolean); - - if (actionDisableds.length === _actions.length) { - // none of the features can be squared - - if (new Set(actionDisableds).size > 1) { - return 'multiple_blockers'; - } - return actionDisableds[0]; - } else if (_extent && - _extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.orthogonalize.' + disable + '.' + _amount) : - _t('operations.orthogonalize.description.' + _type + '.' + _amount); - }; - - - operation.annotation = function() { - return _t('operations.orthogonalize.annotation.' + _type + '.' + _amount); - }; - - - operation.id = 'orthogonalize'; - operation.keys = [_t('operations.orthogonalize.key')]; - operation.title = _t('operations.orthogonalize.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationReflectShort(context, selectedIDs) { - return operationReflect(context, selectedIDs, 'short'); - } - - - function operationReflectLong(context, selectedIDs) { - return operationReflect(context, selectedIDs, 'long'); - } - - - function operationReflect(context, selectedIDs, axis) { - axis = axis || 'long'; - var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'); - var nodes = utilGetAllNodes(selectedIDs, context.graph()); - var coords = nodes.map(function(n) { return n.loc; }); - var extent = utilTotalExtent(selectedIDs, context.graph()); - - - var operation = function() { - var action = actionReflect(selectedIDs, context.projection) - .useLongAxis(Boolean(axis === 'long')); - - context.perform(action, operation.annotation()); - - window.setTimeout(function() { - context.validator().validate(); - }, 300); // after any transition - }; - - - operation.available = function() { - return nodes.length >= 3; - }; - - - // don't cache this because the visible extent could change - operation.disabled = function() { - if (extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } else if (selectedIDs.some(incompleteRelation)) { - return 'incomplete_relation'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - - function incompleteRelation(id) { - var entity = context.entity(id); - return entity.type === 'relation' && !entity.isComplete(context.graph()); - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.reflect.' + disable + '.' + multi) : - _t('operations.reflect.description.' + axis + '.' + multi); - }; - - - operation.annotation = function() { - return _t('operations.reflect.annotation.' + axis + '.' + multi); - }; - - - operation.id = 'reflect-' + axis; - operation.keys = [_t('operations.reflect.key.' + axis)]; - operation.title = _t('operations.reflect.title.' + axis); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationMove(context, selectedIDs) { - var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'); - var nodes = utilGetAllNodes(selectedIDs, context.graph()); - var coords = nodes.map(function(n) { return n.loc; }); - var extent = utilTotalExtent(selectedIDs, context.graph()); - - - var operation = function() { - context.enter(modeMove(context, selectedIDs)); - }; - - - operation.available = function() { - return selectedIDs.length > 1 || - context.entity(selectedIDs[0]).type !== 'node'; - }; - - - operation.disabled = function() { - if (extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } else if (selectedIDs.some(incompleteRelation)) { - return 'incomplete_relation'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - - function incompleteRelation(id) { - var entity = context.entity(id); - return entity.type === 'relation' && !entity.isComplete(context.graph()); - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.move.' + disable + '.' + multi) : - _t('operations.move.description.' + multi); - }; - - - operation.annotation = function() { - return selectedIDs.length === 1 ? - _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : - _t('operations.move.annotation.multiple'); - }; - - - operation.id = 'move'; - operation.keys = [_t('operations.move.key')]; - operation.title = _t('operations.move.title'); - operation.behavior = behaviorOperation(context).which(operation); - - operation.mouseOnly = true; - - return operation; - } - - function modeRotate(context, entityIDs) { - var mode = { - id: 'rotate', - button: 'browse' - }; - - var keybinding = utilKeybinding('rotate'); - var behaviors = [ - behaviorEdit(context), - operationCircularize(context, entityIDs).behavior, - operationDelete(context, entityIDs).behavior, - operationMove(context, entityIDs).behavior, - operationOrthogonalize(context, entityIDs).behavior, - operationReflectLong(context, entityIDs).behavior, - operationReflectShort(context, entityIDs).behavior - ]; - var annotation = entityIDs.length === 1 ? - _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : - _t('operations.rotate.annotation.multiple'); - - var _prevGraph; - var _prevAngle; - var _prevTransform; - var _pivot; - - - function doRotate() { - var fn; - if (context.graph() !== _prevGraph) { - fn = context.perform; - } else { - fn = context.replace; - } - - // projection changed, recalculate _pivot - var projection = context.projection; - var currTransform = projection.transform(); - if (!_prevTransform || - currTransform.k !== _prevTransform.k || - currTransform.x !== _prevTransform.x || - currTransform.y !== _prevTransform.y) { - - var nodes = utilGetAllNodes(entityIDs, context.graph()); - var points = nodes.map(function(n) { return projection(n.loc); }); - _pivot = getPivot(points); - _prevAngle = undefined; - } - - - var currMouse = context.map().mouse(); - var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]); - - if (typeof _prevAngle === 'undefined') _prevAngle = currAngle; - var delta = currAngle - _prevAngle; - - fn(actionRotate(entityIDs, _pivot, delta, projection)); - - _prevTransform = currTransform; - _prevAngle = currAngle; - _prevGraph = context.graph(); - } - - function getPivot(points) { - var _pivot; - if (points.length === 1) { - _pivot = points[0]; - } else if (points.length === 2) { - _pivot = geoVecInterp(points[0], points[1], 0.5); - } else { - var polygonHull = d3_polygonHull(points); - if (polygonHull.length === 2) { - _pivot = geoVecInterp(points[0], points[1], 0.5); - } else { - _pivot = d3_polygonCentroid(d3_polygonHull(points)); - } - } - return _pivot; - } - - - function finish() { - event.stopPropagation(); - context.replace(actionNoop(), annotation); - context.enter(modeSelect(context, entityIDs)); - } - - - function cancel() { - context.pop(); - context.enter(modeSelect(context, entityIDs)); - } - - - function undone() { - context.enter(modeBrowse(context)); - } - - - mode.enter = function() { - context.features().forceVisible(entityIDs); - - behaviors.forEach(context.install); - - context.surface() - .on('mousemove.rotate', doRotate) - .on('click.rotate', finish); - - context.history() - .on('undone.rotate', undone); - - keybinding - .on('⎋', cancel) - .on('↩', finish); - - select(document) - .call(keybinding); - }; - - - mode.exit = function() { - behaviors.forEach(context.uninstall); - - context.surface() - .on('mousemove.rotate', null) - .on('click.rotate', null); - - context.history() - .on('undone.rotate', null); - - select(document) - .call(keybinding.unbind); - - context.features().forceVisible([]); - }; - - - mode.selectedIDs = function() { - if (!arguments.length) return entityIDs; - // no assign - return mode; - }; - - - return mode; - } - - function operationRotate(context, selectedIDs) { - var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'); - var nodes = utilGetAllNodes(selectedIDs, context.graph()); - var coords = nodes.map(function(n) { return n.loc; }); - var extent = utilTotalExtent(selectedIDs, context.graph()); - - - var operation = function() { - context.enter(modeRotate(context, selectedIDs)); - }; - - - operation.available = function() { - return nodes.length >= 2; - }; - - - operation.disabled = function() { - - if (extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } else if (selectedIDs.some(incompleteRelation)) { - return 'incomplete_relation'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - - function incompleteRelation(id) { - var entity = context.entity(id); - return entity.type === 'relation' && !entity.isComplete(context.graph()); - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.rotate.' + disable + '.' + multi) : - _t('operations.rotate.description.' + multi); - }; - - - operation.annotation = function() { - return selectedIDs.length === 1 ? - _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : - _t('operations.rotate.annotation.multiple'); - }; - - - operation.id = 'rotate'; - operation.keys = [_t('operations.rotate.key')]; - operation.title = _t('operations.rotate.title'); - operation.behavior = behaviorOperation(context).which(operation); - - operation.mouseOnly = true; - - return operation; - } - - function modeMove(context, entityIDs, baseGraph) { - var mode = { - id: 'move', - button: 'browse' - }; - - var keybinding = utilKeybinding('move'); - var behaviors = [ - behaviorEdit(context), - operationCircularize(context, entityIDs).behavior, - operationDelete(context, entityIDs).behavior, - operationOrthogonalize(context, entityIDs).behavior, - operationReflectLong(context, entityIDs).behavior, - operationReflectShort(context, entityIDs).behavior, - operationRotate(context, entityIDs).behavior - ]; - var annotation = entityIDs.length === 1 ? - _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : - _t('operations.move.annotation.multiple'); - - var _prevGraph; - var _cache; - var _origin; - var _nudgeInterval; - - - function doMove(nudge) { - nudge = nudge || [0, 0]; - - var fn; - if (_prevGraph !== context.graph()) { - _cache = {}; - _origin = context.map().mouseCoordinates(); - fn = context.perform; - } else { - fn = context.overwrite; - } - - var currMouse = context.map().mouse(); - var origMouse = context.projection(_origin); - var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge); - - fn(actionMove(entityIDs, delta, context.projection, _cache)); - _prevGraph = context.graph(); - } - - - function startNudge(nudge) { - if (_nudgeInterval) window.clearInterval(_nudgeInterval); - _nudgeInterval = window.setInterval(function() { - context.map().pan(nudge); - doMove(nudge); - }, 50); - } - - - function stopNudge() { - if (_nudgeInterval) { - window.clearInterval(_nudgeInterval); - _nudgeInterval = null; - } - } - - - function move() { - doMove(); - var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions()); - if (nudge) { - startNudge(nudge); - } else { - stopNudge(); - } - } - - - function finish() { - event.stopPropagation(); - context.replace(actionNoop(), annotation); - context.enter(modeSelect(context, entityIDs)); - stopNudge(); - } - - - function cancel() { - if (baseGraph) { - while (context.graph() !== baseGraph) context.pop(); - context.enter(modeBrowse(context)); - } else { - context.pop(); - context.enter(modeSelect(context, entityIDs)); - } - stopNudge(); - } - - - function undone() { - context.enter(modeBrowse(context)); - } - - - mode.enter = function() { - _origin = context.map().mouseCoordinates(); - _prevGraph = null; - _cache = {}; - - context.features().forceVisible(entityIDs); - - behaviors.forEach(context.install); - - context.surface() - .on('mousemove.move', move) - .on('click.move', finish); - - context.history() - .on('undone.move', undone); - - keybinding - .on('⎋', cancel) - .on('↩', finish); - - select(document) - .call(keybinding); - }; - - - mode.exit = function() { - stopNudge(); - - behaviors.forEach(function(behavior) { - context.uninstall(behavior); - }); - - context.surface() - .on('mousemove.move', null) - .on('click.move', null); - - context.history() - .on('undone.move', null); - - select(document) - .call(keybinding.unbind); - - context.features().forceVisible([]); - }; - - - mode.selectedIDs = function() { - if (!arguments.length) return entityIDs; - // no assign - return mode; - }; - - - return mode; - } - - // see also `operationPaste` - function behaviorPaste(context) { - - function doPaste() { - // prevent paste during low zoom selection - if (!context.map().withinEditableZoom()) return; - - event.preventDefault(); - - var baseGraph = context.graph(); - var mouse = context.map().mouse(); - var projection = context.projection; - var viewport = geoExtent(projection.clipExtent()).polygon(); - - if (!geoPointInPolygon(mouse, viewport)) return; - - var oldIDs = context.copyIDs(); - if (!oldIDs.length) return; - - var extent = geoExtent(); - var oldGraph = context.copyGraph(); - var newIDs = []; - - var action = actionCopyEntities(oldIDs, oldGraph); - context.perform(action); - - var copies = action.copies(); - var originals = new Set(); - Object.values(copies).forEach(function(entity) { originals.add(entity.id); }); - - for (var id in copies) { - var oldEntity = oldGraph.entity(id); - var newEntity = copies[id]; - - extent._extend(oldEntity.extent(oldGraph)); - - // Exclude child nodes from newIDs if their parent way was also copied. - var parents = context.graph().parentWays(newEntity); - var parentCopied = parents.some(function(parent) { - return originals.has(parent.id); - }); - - if (!parentCopied) { - newIDs.push(newEntity.id); - } - } - - // Put pasted objects where mouse pointer is.. - var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center()); - var delta = geoVecSubtract(mouse, copyPoint); - - context.perform(actionMove(newIDs, delta, projection)); - context.enter(modeMove(context, newIDs, baseGraph)); - } - - - function behavior() { - context.keybinding().on(uiCmd('⌘V'), doPaste); - return behavior; - } - - - behavior.off = function() { - context.keybinding().off(uiCmd('⌘V')); - }; - - - return behavior; - } - - /* - `behaviorDrag` is like `d3_behavior.drag`, with the following differences: - - * The `origin` function is expected to return an [x, y] tuple rather than an - {x, y} object. - * The events are `start`, `move`, and `end`. - (https://github.com/mbostock/d3/issues/563) - * The `start` event is not dispatched until the first cursor movement occurs. - (https://github.com/mbostock/d3/pull/368) - * The `move` event has a `point` and `delta` [x, y] tuple properties rather - than `x`, `y`, `dx`, and `dy` properties. - * The `end` event is not dispatched if no movement occurs. - * An `off` function is available that unbinds the drag's internal event handlers. - */ - - function behaviorDrag() { - var dispatch$1 = dispatch('start', 'move', 'end'); - - // see also behaviorSelect - var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping - var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981 - - var _origin = null; - var _selector = ''; - var _event; - var _target; - var _surface; - var _pointerId; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect'); - var d3_event_userSelectSuppress = function() { - var selection$1 = selection(); - var select = selection$1.style(d3_event_userSelectProperty); - selection$1.style(d3_event_userSelectProperty, 'none'); - return function() { - selection$1.style(d3_event_userSelectProperty, select); - }; - }; - - - function eventOf(thiz, argumentz) { - return function(e1) { - e1.target = behavior; - customEvent(e1, dispatch$1.apply, dispatch$1, [e1.type, thiz, argumentz]); - }; - } - - - function pointerdown() { - - if (_pointerId) return; - - _pointerId = event.pointerId || 'mouse'; - - _target = this; - _event = eventOf(_target, arguments); - - // only force reflow once per drag - var pointerLocGetter = utilFastMouse(_surface || _target.parentNode); - - var offset; - var startOrigin = pointerLocGetter(event); - var started = false; - var selectEnable = d3_event_userSelectSuppress(); - - select(window) - .on(_pointerPrefix + 'move.drag', pointermove) - .on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true); - - if (_origin) { - offset = _origin.apply(_target, arguments); - offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]]; - } else { - offset = [0, 0]; - } - - event.stopPropagation(); - - - function pointermove() { - if (_pointerId !== (event.pointerId || 'mouse')) return; - - var p = pointerLocGetter(event); - - if (!started) { - var dist = geoVecLength(startOrigin, p); - var tolerance = event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; - // don't start until the drag has actually moved somewhat - if (dist < tolerance) return; - - started = true; - _event({ type: 'start' }); - - // Don't send a `move` event in the same cycle as `start` since dragging - // a midpoint will convert the target to a node. - } else { - - startOrigin = p; - event.stopPropagation(); - event.preventDefault(); - - var dx = p[0] - startOrigin[0]; - var dy = p[1] - startOrigin[1]; - _event({ - type: 'move', - point: [p[0] + offset[0], p[1] + offset[1]], - delta: [dx, dy] - }); - } - } - - - function pointerup() { - if (_pointerId !== (event.pointerId || 'mouse')) return; - - _pointerId = null; - - if (started) { - _event({ type: 'end' }); - - event.preventDefault(); - } - - select(window) - .on(_pointerPrefix + 'move.drag', null) - .on(_pointerPrefix + 'up.drag pointercancel.drag', null); - - selectEnable(); - } - } - - - function behavior(selection) { - _pointerId = null; - var matchesSelector = utilPrefixDOMProperty('matchesSelector'); - var delegate = pointerdown; - - if (_selector) { - delegate = function() { - var root = this; - var target = event.target; - for (; target && target !== root; target = target.parentNode) { - var datum = target.__data__; - - var entity = datum instanceof osmNote ? datum - : datum && datum.properties && datum.properties.entity; - - if (entity && target[matchesSelector](_selector)) { - return pointerdown.call(target, entity); - } - } - }; - } - - selection - .on(_pointerPrefix + 'down.drag' + _selector, delegate); - } - - - behavior.off = function(selection) { - selection - .on(_pointerPrefix + 'down.drag' + _selector, null); - }; - - - behavior.selector = function(_) { - if (!arguments.length) return _selector; - _selector = _; - return behavior; - }; - - - behavior.origin = function(_) { - if (!arguments.length) return _origin; - _origin = _; - return behavior; - }; - - - behavior.cancel = function() { - select(window) - .on(_pointerPrefix + 'move.drag', null) - .on(_pointerPrefix + 'up.drag pointercancel.drag', null); - return behavior; - }; - - - behavior.target = function() { - if (!arguments.length) return _target; - _target = arguments[0]; - _event = eventOf(_target, Array.prototype.slice.call(arguments, 1)); - return behavior; - }; - - - behavior.surface = function() { - if (!arguments.length) return _surface; - _surface = arguments[0]; - return behavior; - }; - - - return utilRebind(behavior, dispatch$1, 'on'); - } - - function modeDragNode(context) { - var mode = { - id: 'drag-node', - button: 'browse' - }; - var hover = behaviorHover(context).altDisables(true) - .on('hover', context.ui().sidebar.hover); - var edit = behaviorEdit(context); - - var _nudgeInterval; - var _restoreSelectedIDs = []; - var _wasMidpoint = false; - var _isCancelled = false; - var _activeEntity; - var _startLoc; - var _lastLoc; - - - function startNudge(entity, nudge) { - if (_nudgeInterval) window.clearInterval(_nudgeInterval); - _nudgeInterval = window.setInterval(function() { - context.map().pan(nudge); - doMove(entity, nudge); - }, 50); - } - - - function stopNudge() { - if (_nudgeInterval) { - window.clearInterval(_nudgeInterval); - _nudgeInterval = null; - } - } - - - function moveAnnotation(entity) { - return _t('operations.move.annotation.' + entity.geometry(context.graph())); - } - - - function connectAnnotation(nodeEntity, targetEntity) { - var nodeGeometry = nodeEntity.geometry(context.graph()); - var targetGeometry = targetEntity.geometry(context.graph()); - if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') { - var nodeParentWayIDs = context.graph().parentWays(nodeEntity); - var targetParentWayIDs = context.graph().parentWays(targetEntity); - var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); - // if both vertices are part of the same way - if (sharedParentWays.length !== 0) { - // if the nodes are next to each other, they are merged - if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) { - return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex'); - } - return _t('operations.connect.annotation.from_vertex.to_sibling_vertex'); - } - } - return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry); - } - - - function shouldSnapToNode(target) { - if (!_activeEntity) return false; - return _activeEntity.geometry(context.graph()) !== 'vertex' || - (target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph())); - } - - - function origin(entity) { - return context.projection(entity.loc); - } - - - function keydown() { - if (event.keyCode === utilKeybinding.modifierCodes.alt) { - if (context.surface().classed('nope')) { - context.surface() - .classed('nope-suppressed', true); - } - context.surface() - .classed('nope', false) - .classed('nope-disabled', true); - } - } - - - function keyup() { - if (event.keyCode === utilKeybinding.modifierCodes.alt) { - if (context.surface().classed('nope-suppressed')) { - context.surface() - .classed('nope', true); - } - context.surface() - .classed('nope-suppressed', false) - .classed('nope-disabled', false); - } - } - - - function start(entity) { - _wasMidpoint = entity.type === 'midpoint'; - var hasHidden = context.features().hasHiddenConnections(entity, context.graph()); - _isCancelled = !context.editable() || event.sourceEvent.shiftKey || hasHidden; - - - if (_isCancelled) { - if (hasHidden) { - context.ui().flash - .duration(4000) - .text(_t('modes.drag_node.connected_to_hidden'))(); - } - return drag.cancel(); - } - - if (_wasMidpoint) { - var midpoint = entity; - entity = osmNode(); - context.perform(actionAddMidpoint(midpoint, entity)); - entity = context.entity(entity.id); // get post-action entity - - var vertex = context.surface().selectAll('.' + entity.id); - drag.target(vertex.node(), entity); - - } else { - context.perform(actionNoop()); - } - - _activeEntity = entity; - _startLoc = entity.loc; - - hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex'); - - context.surface().selectAll('.' + _activeEntity.id) - .classed('active', true); - - context.enter(mode); - } - - - // related code - // - `behavior/draw.js` `datum()` - function datum() { - var event$1 = event && event.sourceEvent; - if (!event$1 || event$1.altKey) { - return {}; - } else { - // When dragging, snap only to touch targets.. - // (this excludes area fills and active drawing elements) - var d = event$1.target.__data__; - return (d && d.properties && d.properties.target) ? d : {}; - } - } - - - function doMove(entity, nudge) { - nudge = nudge || [0, 0]; - - var currPoint = (event && event.point) || context.projection(_lastLoc); - var currMouse = geoVecSubtract(currPoint, nudge); - var loc = context.projection.invert(currMouse); - - if (!_nudgeInterval) { // If not nudging at the edge of the viewport, try to snap.. - // related code - // - `mode/drag_node.js` `doMove()` - // - `behavior/draw.js` `click()` - // - `behavior/draw_way.js` `move()` - var d = datum(); - var target = d && d.properties && d.properties.entity; - var targetLoc = target && target.loc; - var targetNodes = d && d.properties && d.properties.nodes; - var edge; - - if (targetLoc) { // snap to node/vertex - a point target with `.loc` - if (shouldSnapToNode(target)) { - loc = targetLoc; - } - - } else if (targetNodes) { // snap to way - a line target with `.nodes` - edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id); - if (edge) { - loc = edge.loc; - } - } - } - - context.replace( - actionMoveNode(entity.id, loc) - ); - - // Below here: validations - var isInvalid = false; - - // Check if this connection to `target` could cause relations to break.. - if (target) { - isInvalid = hasRelationConflict(entity, target, edge, context.graph()); - } - - // Check if this drag causes the geometry to break.. - if (!isInvalid) { - isInvalid = hasInvalidGeometry(entity, context.graph()); - } - - - var nope = context.surface().classed('nope'); - if (isInvalid === 'relation' || isInvalid === 'restriction') { - if (!nope) { // about to nope - show hint - context.ui().flash - .duration(4000) - .text(_t('operations.connect.' + isInvalid, - { relation: _mainPresetIndex.item('type/restriction').name() } - ))(); - } - } else if (isInvalid) { - var errorID = isInvalid === 'line' ? 'lines' : 'areas'; - context.ui().flash - .duration(3000) - .text(_t('self_intersection.error.' + errorID))(); - } else { - if (nope) { // about to un-nope, remove hint - context.ui().flash - .duration(1) - .text('')(); - } - } - - - var nopeDisabled = context.surface().classed('nope-disabled'); - if (nopeDisabled) { - context.surface() - .classed('nope', false) - .classed('nope-suppressed', isInvalid); - } else { - context.surface() - .classed('nope', isInvalid) - .classed('nope-suppressed', false); - } - - _lastLoc = loc; - } - - - // Uses `actionConnect.disabled()` to know whether this connection is ok.. - function hasRelationConflict(entity, target, edge, graph) { - var testGraph = graph.update(); // copy - - // if snapping to way - add midpoint there and consider that the target.. - if (edge) { - var midpoint = osmNode(); - var action = actionAddMidpoint({ - loc: edge.loc, - edge: [target.nodes[edge.index - 1], target.nodes[edge.index]] - }, midpoint); - - testGraph = action(testGraph); - target = midpoint; - } - - // can we connect to it? - var ids = [entity.id, target.id]; - return actionConnect(ids).disabled(testGraph); - } - - - function hasInvalidGeometry(entity, graph) { - var parents = graph.parentWays(entity); - var i, j, k; - - for (i = 0; i < parents.length; i++) { - var parent = parents[i]; - var nodes = []; - var activeIndex = null; // which multipolygon ring contains node being dragged - - // test any parent multipolygons for valid geometry - var relations = graph.parentRelations(parent); - for (j = 0; j < relations.length; j++) { - if (!relations[j].isMultipolygon()) continue; - - var rings = osmJoinWays(relations[j].members, graph); - - // find active ring and test it for self intersections - for (k = 0; k < rings.length; k++) { - nodes = rings[k].nodes; - if (nodes.find(function(n) { return n.id === entity.id; })) { - activeIndex = k; - if (geoHasSelfIntersections(nodes, entity.id)) { - return 'multipolygonMember'; - } - } - rings[k].coords = nodes.map(function(n) { return n.loc; }); - } - - // test active ring for intersections with other rings in the multipolygon - for (k = 0; k < rings.length; k++) { - if (k === activeIndex) continue; - - // make sure active ring doesnt cross passive rings - if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) { - return 'multipolygonRing'; - } - } - } - - - // If we still haven't tested this node's parent way for self-intersections. - // (because it's not a member of a multipolygon), test it now. - if (activeIndex === null) { - nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); }); - if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) { - return parent.geometry(graph); - } - } - - } - - return false; - } - - - function move(entity) { - if (_isCancelled) return; - event.sourceEvent.stopPropagation(); - - context.surface().classed('nope-disabled', event.sourceEvent.altKey); - - _lastLoc = context.projection.invert(event.point); - - doMove(entity); - var nudge = geoViewportEdge(event.point, context.map().dimensions()); - if (nudge) { - startNudge(entity, nudge); - } else { - stopNudge(); - } - } - - function end(entity) { - if (_isCancelled) return; - - var wasPoint = entity.geometry(context.graph()) === 'point'; - - var d = datum(); - var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope'); - var target = d && d.properties && d.properties.entity; // entity to snap to - - if (nope) { // bounce back - context.perform( - _actionBounceBack(entity.id, _startLoc) - ); - - } else if (target && target.type === 'way') { - var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id); - context.replace( - actionAddMidpoint({ - loc: choice.loc, - edge: [target.nodes[choice.index - 1], target.nodes[choice.index]] - }, entity), - connectAnnotation(entity, target) - ); - - } else if (target && target.type === 'node' && shouldSnapToNode(target)) { - context.replace( - actionConnect([target.id, entity.id]), - connectAnnotation(entity, target) - ); - - } else if (_wasMidpoint) { - context.replace( - actionNoop(), - _t('operations.add.annotation.vertex') - ); - - } else { - context.replace( - actionNoop(), - moveAnnotation(entity) - ); - } - - if (wasPoint) { - context.enter(modeSelect(context, [entity.id])); - - } else { - var reselection = _restoreSelectedIDs.filter(function(id) { - return context.graph().hasEntity(id); - }); - - if (reselection.length) { - context.enter(modeSelect(context, reselection)); - } else { - context.enter(modeBrowse(context)); - } - } - } - - - function _actionBounceBack(nodeID, toLoc) { - var moveNode = actionMoveNode(nodeID, toLoc); - var action = function(graph, t) { - // last time through, pop off the bounceback perform. - // it will then overwrite the initial perform with a moveNode that does nothing - if (t === 1) context.pop(); - return moveNode(graph, t); - }; - action.transitionable = true; - return action; - } - - - function cancel() { - drag.cancel(); - context.enter(modeBrowse(context)); - } - - - var drag = behaviorDrag() - .selector('.layer-touch.points .target') - .surface(context.container().select('.main-map').node()) - .origin(origin) - .on('start', start) - .on('move', move) - .on('end', end); - - - mode.enter = function() { - context.install(hover); - context.install(edit); - - select(window) - .on('keydown.dragNode', keydown) - .on('keyup.dragNode', keyup); - - context.history() - .on('undone.drag-node', cancel); - }; - - - mode.exit = function() { - context.ui().sidebar.hover.cancel(); - context.uninstall(hover); - context.uninstall(edit); - - select(window) - .on('keydown.dragNode', null) - .on('keyup.dragNode', null); - - context.history() - .on('undone.drag-node', null); - - _activeEntity = null; - - context.surface() - .classed('nope', false) - .classed('nope-suppressed', false) - .classed('nope-disabled', false) - .selectAll('.active') - .classed('active', false); - - stopNudge(); - }; - - - mode.selectedIDs = function() { - if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; - // no assign - return mode; - }; - - - mode.activeID = function() { - if (!arguments.length) return _activeEntity && _activeEntity.id; - // no assign - return mode; - }; - - - mode.restoreSelectedIDs = function(_) { - if (!arguments.length) return _restoreSelectedIDs; - _restoreSelectedIDs = _; - return mode; - }; - - - mode.behavior = drag; - - - return mode; - } - - function quickselect(arr, k, left, right, compare) { - quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare); - } - - function quickselectStep(arr, k, left, right, compare) { - - while (right > left) { - if (right - left > 600) { - var n = right - left + 1; - var m = k - left + 1; - var z = Math.log(n); - var s = 0.5 * Math.exp(2 * z / 3); - var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); - var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); - var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); - quickselectStep(arr, k, newLeft, newRight, compare); - } - - var t = arr[k]; - var i = left; - var j = right; - - swap(arr, left, k); - if (compare(arr[right], t) > 0) swap(arr, left, right); - - while (i < j) { - swap(arr, i, j); - i++; - j--; - while (compare(arr[i], t) < 0) i++; - while (compare(arr[j], t) > 0) j--; - } - - if (compare(arr[left], t) === 0) swap(arr, left, j); - else { - j++; - swap(arr, j, right); - } - - if (j <= k) left = j + 1; - if (k <= j) right = j - 1; - } - } - - function swap(arr, i, j) { - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; - } - - function defaultCompare(a, b) { - return a < b ? -1 : a > b ? 1 : 0; - } - - class RBush { - constructor(maxEntries = 9) { - // max entries in a node is 9 by default; min node fill is 40% for best performance - this._maxEntries = Math.max(4, maxEntries); - this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); - this.clear(); - } - - all() { - return this._all(this.data, []); - } - - search(bbox) { - let node = this.data; - const result = []; - - if (!intersects(bbox, node)) return result; - - const toBBox = this.toBBox; - const nodesToSearch = []; - - while (node) { - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - const childBBox = node.leaf ? toBBox(child) : child; - - if (intersects(bbox, childBBox)) { - if (node.leaf) result.push(child); - else if (contains$1(bbox, childBBox)) this._all(child, result); - else nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - - return result; - } - - collides(bbox) { - let node = this.data; - - if (!intersects(bbox, node)) return false; - - const nodesToSearch = []; - while (node) { - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - const childBBox = node.leaf ? this.toBBox(child) : child; - - if (intersects(bbox, childBBox)) { - if (node.leaf || contains$1(bbox, childBBox)) return true; - nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - - return false; - } - - load(data) { - if (!(data && data.length)) return this; - - if (data.length < this._minEntries) { - for (let i = 0; i < data.length; i++) { - this.insert(data[i]); - } - return this; - } - - // recursively build the tree with the given data from scratch using OMT algorithm - let node = this._build(data.slice(), 0, data.length - 1, 0); - - if (!this.data.children.length) { - // save as is if tree is empty - this.data = node; - - } else if (this.data.height === node.height) { - // split root if trees have the same height - this._splitRoot(this.data, node); - - } else { - if (this.data.height < node.height) { - // swap trees if inserted one is bigger - const tmpNode = this.data; - this.data = node; - node = tmpNode; - } - - // insert the small tree into the large tree at appropriate level - this._insert(node, this.data.height - node.height - 1, true); - } - - return this; - } - - insert(item) { - if (item) this._insert(item, this.data.height - 1); - return this; - } - - clear() { - this.data = createNode([]); - return this; - } - - remove(item, equalsFn) { - if (!item) return this; - - let node = this.data; - const bbox = this.toBBox(item); - const path = []; - const indexes = []; - let i, parent, goingUp; - - // depth-first iterative tree traversal - while (node || path.length) { - - if (!node) { // go up - node = path.pop(); - parent = path[path.length - 1]; - i = indexes.pop(); - goingUp = true; - } - - if (node.leaf) { // check current node - const index = findItem(item, node.children, equalsFn); - - if (index !== -1) { - // item found, remove the item and condense tree upwards - node.children.splice(index, 1); - path.push(node); - this._condense(path); - return this; - } - } - - if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down - path.push(node); - indexes.push(i); - i = 0; - parent = node; - node = node.children[0]; - - } else if (parent) { // go right - i++; - node = parent.children[i]; - goingUp = false; - - } else node = null; // nothing found - } - - return this; - } - - toBBox(item) { return item; } - - compareMinX(a, b) { return a.minX - b.minX; } - compareMinY(a, b) { return a.minY - b.minY; } - - toJSON() { return this.data; } - - fromJSON(data) { - this.data = data; - return this; - } - - _all(node, result) { - const nodesToSearch = []; - while (node) { - if (node.leaf) result.push(...node.children); - else nodesToSearch.push(...node.children); - - node = nodesToSearch.pop(); - } - return result; - } - - _build(items, left, right, height) { - - const N = right - left + 1; - let M = this._maxEntries; - let node; - - if (N <= M) { - // reached leaf level; return leaf - node = createNode(items.slice(left, right + 1)); - calcBBox(node, this.toBBox); - return node; - } - - if (!height) { - // target height of the bulk-loaded tree - height = Math.ceil(Math.log(N) / Math.log(M)); - - // target number of root entries to maximize storage utilization - M = Math.ceil(N / Math.pow(M, height - 1)); - } - - node = createNode([]); - node.leaf = false; - node.height = height; - - // split the items into M mostly square tiles - - const N2 = Math.ceil(N / M); - const N1 = N2 * Math.ceil(Math.sqrt(M)); - - multiSelect(items, left, right, N1, this.compareMinX); - - for (let i = left; i <= right; i += N1) { - - const right2 = Math.min(i + N1 - 1, right); - - multiSelect(items, i, right2, N2, this.compareMinY); - - for (let j = i; j <= right2; j += N2) { - - const right3 = Math.min(j + N2 - 1, right2); - - // pack each entry recursively - node.children.push(this._build(items, j, right3, height - 1)); - } - } - - calcBBox(node, this.toBBox); - - return node; - } - - _chooseSubtree(bbox, node, level, path) { - while (true) { - path.push(node); - - if (node.leaf || path.length - 1 === level) break; - - let minArea = Infinity; - let minEnlargement = Infinity; - let targetNode; - - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - const area = bboxArea(child); - const enlargement = enlargedArea(bbox, child) - area; - - // choose entry with the least area enlargement - if (enlargement < minEnlargement) { - minEnlargement = enlargement; - minArea = area < minArea ? area : minArea; - targetNode = child; - - } else if (enlargement === minEnlargement) { - // otherwise choose one with the smallest area - if (area < minArea) { - minArea = area; - targetNode = child; - } - } - } - - node = targetNode || node.children[0]; - } - - return node; - } - - _insert(item, level, isNode) { - const bbox = isNode ? item : this.toBBox(item); - const insertPath = []; - - // find the best node for accommodating the item, saving all nodes along the path too - const node = this._chooseSubtree(bbox, this.data, level, insertPath); - - // put the item into the node - node.children.push(item); - extend$1(node, bbox); - - // split on node overflow; propagate upwards if necessary - while (level >= 0) { - if (insertPath[level].children.length > this._maxEntries) { - this._split(insertPath, level); - level--; - } else break; - } - - // adjust bboxes along the insertion path - this._adjustParentBBoxes(bbox, insertPath, level); - } - - // split overflowed node into two - _split(insertPath, level) { - const node = insertPath[level]; - const M = node.children.length; - const m = this._minEntries; - - this._chooseSplitAxis(node, m, M); - - const splitIndex = this._chooseSplitIndex(node, m, M); - - const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); - newNode.height = node.height; - newNode.leaf = node.leaf; - - calcBBox(node, this.toBBox); - calcBBox(newNode, this.toBBox); - - if (level) insertPath[level - 1].children.push(newNode); - else this._splitRoot(node, newNode); - } - - _splitRoot(node, newNode) { - // split root node - this.data = createNode([node, newNode]); - this.data.height = node.height + 1; - this.data.leaf = false; - calcBBox(this.data, this.toBBox); - } - - _chooseSplitIndex(node, m, M) { - let index; - let minOverlap = Infinity; - let minArea = Infinity; - - for (let i = m; i <= M - m; i++) { - const bbox1 = distBBox(node, 0, i, this.toBBox); - const bbox2 = distBBox(node, i, M, this.toBBox); - - const overlap = intersectionArea(bbox1, bbox2); - const area = bboxArea(bbox1) + bboxArea(bbox2); - - // choose distribution with minimum overlap - if (overlap < minOverlap) { - minOverlap = overlap; - index = i; - - minArea = area < minArea ? area : minArea; - - } else if (overlap === minOverlap) { - // otherwise choose distribution with minimum area - if (area < minArea) { - minArea = area; - index = i; - } - } - } - - return index || M - m; - } - - // sorts node children by the best axis for split - _chooseSplitAxis(node, m, M) { - const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; - const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; - const xMargin = this._allDistMargin(node, m, M, compareMinX); - const yMargin = this._allDistMargin(node, m, M, compareMinY); - - // if total distributions margin value is minimal for x, sort by minX, - // otherwise it's already sorted by minY - if (xMargin < yMargin) node.children.sort(compareMinX); - } - - // total margin of all possible split distributions where each node is at least m full - _allDistMargin(node, m, M, compare) { - node.children.sort(compare); - - const toBBox = this.toBBox; - const leftBBox = distBBox(node, 0, m, toBBox); - const rightBBox = distBBox(node, M - m, M, toBBox); - let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); - - for (let i = m; i < M - m; i++) { - const child = node.children[i]; - extend$1(leftBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin(leftBBox); - } - - for (let i = M - m - 1; i >= m; i--) { - const child = node.children[i]; - extend$1(rightBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin(rightBBox); - } - - return margin; - } - - _adjustParentBBoxes(bbox, path, level) { - // adjust bboxes along the given tree path - for (let i = level; i >= 0; i--) { - extend$1(path[i], bbox); - } - } - - _condense(path) { - // go through the path, removing empty nodes and updating bboxes - for (let i = path.length - 1, siblings; i >= 0; i--) { - if (path[i].children.length === 0) { - if (i > 0) { - siblings = path[i - 1].children; - siblings.splice(siblings.indexOf(path[i]), 1); - - } else this.clear(); - - } else calcBBox(path[i], this.toBBox); - } - } - } - - function findItem(item, items, equalsFn) { - if (!equalsFn) return items.indexOf(item); - - for (let i = 0; i < items.length; i++) { - if (equalsFn(item, items[i])) return i; - } - return -1; - } - - // calculate node's bbox from bboxes of its children - function calcBBox(node, toBBox) { - distBBox(node, 0, node.children.length, toBBox, node); - } - - // min bounding rectangle of node children from k to p-1 - function distBBox(node, k, p, toBBox, destNode) { - if (!destNode) destNode = createNode(null); - destNode.minX = Infinity; - destNode.minY = Infinity; - destNode.maxX = -Infinity; - destNode.maxY = -Infinity; - - for (let i = k; i < p; i++) { - const child = node.children[i]; - extend$1(destNode, node.leaf ? toBBox(child) : child); - } - - return destNode; - } - - function extend$1(a, b) { - a.minX = Math.min(a.minX, b.minX); - a.minY = Math.min(a.minY, b.minY); - a.maxX = Math.max(a.maxX, b.maxX); - a.maxY = Math.max(a.maxY, b.maxY); - return a; - } - - function compareNodeMinX(a, b) { return a.minX - b.minX; } - function compareNodeMinY(a, b) { return a.minY - b.minY; } - - function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } - function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } - - function enlargedArea(a, b) { - return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * - (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); - } - - function intersectionArea(a, b) { - const minX = Math.max(a.minX, b.minX); - const minY = Math.max(a.minY, b.minY); - const maxX = Math.min(a.maxX, b.maxX); - const maxY = Math.min(a.maxY, b.maxY); - - return Math.max(0, maxX - minX) * - Math.max(0, maxY - minY); - } - - function contains$1(a, b) { - return a.minX <= b.minX && - a.minY <= b.minY && - b.maxX <= a.maxX && - b.maxY <= a.maxY; - } - - function intersects(a, b) { - return b.minX <= a.maxX && - b.minY <= a.maxY && - b.maxX >= a.minX && - b.maxY >= a.minY; - } - - function createNode(children) { - return { - children, - height: 1, - leaf: true, - minX: Infinity, - minY: Infinity, - maxX: -Infinity, - maxY: -Infinity - }; - } - - // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; - // combines selection algorithm with binary divide & conquer approach - - function multiSelect(arr, left, right, n, compare) { - const stack = [left, right]; - - while (stack.length) { - right = stack.pop(); - left = stack.pop(); - - if (right - left <= n) continue; - - const mid = left + Math.ceil((right - left) / n / 2) * n; - quickselect(arr, mid, left, right, compare); - - stack.push(left, mid, mid, right); - } - } - - const tiler = utilTiler(); - const dispatch$1 = dispatch('loaded'); - const _tileZoom = 14; - const _krUrlRoot = 'https://www.keepright.at'; - let _krData = { errorTypes: {}, localizeStrings: {} }; - - // This gets reassigned if reset - let _cache; - - const _krRuleset = [ - // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads - 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, - 190, 191, 192, 193, 194, 195, 196, 197, 198, - 200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220, - 230, 231, 232, 270, 280, 281, 282, 283, 284, 285, - 290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313, - 320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413 - ]; - - - function abortRequest(controller) { - if (controller) { - controller.abort(); - } - } - - function abortUnwantedRequests(cache, tiles) { - Object.keys(cache.inflightTile).forEach(k => { - const wanted = tiles.find(tile => k === tile.id); - if (!wanted) { - abortRequest(cache.inflightTile[k]); - delete cache.inflightTile[k]; - } - }); - } - - - function encodeIssueRtree(d) { - return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d }; - } - - - // Replace or remove QAItem from rtree - function updateRtree(item, replace) { - _cache.rtree.remove(item, (a, b) => a.data.id === b.data.id); - - if (replace) { - _cache.rtree.insert(item); - } - } - - - function tokenReplacements(d) { - if (!(d instanceof QAItem)) return; - - const htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/); - const replacements = {}; - - const issueTemplate = _krData.errorTypes[d.whichType]; - if (!issueTemplate) { - /* eslint-disable no-console */ - console.log('No Template: ', d.whichType); - console.log(' ', d.description); - /* eslint-enable no-console */ - return; - } - - // some descriptions are just fixed text - if (!issueTemplate.regex) return; - - // regex pattern should match description with variable details captured - const errorRegex = new RegExp(issueTemplate.regex, 'i'); - const errorMatch = errorRegex.exec(d.description); - if (!errorMatch) { - /* eslint-disable no-console */ - console.log('Unmatched: ', d.whichType); - console.log(' ', d.description); - console.log(' ', errorRegex); - /* eslint-enable no-console */ - return; - } - - for (let i = 1; i < errorMatch.length; i++) { // skip first - let capture = errorMatch[i]; - let idType; - - idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i-1] : ''; - if (idType && capture) { // link IDs if present in the capture - capture = parseError(capture, idType); - } else if (htmlRegex.test(capture)) { // escape any html in non-IDs - capture = '\\' + capture + '\\'; - } else { - const compare = capture.toLowerCase(); - if (_krData.localizeStrings[compare]) { // some replacement strings can be localized - capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]); - } - } - - replacements['var' + i] = capture; - } - - return replacements; - } - - - function parseError(capture, idType) { - const compare = capture.toLowerCase(); - if (_krData.localizeStrings[compare]) { // some replacement strings can be localized - capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]); - } - - switch (idType) { - // link a string like "this node" - case 'this': - capture = linkErrorObject(capture); - break; - - case 'url': - capture = linkURL(capture); - break; - - // link an entity ID - case 'n': - case 'w': - case 'r': - capture = linkEntity(idType + capture); - break; - - // some errors have more complex ID lists/variance - case '20': - capture = parse20(capture); - break; - case '211': - capture = parse211(capture); - break; - case '231': - capture = parse231(capture); - break; - case '294': - capture = parse294(capture); - break; - case '370': - capture = parse370(capture); - break; - } - - return capture; - - - function linkErrorObject(d) { - return `${d}`; - } - - function linkEntity(d) { - return `${d}`; - } - - function linkURL(d) { - return `${d}`; - } - - // arbitrary node list of form: #ID, #ID, #ID... - function parse211(capture) { - let newList = []; - const items = capture.split(', '); - - items.forEach(item => { - // ID has # at the front - let id = linkEntity('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - - // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... - function parse231(capture) { - let newList = []; - // unfortunately 'layer' can itself contain commas, so we split on '),' - const items = capture.split('),'); - - items.forEach(item => { - const match = item.match(/\#(\d+)\((.+)\)?/); - if (match !== null && match.length > 2) { - newList.push(linkEntity('w' + match[1]) + ' ' + - _t('QA.keepRight.errorTypes.231.layer', { layer: match[2] }) - ); - } - }); - - return newList.join(', '); - } - - // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... - function parse294(capture) { - let newList = []; - const items = capture.split(','); - - items.forEach(item => { - // item of form "from/to node/relation #ID" - item = item.split(' '); - - // to/from role is more clear in quotes - const role = `"${item[0]}"`; - - // first letter of node/relation provides the type - const idType = item[1].slice(0,1); - - // ID has # at the front - let id = item[2].slice(1); - id = linkEntity(idType + id); - - newList.push(`${role} ${item[1]} ${id}`); - }); - - return newList.join(', '); - } - - // may or may not include the string "(including the name 'name')" - function parse370(capture) { - if (!capture) return ''; - - const match = capture.match(/\(including the name (\'.+\')\)/); - if (match && match.length) { - return _t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] }); - } - return ''; - } - - // arbitrary node list of form: #ID,#ID,#ID... - function parse20(capture) { - let newList = []; - const items = capture.split(','); - - items.forEach(item => { - // ID has # at the front - const id = linkEntity('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - } - - - var serviceKeepRight = { - title: 'keepRight', - - init() { - _mainFileFetcher.get('keepRight') - .then(d => _krData = d); - - if (!_cache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$1, 'on'); - }, - - reset() { - if (_cache) { - Object.values(_cache.inflightTile).forEach(abortRequest); - } - - _cache = { - data: {}, - loadedTile: {}, - inflightTile: {}, - inflightPost: {}, - closed: {}, - rtree: new RBush() - }; - }, - - - // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php - loadIssues(projection) { - const options = { - format: 'geojson', - ch: _krRuleset - }; - - // determine the needed tiles to cover the view - const tiles = tiler - .zoomExtent([_tileZoom, _tileZoom]) - .getTiles(projection); - - // abort inflight requests that are no longer needed - abortUnwantedRequests(_cache, tiles); - - // issue new requests.. - tiles.forEach(tile => { - if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return; - - const [ left, top, right, bottom ] = tile.extent.rectangle(); - const params = Object.assign({}, options, { left, bottom, right, top }); - const url = `${_krUrlRoot}/export.php?` + utilQsString(params); - const controller = new AbortController(); - - _cache.inflightTile[tile.id] = controller; - - d3_json(url, { signal: controller.signal }) - .then(data => { - delete _cache.inflightTile[tile.id]; - _cache.loadedTile[tile.id] = true; - if (!data || !data.features || !data.features.length) { - throw new Error('No Data'); - } - - data.features.forEach(feature => { - const { - properties: { - error_type: itemType, - error_id: id, - comment = null, - object_id: objectId, - object_type: objectType, - schema, - title - } - } = feature; - let { - geometry: { coordinates: loc }, - properties: { description = '' } - } = feature; - - // if there is a parent, save its error type e.g.: - // Error 191 = "highway-highway" - // Error 190 = "intersections without junctions" (parent) - const issueTemplate = _krData.errorTypes[itemType]; - const parentIssueType = (Math.floor(itemType / 10) * 10).toString(); - - // try to handle error type directly, fallback to parent error type. - const whichType = issueTemplate ? itemType : parentIssueType; - const whichTemplate = _krData.errorTypes[whichType]; - - // Rewrite a few of the errors at this point.. - // This is done to make them easier to linkify and translate. - switch (whichType) { - case '170': - description = `This feature has a FIXME tag: ${description}`; - break; - case '292': - case '293': - description = description.replace('A turn-', 'This turn-'); - break; - case '294': - case '295': - case '296': - case '297': - case '298': - description = `This turn-restriction~${description}`; - break; - case '300': - description = 'This highway is missing a maxspeed tag'; - break; - case '411': - case '412': - case '413': - description = `This feature~${description}`; - break; - } - - // move markers slightly so it doesn't obscure the geometry, - // then move markers away from other coincident markers - let coincident = false; - do { - // first time, move marker up. after that, move marker right. - let delta = coincident ? [0.00001, 0] : [0, 0.00001]; - loc = geoVecAdd(loc, delta); - let bbox = geoExtent(loc).bbox(); - coincident = _cache.rtree.search(bbox).length; - } while (coincident); - - let d = new QAItem(loc, this, itemType, id, { - comment, - description, - whichType, - parentIssueType, - severity: whichTemplate.severity || 'error', - objectId, - objectType, - schema, - title - }); - - d.replacements = tokenReplacements(d); - - _cache.data[id] = d; - _cache.rtree.insert(encodeIssueRtree(d)); - }); - - dispatch$1.call('loaded'); - }) - .catch(() => { - delete _cache.inflightTile[tile.id]; - _cache.loadedTile[tile.id] = true; - }); - - }); - }, - - - postUpdate(d, callback) { - if (_cache.inflightPost[d.id]) { - return callback({ message: 'Error update already inflight', status: -2 }, d); - } - - const params = { schema: d.schema, id: d.id }; - - if (d.newStatus) { - params.st = d.newStatus; - } - if (d.newComment !== undefined) { - params.co = d.newComment; - } - - // NOTE: This throws a CORS err, but it seems successful. - // We don't care too much about the response, so this is fine. - const url = `${_krUrlRoot}/comment.php?` + utilQsString(params); - const controller = new AbortController(); - - _cache.inflightPost[d.id] = controller; - - // Since this is expected to throw an error just continue as if it worked - // (worst case scenario the request truly fails and issue will show up if iD restarts) - d3_json(url, { signal: controller.signal }) - .finally(() => { - delete _cache.inflightPost[d.id]; - - if (d.newStatus === 'ignore') { - // ignore permanently (false positive) - this.removeItem(d); - } else if (d.newStatus === 'ignore_t') { - // ignore temporarily (error fixed) - this.removeItem(d); - _cache.closed[`${d.schema}:${d.id}`] = true; - } else { - d = this.replaceItem(d.update({ - comment: d.newComment, - newComment: undefined, - newState: undefined - })); - } - - if (callback) callback(null, d); - }); - }, - - // Get all cached QAItems covering the viewport - getItems(projection) { - const viewport = projection.clipExtent(); - const min = [viewport[0][0], viewport[1][1]]; - const max = [viewport[1][0], viewport[0][1]]; - const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - - return _cache.rtree.search(bbox).map(d => d.data); - }, - - // Get a QAItem from cache - // NOTE: Don't change method name until UI v3 is merged - getError(id) { - return _cache.data[id]; - }, - - // Replace a single QAItem in the cache - replaceItem(item) { - if (!(item instanceof QAItem) || !item.id) return; - - _cache.data[item.id] = item; - updateRtree(encodeIssueRtree(item), true); // true = replace - return item; - }, - - // Remove a single QAItem from the cache - removeItem(item) { - if (!(item instanceof QAItem) || !item.id) return; - - delete _cache.data[item.id]; - updateRtree(encodeIssueRtree(item), false); // false = remove - }, - - issueURL(item) { - return `${_krUrlRoot}/report_map.php?schema=${item.schema}&error=${item.id}`; - }, - - // Get an array of issues closed during this session. - // Used to populate `closed:keepright` changeset tag - getClosedIDs() { - return Object.keys(_cache.closed).sort(); - } - - }; - - const tiler$1 = utilTiler(); - const dispatch$2 = dispatch('loaded'); - const _tileZoom$1 = 14; - const _impOsmUrls = { - ow: 'https://grab.community.improve-osm.org/directionOfFlowService', - mr: 'https://grab.community.improve-osm.org/missingGeoService', - tr: 'https://grab.community.improve-osm.org/turnRestrictionService' - }; - let _impOsmData = { icons: {} }; - - - // This gets reassigned if reset - let _cache$1; - - function abortRequest$1(i) { - Object.values(i).forEach(controller => { - if (controller) { - controller.abort(); - } - }); - } - - function abortUnwantedRequests$1(cache, tiles) { - Object.keys(cache.inflightTile).forEach(k => { - const wanted = tiles.find(tile => k === tile.id); - if (!wanted) { - abortRequest$1(cache.inflightTile[k]); - delete cache.inflightTile[k]; - } - }); - } - - function encodeIssueRtree$1(d) { - return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d }; - } - - // Replace or remove QAItem from rtree - function updateRtree$1(item, replace) { - _cache$1.rtree.remove(item, (a, b) => a.data.id === b.data.id); - - if (replace) { - _cache$1.rtree.insert(item); - } - } - - function linkErrorObject(d) { - return `${d}`; - } - - function linkEntity(d) { - return `${d}`; - } - - function pointAverage(points) { - if (points.length) { - const sum = points.reduce( - (acc, point) => geoVecAdd(acc, [point.lon, point.lat]), - [0,0] - ); - return geoVecScale(sum, 1 / points.length); - } else { - return [0,0]; - } - } - - function relativeBearing(p1, p2) { - let angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat); - if (angle < 0) { - angle += 2 * Math.PI; - } - - // Return degrees - return angle * 180 / Math.PI; - } - - // Assuming range [0,360) - function cardinalDirection(bearing) { - const dir = 45 * Math.round(bearing / 45); - const compass = { - 0: 'north', - 45: 'northeast', - 90: 'east', - 135: 'southeast', - 180: 'south', - 225: 'southwest', - 270: 'west', - 315: 'northwest', - 360: 'north' - }; - - return _t(`QA.improveOSM.directions.${compass[dir]}`); - } - - // Errors shouldn't obscure eachother - function preventCoincident(loc, bumpUp) { - let coincident = false; - do { - // first time, move marker up. after that, move marker right. - let delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]); - loc = geoVecAdd(loc, delta); - let bbox = geoExtent(loc).bbox(); - coincident = _cache$1.rtree.search(bbox).length; - } while (coincident); - - return loc; - } - - var serviceImproveOSM = { - title: 'improveOSM', - - init() { - _mainFileFetcher.get('qa_data') - .then(d => _impOsmData = d.improveOSM); - - if (!_cache$1) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$2, 'on'); - }, - - reset() { - if (_cache$1) { - Object.values(_cache$1.inflightTile).forEach(abortRequest$1); - } - _cache$1 = { - data: {}, - loadedTile: {}, - inflightTile: {}, - inflightPost: {}, - closed: {}, - rtree: new RBush() - }; - }, - - loadIssues(projection) { - const options = { - client: 'iD', - status: 'OPEN', - zoom: '19' // Use a high zoom so that clusters aren't returned - }; - - // determine the needed tiles to cover the view - const tiles = tiler$1 - .zoomExtent([_tileZoom$1, _tileZoom$1]) - .getTiles(projection); - - // abort inflight requests that are no longer needed - abortUnwantedRequests$1(_cache$1, tiles); - - // issue new requests.. - tiles.forEach(tile => { - if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return; - - const [ east, north, west, south ] = tile.extent.rectangle(); - const params = Object.assign({}, options, { east, south, west, north }); - - // 3 separate requests to store for each tile - const requests = {}; - - Object.keys(_impOsmUrls).forEach(k => { - // We exclude WATER from missing geometry as it doesn't seem useful - // We use most confident one-way and turn restrictions only, still have false positives - const kParams = Object.assign({}, - params, - (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' } - ); - const url = `${_impOsmUrls[k]}/search?` + utilQsString(kParams); - const controller = new AbortController(); - - requests[k] = controller; - - d3_json(url, { signal: controller.signal }) - .then(data => { - delete _cache$1.inflightTile[tile.id][k]; - if (!Object.keys(_cache$1.inflightTile[tile.id]).length) { - delete _cache$1.inflightTile[tile.id]; - _cache$1.loadedTile[tile.id] = true; - } - - // Road segments at high zoom == oneways - if (data.roadSegments) { - data.roadSegments.forEach(feature => { - // Position error at the approximate middle of the segment - const { points, wayId, fromNodeId, toNodeId } = feature; - const itemId = `${wayId}${fromNodeId}${toNodeId}`; - let mid = points.length / 2; - let loc; - - // Even number of points, find midpoint of the middle two - // Odd number of points, use position of very middle point - if (mid % 1 === 0) { - loc = pointAverage([points[mid - 1], points[mid]]); - } else { - mid = points[Math.floor(mid)]; - loc = [mid.lon, mid.lat]; - } - - // One-ways can land on same segment in opposite direction - loc = preventCoincident(loc, false); - - let d = new QAItem(loc, this, k, itemId, { - issueKey: k, // used as a category - identifier: { // used to post changes - wayId, - fromNodeId, - toNodeId - }, - objectId: wayId, - objectType: 'way' - }); - - // Variables used in the description - d.replacements = { - percentage: feature.percentOfTrips, - num_trips: feature.numberOfTrips, - highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')), - from_node: linkEntity('n' + feature.fromNodeId), - to_node: linkEntity('n' + feature.toNodeId) - }; - - _cache$1.data[d.id] = d; - _cache$1.rtree.insert(encodeIssueRtree$1(d)); - }); - } - - // Tiles at high zoom == missing roads - if (data.tiles) { - data.tiles.forEach(feature => { - const { type, x, y, numberOfTrips } = feature; - const geoType = type.toLowerCase(); - const itemId = `${geoType}${x}${y}${numberOfTrips}`; - - // Average of recorded points should land on the missing geometry - // Missing geometry could happen to land on another error - let loc = pointAverage(feature.points); - loc = preventCoincident(loc, false); - - let d = new QAItem(loc, this, `${k}-${geoType}`, itemId, { - issueKey: k, - identifier: { x, y } - }); - - d.replacements = { - num_trips: numberOfTrips, - geometry_type: _t(`QA.improveOSM.geometry_types.${geoType}`) - }; - - // -1 trips indicates data came from a 3rd party - if (numberOfTrips === -1) { - d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements); - } - - _cache$1.data[d.id] = d; - _cache$1.rtree.insert(encodeIssueRtree$1(d)); - }); - } - - // Entities at high zoom == turn restrictions - if (data.entities) { - data.entities.forEach(feature => { - const { point, id, segments, numberOfPasses, turnType } = feature; - const itemId = `${id.replace(/[,:+#]/g, '_')}`; - - // Turn restrictions could be missing at same junction - // We also want to bump the error up so node is accessible - const loc = preventCoincident([point.lon, point.lat], true); - - // Elements are presented in a strange way - const ids = id.split(','); - const from_way = ids[0]; - const via_node = ids[3]; - const to_way = ids[2].split(':')[1]; - - let d = new QAItem(loc, this, k, itemId, { - issueKey: k, - identifier: id, - objectId: via_node, - objectType: 'node' - }); - - // Travel direction along from_way clarifies the turn restriction - const [ p1, p2 ] = segments[0].points; - const dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); - - // Variables used in the description - d.replacements = { - num_passed: numberOfPasses, - num_trips: segments[0].numberOfTrips, - turn_restriction: turnType.toLowerCase(), - from_way: linkEntity('w' + from_way), - to_way: linkEntity('w' + to_way), - travel_direction: dir_of_travel, - junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node')) - }; - - _cache$1.data[d.id] = d; - _cache$1.rtree.insert(encodeIssueRtree$1(d)); - dispatch$2.call('loaded'); - }); - } - }) - .catch(() => { - delete _cache$1.inflightTile[tile.id][k]; - if (!Object.keys(_cache$1.inflightTile[tile.id]).length) { - delete _cache$1.inflightTile[tile.id]; - _cache$1.loadedTile[tile.id] = true; - } - }); - }); - - _cache$1.inflightTile[tile.id] = requests; - }); - }, - - getComments(item) { - // If comments already retrieved no need to do so again - if (item.comments) { - return Promise.resolve(item); - } - - const key = item.issueKey; - let qParams = {}; - - if (key === 'ow') { - qParams = item.identifier; - } else if (key === 'mr') { - qParams.tileX = item.identifier.x; - qParams.tileY = item.identifier.y; - } else if (key === 'tr') { - qParams.targetId = item.identifier; - } - - const url = `${_impOsmUrls[key]}/retrieveComments?` + utilQsString(qParams); - const cacheComments = data => { - // Assign directly for immediate use afterwards - // comments are served newest to oldest - item.comments = data.comments ? data.comments.reverse() : []; - this.replaceItem(item); - }; - - return d3_json(url).then(cacheComments).then(() => item); - }, - - postUpdate(d, callback) { - if (!serviceOsm.authenticated()) { // Username required in payload - return callback({ message: 'Not Authenticated', status: -3}, d); - } - if (_cache$1.inflightPost[d.id]) { - return callback({ message: 'Error update already inflight', status: -2 }, d); - } - - // Payload can only be sent once username is established - serviceOsm.userDetails(sendPayload.bind(this)); - - function sendPayload(err, user) { - if (err) { return callback(err, d); } - - const key = d.issueKey; - const url = `${_impOsmUrls[key]}/comment`; - const payload = { - username: user.display_name, - targetIds: [ d.identifier ] - }; - - if (d.newStatus) { - payload.status = d.newStatus; - payload.text = 'status changed'; - } - - // Comment take place of default text - if (d.newComment) { - payload.text = d.newComment; - } - - const controller = new AbortController(); - _cache$1.inflightPost[d.id] = controller; - - const options = { - method: 'POST', - signal: controller.signal, - body: JSON.stringify(payload) - }; - - d3_json(url, options) - .then(() => { - delete _cache$1.inflightPost[d.id]; - - // Just a comment, update error in cache - if (!d.newStatus) { - const now = new Date(); - let comments = d.comments ? d.comments : []; - - comments.push({ - username: payload.username, - text: payload.text, - timestamp: now.getTime() / 1000 - }); - - this.replaceItem(d.update({ - comments: comments, - newComment: undefined - })); - } else { - this.removeItem(d); - if (d.newStatus === 'SOLVED') { - // Keep track of the number of issues closed per type to tag the changeset - if (!(d.issueKey in _cache$1.closed)) { - _cache$1.closed[d.issueKey] = 0; - } - _cache$1.closed[d.issueKey] += 1; - } - } - if (callback) callback(null, d); - }) - .catch(err => { - delete _cache$1.inflightPost[d.id]; - if (callback) callback(err.message); - }); - } - }, - - - // Get all cached QAItems covering the viewport - getItems(projection) { - const viewport = projection.clipExtent(); - const min = [viewport[0][0], viewport[1][1]]; - const max = [viewport[1][0], viewport[0][1]]; - const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - - return _cache$1.rtree.search(bbox).map(d => d.data); - }, - - // Get a QAItem from cache - // NOTE: Don't change method name until UI v3 is merged - getError(id) { - return _cache$1.data[id]; - }, - - // get the name of the icon to display for this item - getIcon(itemType) { - return _impOsmData.icons[itemType]; - }, - - // Replace a single QAItem in the cache - replaceItem(issue) { - if (!(issue instanceof QAItem) || !issue.id) return; - - _cache$1.data[issue.id] = issue; - updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace - return issue; - }, - - // Remove a single QAItem from the cache - removeItem(issue) { - if (!(issue instanceof QAItem) || !issue.id) return; - - delete _cache$1.data[issue.id]; - updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove - }, - - // Used to populate `closed:improveosm:*` changeset tags - getClosedCounts() { - return _cache$1.closed; - } - }; - - var defaults = createCommonjsModule(function (module) { - function getDefaults() { - return { - baseUrl: null, - breaks: false, - gfm: true, - headerIds: true, - headerPrefix: '', - highlight: null, - langPrefix: 'language-', - mangle: true, - pedantic: false, - renderer: null, - sanitize: false, - sanitizer: null, - silent: false, - smartLists: false, - smartypants: false, - tokenizer: null, - xhtml: false - }; - } - - function changeDefaults(newDefaults) { - module.exports.defaults = newDefaults; - } - - module.exports = { - defaults: getDefaults(), - getDefaults, - changeDefaults - }; - }); - - /** - * Helpers - */ - const escapeTest = /[&<>"']/; - const escapeReplace = /[&<>"']/g; - const escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; - const escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; - const escapeReplacements = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - const getEscapeReplacement = (ch) => escapeReplacements[ch]; - function escape$1(html, encode) { - if (encode) { - if (escapeTest.test(html)) { - return html.replace(escapeReplace, getEscapeReplacement); - } - } else { - if (escapeTestNoEncode.test(html)) { - return html.replace(escapeReplaceNoEncode, getEscapeReplacement); - } - } - - return html; - } - - const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; - - function unescape$1(html) { - // explicitly match decimal, hex, and named HTML entities - return html.replace(unescapeTest, (_, n) => { - n = n.toLowerCase(); - if (n === 'colon') return ':'; - if (n.charAt(0) === '#') { - return n.charAt(1) === 'x' - ? String.fromCharCode(parseInt(n.substring(2), 16)) - : String.fromCharCode(+n.substring(1)); - } - return ''; - }); - } - - const caret = /(^|[^\[])\^/g; - function edit(regex, opt) { - regex = regex.source || regex; - opt = opt || ''; - const obj = { - replace: (name, val) => { - val = val.source || val; - val = val.replace(caret, '$1'); - regex = regex.replace(name, val); - return obj; - }, - getRegex: () => { - return new RegExp(regex, opt); - } - }; - return obj; - } - - const nonWordAndColonTest = /[^\w:]/g; - const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - function cleanUrl(sanitize, base, href) { - if (sanitize) { - let prot; - try { - prot = decodeURIComponent(unescape$1(href)) - .replace(nonWordAndColonTest, '') - .toLowerCase(); - } catch (e) { - return null; - } - if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { - return null; - } - } - if (base && !originIndependentUrl.test(href)) { - href = resolveUrl(base, href); - } - try { - href = encodeURI(href).replace(/%25/g, '%'); - } catch (e) { - return null; - } - return href; - } - - const baseUrls = {}; - const justDomain = /^[^:]+:\/*[^/]*$/; - const protocol = /^([^:]+:)[\s\S]*$/; - const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; - - function resolveUrl(base, href) { - if (!baseUrls[' ' + base]) { - // we can ignore everything in base after the last slash of its path component, - // but we might need to add _that_ - // https://tools.ietf.org/html/rfc3986#section-3 - if (justDomain.test(base)) { - baseUrls[' ' + base] = base + '/'; - } else { - baseUrls[' ' + base] = rtrim(base, '/', true); - } - } - base = baseUrls[' ' + base]; - const relativeBase = base.indexOf(':') === -1; - - if (href.substring(0, 2) === '//') { - if (relativeBase) { - return href; - } - return base.replace(protocol, '$1') + href; - } else if (href.charAt(0) === '/') { - if (relativeBase) { - return href; - } - return base.replace(domain, '$1') + href; - } else { - return base + href; - } - } - - const noopTest = { exec: function noopTest() {} }; - - function merge$1(obj) { - let i = 1, - target, - key; - - for (; i < arguments.length; i++) { - target = arguments[i]; - for (key in target) { - if (Object.prototype.hasOwnProperty.call(target, key)) { - obj[key] = target[key]; - } - } - } - - return obj; - } - - function splitCells(tableRow, count) { - // ensure that every cell-delimiting pipe has a space - // before it to distinguish it from an escaped pipe - const row = tableRow.replace(/\|/g, (match, offset, str) => { - let escaped = false, - curr = offset; - while (--curr >= 0 && str[curr] === '\\') escaped = !escaped; - if (escaped) { - // odd number of slashes means | is escaped - // so we leave it alone - return '|'; - } else { - // add space before unescaped | - return ' |'; - } - }), - cells = row.split(/ \|/); - let i = 0; - - if (cells.length > count) { - cells.splice(count); - } else { - while (cells.length < count) cells.push(''); - } - - for (; i < cells.length; i++) { - // leading or trailing whitespace is ignored per the gfm spec - cells[i] = cells[i].trim().replace(/\\\|/g, '|'); - } - return cells; - } - - // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). - // /c*$/ is vulnerable to REDOS. - // invert: Remove suffix of non-c chars instead. Default falsey. - function rtrim(str, c, invert) { - const l = str.length; - if (l === 0) { - return ''; - } - - // Length of suffix matching the invert condition. - let suffLen = 0; - - // Step left until we fail to match the invert condition. - while (suffLen < l) { - const currChar = str.charAt(l - suffLen - 1); - if (currChar === c && !invert) { - suffLen++; - } else if (currChar !== c && invert) { - suffLen++; - } else { - break; - } - } - - return str.substr(0, l - suffLen); - } - - function findClosingBracket(str, b) { - if (str.indexOf(b[1]) === -1) { - return -1; - } - const l = str.length; - let level = 0, - i = 0; - for (; i < l; i++) { - if (str[i] === '\\') { - i++; - } else if (str[i] === b[0]) { - level++; - } else if (str[i] === b[1]) { - level--; - if (level < 0) { - return i; - } - } - } - return -1; - } - - function checkSanitizeDeprecation(opt) { - if (opt && opt.sanitize && !opt.silent) { - console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); - } - } - - var helpers = { - escape: escape$1, - unescape: unescape$1, - edit, - cleanUrl, - resolveUrl, - noopTest, - merge: merge$1, - splitCells, - rtrim, - findClosingBracket, - checkSanitizeDeprecation - }; - - const { defaults: defaults$1 } = defaults; - const { - rtrim: rtrim$1, - splitCells: splitCells$1, - escape: escape$2, - findClosingBracket: findClosingBracket$1 - } = helpers; - - function outputLink(cap, link, raw) { - const href = link.href; - const title = link.title ? escape$2(link.title) : null; - - if (cap[0].charAt(0) !== '!') { - return { - type: 'link', - raw, - href, - title, - text: cap[1] - }; - } else { - return { - type: 'image', - raw, - text: escape$2(cap[1]), - href, - title - }; - } - } - - /** - * Tokenizer - */ - var Tokenizer_1 = class Tokenizer { - constructor(options) { - this.options = options || defaults$1; - } - - space(src) { - const cap = this.rules.block.newline.exec(src); - if (cap) { - if (cap[0].length > 1) { - return { - type: 'space', - raw: cap[0] - }; - } - return { raw: '\n' }; - } - } - - code(src, tokens) { - const cap = this.rules.block.code.exec(src); - if (cap) { - const lastToken = tokens[tokens.length - 1]; - // An indented code block cannot interrupt a paragraph. - if (lastToken && lastToken.type === 'paragraph') { - tokens.pop(); - lastToken.text += '\n' + cap[0].trimRight(); - lastToken.raw += '\n' + cap[0]; - return lastToken; - } else { - const text = cap[0].replace(/^ {4}/gm, ''); - return { - type: 'code', - raw: cap[0], - codeBlockStyle: 'indented', - text: !this.options.pedantic - ? rtrim$1(text, '\n') - : text - }; - } - } - } - - fences(src) { - const cap = this.rules.block.fences.exec(src); - if (cap) { - return { - type: 'code', - raw: cap[0], - lang: cap[2] ? cap[2].trim() : cap[2], - text: cap[3] || '' - }; - } - } - - heading(src) { - const cap = this.rules.block.heading.exec(src); - if (cap) { - return { - type: 'heading', - raw: cap[0], - depth: cap[1].length, - text: cap[2] - }; - } - } - - nptable(src) { - const cap = this.rules.block.nptable.exec(src); - if (cap) { - const item = { - type: 'table', - header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], - raw: cap[0] - }; - - if (item.header.length === item.align.length) { - let l = item.align.length; - let i; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1(item.cells[i], item.header.length); - } - - return item; - } - } - } - - hr(src) { - const cap = this.rules.block.hr.exec(src); - if (cap) { - return { - type: 'hr', - raw: cap[0] - }; - } - } - - blockquote(src) { - const cap = this.rules.block.blockquote.exec(src); - if (cap) { - const text = cap[0].replace(/^ *> ?/gm, ''); - - return { - type: 'blockquote', - raw: cap[0], - text - }; - } - } - - list(src) { - const cap = this.rules.block.list.exec(src); - if (cap) { - let raw = cap[0]; - const bull = cap[2]; - const isordered = bull.length > 1; - - const list = { - type: 'list', - raw, - ordered: isordered, - start: isordered ? +bull : '', - loose: false, - items: [] - }; - - // Get each top-level item. - const itemMatch = cap[0].match(this.rules.block.item); - - let next = false, - item, - space, - b, - addBack, - loose, - istask, - ischecked; - - const l = itemMatch.length; - for (let i = 0; i < l; i++) { - item = itemMatch[i]; - raw = item; - - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) */, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); - } - - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - b = this.rules.block.bullet.exec(itemMatch[i + 1])[0]; - if (bull.length > 1 ? b.length === 1 - : (b.length > 1 || (this.options.smartLists && b !== bull))) { - addBack = itemMatch.slice(i + 1).join('\n'); - list.raw = list.raw.substring(0, list.raw.length - addBack.length); - i = l - 1; - } - } - - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(item); - if (i !== l - 1) { - next = item.charAt(item.length - 1) === '\n'; - if (!loose) loose = next; - } - - if (loose) { - list.loose = true; - } - - // Check for task list items - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; - if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); - } - - list.items.push({ - raw, - task: istask, - checked: ischecked, - loose: loose, - text: item - }); - } - - return list; - } - } - - html(src) { - const cap = this.rules.block.html.exec(src); - if (cap) { - return { - type: this.options.sanitize - ? 'paragraph' - : 'html', - raw: cap[0], - pre: !this.options.sanitizer - && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0] - }; - } - } - - def(src) { - const cap = this.rules.block.def.exec(src); - if (cap) { - if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); - const tag = cap[1].toLowerCase().replace(/\s+/g, ' '); - return { - tag, - raw: cap[0], - href: cap[2], - title: cap[3] - }; - } - } - - table(src) { - const cap = this.rules.block.table.exec(src); - if (cap) { - const item = { - type: 'table', - header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; - - if (item.header.length === item.align.length) { - item.raw = cap[0]; - - let l = item.align.length; - let i; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells$1( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); - } - - return item; - } - } - } - - lheading(src) { - const cap = this.rules.block.lheading.exec(src); - if (cap) { - return { - type: 'heading', - raw: cap[0], - depth: cap[2].charAt(0) === '=' ? 1 : 2, - text: cap[1] - }; - } - } - - paragraph(src) { - const cap = this.rules.block.paragraph.exec(src); - if (cap) { - return { - type: 'paragraph', - raw: cap[0], - text: cap[1].charAt(cap[1].length - 1) === '\n' - ? cap[1].slice(0, -1) - : cap[1] - }; - } - } - - text(src) { - const cap = this.rules.block.text.exec(src); - if (cap) { - return { - type: 'text', - raw: cap[0], - text: cap[0] - }; - } - } - - escape(src) { - const cap = this.rules.inline.escape.exec(src); - if (cap) { - return { - type: 'escape', - raw: cap[0], - text: escape$2(cap[1]) - }; - } - } - - tag(src, inLink, inRawBlock) { - const cap = this.rules.inline.tag.exec(src); - if (cap) { - if (!inLink && /^/i.test(cap[0])) { - inLink = false; - } - if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = true; - } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - inRawBlock = false; - } - - return { - type: this.options.sanitize - ? 'text' - : 'html', - raw: cap[0], - inLink, - inRawBlock, - text: this.options.sanitize - ? (this.options.sanitizer - ? this.options.sanitizer(cap[0]) - : escape$2(cap[0])) - : cap[0] - }; - } - } - - link(src) { - const cap = this.rules.inline.link.exec(src); - if (cap) { - const lastParenIndex = findClosingBracket$1(cap[2], '()'); - if (lastParenIndex > -1) { - const start = cap[0].indexOf('!') === 0 ? 5 : 4; - const linkLen = start + cap[1].length + lastParenIndex; - cap[2] = cap[2].substring(0, lastParenIndex); - cap[0] = cap[0].substring(0, linkLen).trim(); - cap[3] = ''; - } - let href = cap[2]; - let title = ''; - if (this.options.pedantic) { - const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); - - if (link) { - href = link[1]; - title = link[3]; - } else { - title = ''; - } - } else { - title = cap[3] ? cap[3].slice(1, -1) : ''; - } - href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); - const token = outputLink(cap, { - href: href ? href.replace(this.rules.inline._escapes, '$1') : href, - title: title ? title.replace(this.rules.inline._escapes, '$1') : title - }, cap[0]); - return token; - } - } - - reflink(src, links) { - let cap; - if ((cap = this.rules.inline.reflink.exec(src)) - || (cap = this.rules.inline.nolink.exec(src))) { - let link = (cap[2] || cap[1]).replace(/\s+/g, ' '); - link = links[link.toLowerCase()]; - if (!link || !link.href) { - const text = cap[0].charAt(0); - return { - type: 'text', - raw: text, - text - }; - } - const token = outputLink(cap, link, cap[0]); - return token; - } - } - - strong(src) { - const cap = this.rules.inline.strong.exec(src); - if (cap) { - return { - type: 'strong', - raw: cap[0], - text: cap[4] || cap[3] || cap[2] || cap[1] - }; - } - } - - em(src) { - const cap = this.rules.inline.em.exec(src); - if (cap) { - return { - type: 'em', - raw: cap[0], - text: cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1] - }; - } - } - - codespan(src) { - const cap = this.rules.inline.code.exec(src); - if (cap) { - return { - type: 'codespan', - raw: cap[0], - text: escape$2(cap[2].trim(), true) - }; - } - } - - br(src) { - const cap = this.rules.inline.br.exec(src); - if (cap) { - return { - type: 'br', - raw: cap[0] - }; - } - } - - del(src) { - const cap = this.rules.inline.del.exec(src); - if (cap) { - return { - type: 'del', - raw: cap[0], - text: cap[1] - }; - } - } - - autolink(src, mangle) { - const cap = this.rules.inline.autolink.exec(src); - if (cap) { - let text, href; - if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]); - href = 'mailto:' + text; - } else { - text = escape$2(cap[1]); - href = text; - } - - return { - type: 'link', - raw: cap[0], - text, - href, - tokens: [ - { - type: 'text', - raw: text, - text - } - ] - }; - } - } - - url(src, mangle) { - let cap; - if (cap = this.rules.inline.url.exec(src)) { - let text, href; - if (cap[2] === '@') { - text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]); - href = 'mailto:' + text; - } else { - // do extended autolink path validation - let prevCapZero; - do { - prevCapZero = cap[0]; - cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; - } while (prevCapZero !== cap[0]); - text = escape$2(cap[0]); - if (cap[1] === 'www.') { - href = 'http://' + text; - } else { - href = text; - } - } - return { - type: 'link', - raw: cap[0], - text, - href, - tokens: [ - { - type: 'text', - raw: text, - text - } - ] - }; - } - } - - inlineText(src, inRawBlock, smartypants) { - const cap = this.rules.inline.text.exec(src); - if (cap) { - let text; - if (inRawBlock) { - text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]; - } else { - text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]); - } - return { - type: 'text', - raw: cap[0], - text - }; - } - } - }; - - const { - noopTest: noopTest$1, - edit: edit$1, - merge: merge$2 - } = helpers; - - /** - * Block-Level Grammar - */ - const block = { - newline: /^\n+/, - code: /^( {4}[^\n]+\n*)+/, - fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, - hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, - heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/, - blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, - html: '^ {0,3}(?:' // optional indentation - + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) - + '|comment[^\\n]*(\\n+|$)' // (2) - + '|<\\?[\\s\\S]*?\\?>\\n*' // (3) - + '|\\n*' // (4) - + '|\\n*' // (5) - + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) - + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag - + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag - + ')', - def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, - nptable: noopTest$1, - table: noopTest$1, - lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/, - // regex template, placeholders will be replaced according to different paragraph - // interruption rules of commonmark and the original markdown spec: - _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/, - text: /^[^\n]+/ - }; - - block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/; - block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/; - block.def = edit$1(block.def) - .replace('label', block._label) - .replace('title', block._title) - .getRegex(); - - block.bullet = /(?:[*+-]|\d{1,9}\.)/; - block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; - block.item = edit$1(block.item, 'gm') - .replace(/bull/g, block.bullet) - .getRegex(); - - block.list = edit$1(block.list) - .replace(/bull/g, block.bullet) - .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') - .replace('def', '\\n+(?=' + block.def.source + ')') - .getRegex(); - - block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' - + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' - + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' - + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' - + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' - + '|track|ul'; - block._comment = //; - block.html = edit$1(block.html, 'i') - .replace('comment', block._comment) - .replace('tag', block._tag) - .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) - .getRegex(); - - block.paragraph = edit$1(block._paragraph) - .replace('hr', block.hr) - .replace('heading', ' {0,3}#{1,6} ') - .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs - .replace('blockquote', ' {0,3}>') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks - .getRegex(); - - block.blockquote = edit$1(block.blockquote) - .replace('paragraph', block.paragraph) - .getRegex(); - - /** - * Normal Block Grammar - */ - - block.normal = merge$2({}, block); - - /** - * GFM Block Grammar - */ - - block.gfm = merge$2({}, block.normal, { - nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header - + ' *([-:]+ *\\|[-| :]*)' // Align - + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells - table: '^ *\\|(.+)\\n' // Header - + ' *\\|?( *[-:]+[-| :]*)' // Align - + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells - }); - - block.gfm.nptable = edit$1(block.gfm.nptable) - .replace('hr', block.hr) - .replace('heading', ' {0,3}#{1,6} ') - .replace('blockquote', ' {0,3}>') - .replace('code', ' {4}[^\\n]') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks - .getRegex(); - - block.gfm.table = edit$1(block.gfm.table) - .replace('hr', block.hr) - .replace('heading', ' {0,3}#{1,6} ') - .replace('blockquote', ' {0,3}>') - .replace('code', ' {4}[^\\n]') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') - .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt - .replace('html', ')|<(?:script|pre|style|!--)') - .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks - .getRegex(); - - /** - * Pedantic grammar (original John Gruber's loose markdown specification) - */ - - block.pedantic = merge$2({}, block.normal, { - html: edit$1( - '^ *(?:comment *(?:\\n|\\s*$)' - + '|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)' // closed tag - + '|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))') - .replace('comment', block._comment) - .replace(/tag/g, '(?!(?:' - + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' - + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' - + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b') - .getRegex(), - def: /^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, - heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, - fences: noopTest$1, // fences not supported - paragraph: edit$1(block.normal._paragraph) - .replace('hr', block.hr) - .replace('heading', ' *#{1,6} *[^\n]') - .replace('lheading', block.lheading) - .replace('blockquote', ' {0,3}>') - .replace('|fences', '') - .replace('|list', '') - .replace('|html', '') - .getRegex() - }); - - /** - * Inline-Level Grammar - */ - const inline = { - escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, - autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noopTest$1, - tag: '^comment' - + '|^' // self-closing tag - + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag - + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. - + '|^' // declaration, e.g. - + '|^', // CDATA section - link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/, - reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, - nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, - strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, - em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/, - code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, - br: /^( {2,}|\\)\n(?!\s*$)/, - del: noopTest$1, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~'; - inline.em = edit$1(inline.em).replace(/punctuation/g, inline._punctuation).getRegex(); - - inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; - - inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; - inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/; - inline.autolink = edit$1(inline.autolink) - .replace('scheme', inline._scheme) - .replace('email', inline._email) - .getRegex(); - - inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; - - inline.tag = edit$1(inline.tag) - .replace('comment', block._comment) - .replace('attribute', inline._attribute) - .getRegex(); - - inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; - inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/; - inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; - - inline.link = edit$1(inline.link) - .replace('label', inline._label) - .replace('href', inline._href) - .replace('title', inline._title) - .getRegex(); - - inline.reflink = edit$1(inline.reflink) - .replace('label', inline._label) - .getRegex(); - - /** - * Normal Inline Grammar - */ - - inline.normal = merge$2({}, inline); - - /** - * Pedantic Inline Grammar - */ - - inline.pedantic = merge$2({}, inline.normal, { - strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, - em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/, - link: edit$1(/^!?\[(label)\]\((.*?)\)/) - .replace('label', inline._label) - .getRegex(), - reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/) - .replace('label', inline._label) - .getRegex() - }); - - /** - * GFM Inline Grammar - */ - - inline.gfm = merge$2({}, inline.normal, { - escape: edit$1(inline.escape).replace('])', '~|])').getRegex(), - _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, - url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, - _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, - del: /^~+(?=\S)([\s\S]*?\S)~+/, - text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\ 0.5) { - ch = 'x' + ch.toString(16); - } - out += '&#' + ch + ';'; - } - - return out; - } - - /** - * Block Lexer - */ - var Lexer_1 = class Lexer { - constructor(options) { - this.tokens = []; - this.tokens.links = Object.create(null); - this.options = options || defaults$2; - this.options.tokenizer = this.options.tokenizer || new Tokenizer_1(); - this.tokenizer = this.options.tokenizer; - this.tokenizer.options = this.options; - - const rules = { - block: block$1.normal, - inline: inline$1.normal - }; - - if (this.options.pedantic) { - rules.block = block$1.pedantic; - rules.inline = inline$1.pedantic; - } else if (this.options.gfm) { - rules.block = block$1.gfm; - if (this.options.breaks) { - rules.inline = inline$1.breaks; - } else { - rules.inline = inline$1.gfm; - } - } - this.tokenizer.rules = rules; - } - - /** - * Expose Rules - */ - static get rules() { - return { - block: block$1, - inline: inline$1 - }; - } - - /** - * Static Lex Method - */ - static lex(src, options) { - const lexer = new Lexer(options); - return lexer.lex(src); - } - - /** - * Preprocessing - */ - lex(src) { - src = src - .replace(/\r\n|\r/g, '\n') - .replace(/\t/g, ' '); - - this.blockTokens(src, this.tokens, true); - - this.inline(this.tokens); - - return this.tokens; - } - - /** - * Lexing - */ - blockTokens(src, tokens = [], top = true) { - src = src.replace(/^ +$/gm, ''); - let token, i, l; - - while (src) { - // newline - if (token = this.tokenizer.space(src)) { - src = src.substring(token.raw.length); - if (token.type) { - tokens.push(token); - } - continue; - } - - // code - if (token = this.tokenizer.code(src, tokens)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // fences - if (token = this.tokenizer.fences(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // heading - if (token = this.tokenizer.heading(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // table no leading pipe (gfm) - if (token = this.tokenizer.nptable(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // hr - if (token = this.tokenizer.hr(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // blockquote - if (token = this.tokenizer.blockquote(src)) { - src = src.substring(token.raw.length); - token.tokens = this.blockTokens(token.text, [], top); - tokens.push(token); - continue; - } - - // list - if (token = this.tokenizer.list(src)) { - src = src.substring(token.raw.length); - l = token.items.length; - for (i = 0; i < l; i++) { - token.items[i].tokens = this.blockTokens(token.items[i].text, [], false); - } - tokens.push(token); - continue; - } - - // html - if (token = this.tokenizer.html(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // def - if (top && (token = this.tokenizer.def(src))) { - src = src.substring(token.raw.length); - if (!this.tokens.links[token.tag]) { - this.tokens.links[token.tag] = { - href: token.href, - title: token.title - }; - } - continue; - } - - // table (gfm) - if (token = this.tokenizer.table(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // lheading - if (token = this.tokenizer.lheading(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // top-level paragraph - if (top && (token = this.tokenizer.paragraph(src))) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // text - if (token = this.tokenizer.text(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - if (src) { - const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); - if (this.options.silent) { - console.error(errMsg); - break; - } else { - throw new Error(errMsg); - } - } - } - - return tokens; - } - - inline(tokens) { - let i, - j, - k, - l2, - row, - token; - - const l = tokens.length; - for (i = 0; i < l; i++) { - token = tokens[i]; - switch (token.type) { - case 'paragraph': - case 'text': - case 'heading': { - token.tokens = []; - this.inlineTokens(token.text, token.tokens); - break; - } - case 'table': { - token.tokens = { - header: [], - cells: [] - }; - - // header - l2 = token.header.length; - for (j = 0; j < l2; j++) { - token.tokens.header[j] = []; - this.inlineTokens(token.header[j], token.tokens.header[j]); - } - - // cells - l2 = token.cells.length; - for (j = 0; j < l2; j++) { - row = token.cells[j]; - token.tokens.cells[j] = []; - for (k = 0; k < row.length; k++) { - token.tokens.cells[j][k] = []; - this.inlineTokens(row[k], token.tokens.cells[j][k]); - } - } - - break; - } - case 'blockquote': { - this.inline(token.tokens); - break; - } - case 'list': { - l2 = token.items.length; - for (j = 0; j < l2; j++) { - this.inline(token.items[j].tokens); - } - break; - } - } - } - - return tokens; - } - - /** - * Lexing/Compiling - */ - inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) { - let token; - - while (src) { - // escape - if (token = this.tokenizer.escape(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // tag - if (token = this.tokenizer.tag(src, inLink, inRawBlock)) { - src = src.substring(token.raw.length); - inLink = token.inLink; - inRawBlock = token.inRawBlock; - tokens.push(token); - continue; - } - - // link - if (token = this.tokenizer.link(src)) { - src = src.substring(token.raw.length); - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - } - tokens.push(token); - continue; - } - - // reflink, nolink - if (token = this.tokenizer.reflink(src, this.tokens.links)) { - src = src.substring(token.raw.length); - if (token.type === 'link') { - token.tokens = this.inlineTokens(token.text, [], true, inRawBlock); - } - tokens.push(token); - continue; - } - - // strong - if (token = this.tokenizer.strong(src)) { - src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); - tokens.push(token); - continue; - } - - // em - if (token = this.tokenizer.em(src)) { - src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); - tokens.push(token); - continue; - } - - // code - if (token = this.tokenizer.codespan(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // br - if (token = this.tokenizer.br(src)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // del (gfm) - if (token = this.tokenizer.del(src)) { - src = src.substring(token.raw.length); - token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock); - tokens.push(token); - continue; - } - - // autolink - if (token = this.tokenizer.autolink(src, mangle)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // url (gfm) - if (!inLink && (token = this.tokenizer.url(src, mangle))) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - // text - if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { - src = src.substring(token.raw.length); - tokens.push(token); - continue; - } - - if (src) { - const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); - if (this.options.silent) { - console.error(errMsg); - break; - } else { - throw new Error(errMsg); - } - } - } - - return tokens; - } - }; - - const { defaults: defaults$3 } = defaults; - const { - cleanUrl: cleanUrl$1, - escape: escape$3 - } = helpers; - - /** - * Renderer - */ - var Renderer_1 = class Renderer { - constructor(options) { - this.options = options || defaults$3; - } - - code(code, infostring, escaped) { - const lang = (infostring || '').match(/\S*/)[0]; - if (this.options.highlight) { - const out = this.options.highlight(code, lang); - if (out != null && out !== code) { - escaped = true; - code = out; - } - } - - if (!lang) { - return '
'
-	        + (escaped ? code : escape$3(code, true))
-	        + '
'; - } - - return '
'
-	      + (escaped ? code : escape$3(code, true))
-	      + '
\n'; - } - - blockquote(quote) { - return '
\n' + quote + '
\n'; - } - - html(html) { - return html; - } - - heading(text, level, raw, slugger) { - if (this.options.headerIds) { - return '' - + text - + '\n'; - } - // ignore IDs - return '' + text + '\n'; - } - - hr() { - return this.options.xhtml ? '
\n' : '
\n'; - } - - list(body, ordered, start) { - const type = ordered ? 'ol' : 'ul', - startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; - return '<' + type + startatt + '>\n' + body + '\n'; - } - - listitem(text) { - return '
  • ' + text + '
  • \n'; - } - - checkbox(checked) { - return ' '; - } - - paragraph(text) { - return '

    ' + text + '

    \n'; - } - - table(header, body) { - if (body) body = '' + body + ''; - - return '\n' - + '\n' - + header - + '\n' - + body - + '
    \n'; - } - - tablerow(content) { - return '\n' + content + '\n'; - } - - tablecell(content, flags) { - const type = flags.header ? 'th' : 'td'; - const tag = flags.align - ? '<' + type + ' align="' + flags.align + '">' - : '<' + type + '>'; - return tag + content + '\n'; - } - - // span level renderer - strong(text) { - return '' + text + ''; - } - - em(text) { - return '' + text + ''; - } - - codespan(text) { - return '' + text + ''; - } - - br() { - return this.options.xhtml ? '
    ' : '
    '; - } - - del(text) { - return '' + text + ''; - } - - link(href, title, text) { - href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } - let out = '
    '; - return out; - } - - image(href, title, text) { - href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } - - let out = '' + text + '' : '>'; - return out; - } - - text(text) { - return text; - } - }; - - /** - * TextRenderer - * returns only the textual part of the token - */ - var TextRenderer_1 = class TextRenderer { - // no need for block level renderers - strong(text) { - return text; - } - - em(text) { - return text; - } - - codespan(text) { - return text; - } - - del(text) { - return text; - } - - html(text) { - return text; - } - - text(text) { - return text; - } - - link(href, title, text) { - return '' + text; - } - - image(href, title, text) { - return '' + text; - } - - br() { - return ''; - } - }; - - /** - * Slugger generates header id - */ - var Slugger_1 = class Slugger { - constructor() { - this.seen = {}; - } - - /** - * Convert string to unique id - */ - slug(value) { - let slug = value - .toLowerCase() - .trim() - // remove html tags - .replace(/<[!\/a-z].*?>/ig, '') - // remove unwanted chars - .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') - .replace(/\s/g, '-'); - - if (this.seen.hasOwnProperty(slug)) { - const originalSlug = slug; - do { - this.seen[originalSlug]++; - slug = originalSlug + '-' + this.seen[originalSlug]; - } while (this.seen.hasOwnProperty(slug)); - } - this.seen[slug] = 0; - - return slug; - } - }; - - const { defaults: defaults$4 } = defaults; - const { - unescape: unescape$2 - } = helpers; - - /** - * Parsing & Compiling - */ - var Parser_1 = class Parser { - constructor(options) { - this.options = options || defaults$4; - this.options.renderer = this.options.renderer || new Renderer_1(); - this.renderer = this.options.renderer; - this.renderer.options = this.options; - this.textRenderer = new TextRenderer_1(); - this.slugger = new Slugger_1(); - } - - /** - * Static Parse Method - */ - static parse(tokens, options) { - const parser = new Parser(options); - return parser.parse(tokens); - } - - /** - * Parse Loop - */ - parse(tokens, top = true) { - let out = '', - i, - j, - k, - l2, - l3, - row, - cell, - header, - body, - token, - ordered, - start, - loose, - itemBody, - item, - checked, - task, - checkbox; - - const l = tokens.length; - for (i = 0; i < l; i++) { - token = tokens[i]; - switch (token.type) { - case 'space': { - continue; - } - case 'hr': { - out += this.renderer.hr(); - continue; - } - case 'heading': { - out += this.renderer.heading( - this.parseInline(token.tokens), - token.depth, - unescape$2(this.parseInline(token.tokens, this.textRenderer)), - this.slugger); - continue; - } - case 'code': { - out += this.renderer.code(token.text, - token.lang, - token.escaped); - continue; - } - case 'table': { - header = ''; - - // header - cell = ''; - l2 = token.header.length; - for (j = 0; j < l2; j++) { - cell += this.renderer.tablecell( - this.parseInline(token.tokens.header[j]), - { header: true, align: token.align[j] } - ); - } - header += this.renderer.tablerow(cell); - - body = ''; - l2 = token.cells.length; - for (j = 0; j < l2; j++) { - row = token.tokens.cells[j]; - - cell = ''; - l3 = row.length; - for (k = 0; k < l3; k++) { - cell += this.renderer.tablecell( - this.parseInline(row[k]), - { header: false, align: token.align[k] } - ); - } - - body += this.renderer.tablerow(cell); - } - out += this.renderer.table(header, body); - continue; - } - case 'blockquote': { - body = this.parse(token.tokens); - out += this.renderer.blockquote(body); - continue; - } - case 'list': { - ordered = token.ordered; - start = token.start; - loose = token.loose; - l2 = token.items.length; - - body = ''; - for (j = 0; j < l2; j++) { - item = token.items[j]; - checked = item.checked; - task = item.task; - - itemBody = ''; - if (item.task) { - checkbox = this.renderer.checkbox(checked); - if (loose) { - if (item.tokens[0].type === 'text') { - item.tokens[0].text = checkbox + ' ' + item.tokens[0].text; - if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') { - item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text; - } - } else { - item.tokens.unshift({ - type: 'text', - text: checkbox - }); - } - } else { - itemBody += checkbox; - } - } - - itemBody += this.parse(item.tokens, loose); - body += this.renderer.listitem(itemBody, task, checked); - } - - out += this.renderer.list(body, ordered, start); - continue; - } - case 'html': { - // TODO parse inline content if parameter markdown=1 - out += this.renderer.html(token.text); - continue; - } - case 'paragraph': { - out += this.renderer.paragraph(this.parseInline(token.tokens)); - continue; - } - case 'text': { - body = token.tokens ? this.parseInline(token.tokens) : token.text; - while (i + 1 < l && tokens[i + 1].type === 'text') { - token = tokens[++i]; - body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text); - } - out += top ? this.renderer.paragraph(body) : body; - continue; - } - default: { - const errMsg = 'Token with "' + token.type + '" type was not found.'; - if (this.options.silent) { - console.error(errMsg); - return; - } else { - throw new Error(errMsg); - } - } - } - } - - return out; - } - - /** - * Parse Inline Tokens - */ - parseInline(tokens, renderer) { - renderer = renderer || this.renderer; - let out = '', - i, - token; - - const l = tokens.length; - for (i = 0; i < l; i++) { - token = tokens[i]; - switch (token.type) { - case 'escape': { - out += renderer.text(token.text); - break; - } - case 'html': { - out += renderer.html(token.text); - break; - } - case 'link': { - out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer)); - break; - } - case 'image': { - out += renderer.image(token.href, token.title, token.text); - break; - } - case 'strong': { - out += renderer.strong(this.parseInline(token.tokens, renderer)); - break; - } - case 'em': { - out += renderer.em(this.parseInline(token.tokens, renderer)); - break; - } - case 'codespan': { - out += renderer.codespan(token.text); - break; - } - case 'br': { - out += renderer.br(); - break; - } - case 'del': { - out += renderer.del(this.parseInline(token.tokens, renderer)); - break; - } - case 'text': { - out += renderer.text(token.text); - break; - } - default: { - const errMsg = 'Token with "' + token.type + '" type was not found.'; - if (this.options.silent) { - console.error(errMsg); - return; - } else { - throw new Error(errMsg); - } - } - } - } - return out; - } - }; - - const { - merge: merge$3, - checkSanitizeDeprecation: checkSanitizeDeprecation$1, - escape: escape$4 - } = helpers; - const { - getDefaults, - changeDefaults, - defaults: defaults$5 - } = defaults; - - /** - * Marked - */ - function marked(src, opt, callback) { - // throw error in case of non string input - if (typeof src === 'undefined' || src === null) { - throw new Error('marked(): input parameter is undefined or null'); - } - if (typeof src !== 'string') { - throw new Error('marked(): input parameter is of type ' - + Object.prototype.toString.call(src) + ', string expected'); - } - - if (callback || typeof opt === 'function') { - if (!callback) { - callback = opt; - opt = null; - } - - opt = merge$3({}, marked.defaults, opt || {}); - checkSanitizeDeprecation$1(opt); - const highlight = opt.highlight; - let tokens, - pending, - i = 0; - - try { - tokens = Lexer_1.lex(src, opt); - } catch (e) { - return callback(e); - } - - pending = tokens.length; - - const done = function(err) { - if (err) { - opt.highlight = highlight; - return callback(err); - } - - let out; - - try { - out = Parser_1.parse(tokens, opt); - } catch (e) { - err = e; - } - - opt.highlight = highlight; - - return err - ? callback(err) - : callback(null, out); - }; - - if (!highlight || highlight.length < 3) { - return done(); - } - - delete opt.highlight; - - if (!pending) return done(); - - for (; i < tokens.length; i++) { - (function(token) { - if (token.type !== 'code') { - return --pending || done(); - } - return highlight(token.text, token.lang, function(err, code) { - if (err) return done(err); - if (code == null || code === token.text) { - return --pending || done(); - } - token.text = code; - token.escaped = true; - --pending || done(); - }); - })(tokens[i]); - } - - return; - } - try { - opt = merge$3({}, marked.defaults, opt || {}); - checkSanitizeDeprecation$1(opt); - return Parser_1.parse(Lexer_1.lex(src, opt), opt); - } catch (e) { - e.message += '\nPlease report this to https://github.com/markedjs/marked.'; - if ((opt || marked.defaults).silent) { - return '

    An error occurred:

    '
    -	        + escape$4(e.message + '', true)
    -	        + '
    '; - } - throw e; - } - } - - /** - * Options - */ - - marked.options = - marked.setOptions = function(opt) { - merge$3(marked.defaults, opt); - changeDefaults(marked.defaults); - return marked; - }; - - marked.getDefaults = getDefaults; - - marked.defaults = defaults$5; - - /** - * Use Extension - */ - - marked.use = function(extension) { - const opts = merge$3({}, extension); - if (extension.renderer) { - const renderer = marked.defaults.renderer || new Renderer_1(); - for (const prop in extension.renderer) { - const prevRenderer = renderer[prop]; - renderer[prop] = (...args) => { - let ret = extension.renderer[prop].apply(renderer, args); - if (ret === false) { - ret = prevRenderer.apply(renderer, args); - } - return ret; - }; - } - opts.renderer = renderer; - } - if (extension.tokenizer) { - const tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); - for (const prop in extension.tokenizer) { - const prevTokenizer = tokenizer[prop]; - tokenizer[prop] = (...args) => { - let ret = extension.tokenizer[prop].apply(tokenizer, args); - if (ret === false) { - ret = prevTokenizer.apply(tokenizer, args); - } - return ret; - }; - } - opts.tokenizer = tokenizer; - } - marked.setOptions(opts); - }; - - /** - * Expose - */ - - marked.Parser = Parser_1; - marked.parser = Parser_1.parse; - - marked.Renderer = Renderer_1; - marked.TextRenderer = TextRenderer_1; - - marked.Lexer = Lexer_1; - marked.lexer = Lexer_1.lex; - - marked.Tokenizer = Tokenizer_1; - - marked.Slugger = Slugger_1; - - marked.parse = marked; - - var marked_1 = marked; - - const tiler$2 = utilTiler(); - const dispatch$3 = dispatch('loaded'); - const _tileZoom$2 = 14; - const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3'; - let _osmoseData = { icons: {}, items: [] }; - - // This gets reassigned if reset - let _cache$2; - - function abortRequest$2(controller) { - if (controller) { - controller.abort(); - } - } - - function abortUnwantedRequests$2(cache, tiles) { - Object.keys(cache.inflightTile).forEach(k => { - let wanted = tiles.find(tile => k === tile.id); - if (!wanted) { - abortRequest$2(cache.inflightTile[k]); - delete cache.inflightTile[k]; - } - }); - } - - function encodeIssueRtree$2(d) { - return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d }; - } - - // Replace or remove QAItem from rtree - function updateRtree$2(item, replace) { - _cache$2.rtree.remove(item, (a, b) => a.data.id === b.data.id); - - if (replace) { - _cache$2.rtree.insert(item); - } - } - - // Issues shouldn't obscure eachother - function preventCoincident$1(loc) { - let coincident = false; - do { - // first time, move marker up. after that, move marker right. - let delta = coincident ? [0.00001, 0] : [0, 0.00001]; - loc = geoVecAdd(loc, delta); - let bbox = geoExtent(loc).bbox(); - coincident = _cache$2.rtree.search(bbox).length; - } while (coincident); - - return loc; - } - - var serviceOsmose = { - title: 'osmose', - - init() { - _mainFileFetcher.get('qa_data') - .then(d => { - _osmoseData = d.osmose; - _osmoseData.items = Object.keys(d.osmose.icons) - .map(s => s.split('-')[0]) - .reduce((unique, item) => unique.indexOf(item) !== -1 ? unique : [...unique, item], []); - }); - - if (!_cache$2) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$3, 'on'); - }, - - reset() { - let _strings = {}; - let _colors = {}; - if (_cache$2) { - Object.values(_cache$2.inflightTile).forEach(abortRequest$2); - // Strings and colors are static and should not be re-populated - _strings = _cache$2.strings; - _colors = _cache$2.colors; - } - _cache$2 = { - data: {}, - loadedTile: {}, - inflightTile: {}, - inflightPost: {}, - closed: {}, - rtree: new RBush(), - strings: _strings, - colors: _colors - }; - }, - - loadIssues(projection) { - let params = { - // Tiles return a maximum # of issues - // So we want to filter our request for only types iD supports - item: _osmoseData.items - }; - - // determine the needed tiles to cover the view - let tiles = tiler$2 - .zoomExtent([_tileZoom$2, _tileZoom$2]) - .getTiles(projection); - - // abort inflight requests that are no longer needed - abortUnwantedRequests$2(_cache$2, tiles); - - // issue new requests.. - tiles.forEach(tile => { - if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return; - - let [ x, y, z ] = tile.xyz; - let url = `${_osmoseUrlRoot}/issues/${z}/${x}/${y}.json?` + utilQsString(params); - - let controller = new AbortController(); - _cache$2.inflightTile[tile.id] = controller; - - d3_json(url, { signal: controller.signal }) - .then(data => { - delete _cache$2.inflightTile[tile.id]; - _cache$2.loadedTile[tile.id] = true; - - if (data.features) { - data.features.forEach(issue => { - const { item, class: cl, uuid: id } = issue.properties; - /* Osmose issues are uniquely identified by a unique - `item` and `class` combination (both integer values) */ - const itemType = `${item}-${cl}`; - - // Filter out unsupported issue types (some are too specific or advanced) - if (itemType in _osmoseData.icons) { - let loc = issue.geometry.coordinates; // lon, lat - loc = preventCoincident$1(loc); - - let d = new QAItem(loc, this, itemType, id, { item }); - - // Setting elems here prevents UI detail requests - if (item === 8300 || item === 8360) { - d.elems = []; - } - - _cache$2.data[d.id] = d; - _cache$2.rtree.insert(encodeIssueRtree$2(d)); - } - }); - } - - dispatch$3.call('loaded'); - }) - .catch(() => { - delete _cache$2.inflightTile[tile.id]; - _cache$2.loadedTile[tile.id] = true; - }); - }); - }, - - loadIssueDetail(issue) { - // Issue details only need to be fetched once - if (issue.elems !== undefined) { - return Promise.resolve(issue); - } - - const url = `${_osmoseUrlRoot}/issue/${issue.id}?langs=${_mainLocalizer.localeCode()}`; - const cacheDetails = data => { - // Associated elements used for highlighting - // Assign directly for immediate use in the callback - issue.elems = data.elems.map(e => e.type.substring(0,1) + e.id); - - // Some issues have instance specific detail in a subtitle - issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : ''; - - this.replaceItem(issue); - }; - - return d3_json(url).then(cacheDetails).then(() => issue); - }, - - loadStrings(locale=_mainLocalizer.localeCode()) { - const items = Object.keys(_osmoseData.icons); - - if ( - locale in _cache$2.strings - && Object.keys(_cache$2.strings[locale]).length === items.length - ) { - return Promise.resolve(_cache$2.strings[locale]); - } - - // May be partially populated already if some requests were successful - if (!(locale in _cache$2.strings)) { - _cache$2.strings[locale] = {}; - } - - // Only need to cache strings for supported issue types - // Using multiple individual item + class requests to reduce fetched data size - const allRequests = items.map(itemType => { - // No need to request data we already have - if (itemType in _cache$2.strings[locale]) return; - - const cacheData = data => { - // Bunch of nested single value arrays of objects - const [ cat = {items:[]} ] = data.categories; - const [ item = {class:[]} ] = cat.items; - const [ cl = null ] = item.class; - - // If null default value is reached, data wasn't as expected (or was empty) - if (!cl) { - /* eslint-disable no-console */ - console.log(`Osmose strings request (${itemType}) had unexpected data`); - /* eslint-enable no-console */ - return; - } - - // Cache served item colors to automatically style issue markers later - const { item: itemInt, color } = item; - if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) { - _cache$2.colors[itemInt] = color; - } - - // Value of root key will be null if no string exists - // If string exists, value is an object with key 'auto' for string - const { title, detail, fix, trap } = cl; - - // Osmose titles shouldn't contain markdown - let issueStrings = {}; - if (title) issueStrings.title = title.auto; - if (detail) issueStrings.detail = marked_1(detail.auto); - if (trap) issueStrings.trap = marked_1(trap.auto); - if (fix) issueStrings.fix = marked_1(fix.auto); - - _cache$2.strings[locale][itemType] = issueStrings; - }; - - const [ item, cl ] = itemType.split('-'); - - // Osmose API falls back to English strings where untranslated or if locale doesn't exist - const url = `${_osmoseUrlRoot}/items/${item}/class/${cl}?langs=${locale}`; - - return d3_json(url).then(cacheData); - }); - - return Promise.all(allRequests).then(() => _cache$2.strings[locale]); - }, - - getStrings(itemType, locale=_mainLocalizer.localeCode()) { - // No need to fallback to English, Osmose API handles this for us - return (locale in _cache$2.strings) ? _cache$2.strings[locale][itemType] : {}; - }, - - getColor(itemType) { - return (itemType in _cache$2.colors) ? _cache$2.colors[itemType] : '#FFFFFF'; - }, - - postUpdate(issue, callback) { - if (_cache$2.inflightPost[issue.id]) { - return callback({ message: 'Issue update already inflight', status: -2 }, issue); - } - - // UI sets the status to either 'done' or 'false' - const url = `${_osmoseUrlRoot}/issue/${issue.id}/${issue.newStatus}`; - const controller = new AbortController(); - const after = () => { - delete _cache$2.inflightPost[issue.id]; - - this.removeItem(issue); - if (issue.newStatus === 'done') { - // Keep track of the number of issues closed per `item` to tag the changeset - if (!(issue.item in _cache$2.closed)) { - _cache$2.closed[issue.item] = 0; - } - _cache$2.closed[issue.item] += 1; - } - if (callback) callback(null, issue); - }; - - _cache$2.inflightPost[issue.id] = controller; - - fetch(url, { signal: controller.signal }) - .then(after) - .catch(err => { - delete _cache$2.inflightPost[issue.id]; - if (callback) callback(err.message); - }); - }, - - // Get all cached QAItems covering the viewport - getItems(projection) { - const viewport = projection.clipExtent(); - const min = [viewport[0][0], viewport[1][1]]; - const max = [viewport[1][0], viewport[0][1]]; - const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - - return _cache$2.rtree.search(bbox).map(d => d.data); - }, - - // Get a QAItem from cache - // NOTE: Don't change method name until UI v3 is merged - getError(id) { - return _cache$2.data[id]; - }, - - // get the name of the icon to display for this item - getIcon(itemType) { - return _osmoseData.icons[itemType]; - }, - - // Replace a single QAItem in the cache - replaceItem(item) { - if (!(item instanceof QAItem) || !item.id) return; - - _cache$2.data[item.id] = item; - updateRtree$2(encodeIssueRtree$2(item), true); // true = replace - return item; - }, - - // Remove a single QAItem from the cache - removeItem(item) { - if (!(item instanceof QAItem) || !item.id) return; - - delete _cache$2.data[item.id]; - updateRtree$2(encodeIssueRtree$2(item), false); // false = remove - }, - - // Used to populate `closed:osmose:*` changeset tags - getClosedCounts() { - return _cache$2.closed; - }, - - itemURL(item) { - return `https://osmose.openstreetmap.fr/en/error/${item.id}`; - } - }; - - /* - A standalone SVG element that contains only a `defs` sub-element. To be - used once globally, since defs IDs must be unique within a document. - */ - function svgDefs(context) { - - function drawDefs(selection) { - var defs = selection.append('defs'); - - // add markers - defs - .append('marker') - .attr('id', 'ideditor-oneway-marker') - .attr('viewBox', '0 0 10 5') - .attr('refX', 2.5) - .attr('refY', 2.5) - .attr('markerWidth', 2) - .attr('markerHeight', 2) - .attr('markerUnits', 'strokeWidth') - .attr('orient', 'auto') - .append('path') - .attr('class', 'oneway-marker-path') - .attr('d', 'M 5,3 L 0,3 L 0,2 L 5,2 L 5,0 L 10,2.5 L 5,5 z') - .attr('stroke', 'none') - .attr('fill', '#000') - .attr('opacity', '0.75'); - - // SVG markers have to be given a colour where they're defined - // (they can't inherit it from the line they're attached to), - // so we need to manually define markers for each color of tag - // (also, it's slightly nicer if we can control the - // positioning for different tags) - function addSidedMarker(name, color, offset) { - defs - .append('marker') - .attr('id', 'ideditor-sided-marker-' + name) - .attr('viewBox', '0 0 2 2') - .attr('refX', 1) - .attr('refY', -offset) - .attr('markerWidth', 1.5) - .attr('markerHeight', 1.5) - .attr('markerUnits', 'strokeWidth') - .attr('orient', 'auto') - .append('path') - .attr('class', 'sided-marker-path sided-marker-' + name + '-path') - .attr('d', 'M 0,0 L 1,1 L 2,0 z') - .attr('stroke', 'none') - .attr('fill', color); - } - addSidedMarker('natural', 'rgb(170, 170, 170)', 0); - // for a coastline, the arrows are (somewhat unintuitively) on - // the water side, so let's color them blue (with a gap) to - // give a stronger indication - addSidedMarker('coastline', '#77dede', 1); - addSidedMarker('waterway', '#77dede', 1); - // barriers have a dashed line, and separating the triangle - // from the line visually suits that - addSidedMarker('barrier', '#ddd', 1); - addSidedMarker('man_made', '#fff', 0); - - defs - .append('marker') - .attr('id', 'ideditor-viewfield-marker') - .attr('viewBox', '0 0 16 16') - .attr('refX', 8) - .attr('refY', 16) - .attr('markerWidth', 4) - .attr('markerHeight', 4) - .attr('markerUnits', 'strokeWidth') - .attr('orient', 'auto') - .append('path') - .attr('class', 'viewfield-marker-path') - .attr('d', 'M 6,14 C 8,13.4 8,13.4 10,14 L 16,3 C 12,0 4,0 0,3 z') - .attr('fill', '#333') - .attr('fill-opacity', '0.75') - .attr('stroke', '#fff') - .attr('stroke-width', '0.5px') - .attr('stroke-opacity', '0.75'); - - defs - .append('marker') - .attr('id', 'ideditor-viewfield-marker-wireframe') - .attr('viewBox', '0 0 16 16') - .attr('refX', 8) - .attr('refY', 16) - .attr('markerWidth', 4) - .attr('markerHeight', 4) - .attr('markerUnits', 'strokeWidth') - .attr('orient', 'auto') - .append('path') - .attr('class', 'viewfield-marker-path') - .attr('d', 'M 6,14 C 8,13.4 8,13.4 10,14 L 16,3 C 12,0 4,0 0,3 z') - .attr('fill', 'none') - .attr('stroke', '#fff') - .attr('stroke-width', '0.5px') - .attr('stroke-opacity', '0.75'); - - // add patterns - var patterns = defs.selectAll('pattern') - .data([ - // pattern name, pattern image name - ['beach', 'dots'], - ['construction', 'construction'], - ['cemetery', 'cemetery'], - ['cemetery_christian', 'cemetery_christian'], - ['cemetery_buddhist', 'cemetery_buddhist'], - ['cemetery_muslim', 'cemetery_muslim'], - ['cemetery_jewish', 'cemetery_jewish'], - ['farmland', 'farmland'], - ['farmyard', 'farmyard'], - ['forest', 'forest'], - ['forest_broadleaved', 'forest_broadleaved'], - ['forest_needleleaved', 'forest_needleleaved'], - ['forest_leafless', 'forest_leafless'], - ['golf_green', 'grass'], - ['grass', 'grass'], - ['landfill', 'landfill'], - ['meadow', 'grass'], - ['orchard', 'orchard'], - ['pond', 'pond'], - ['quarry', 'quarry'], - ['scrub', 'bushes'], - ['vineyard', 'vineyard'], - ['water_standing', 'lines'], - ['waves', 'waves'], - ['wetland', 'wetland'], - ['wetland_marsh', 'wetland_marsh'], - ['wetland_swamp', 'wetland_swamp'], - ['wetland_bog', 'wetland_bog'], - ['wetland_reedbed', 'wetland_reedbed'] - ]) - .enter() - .append('pattern') - .attr('id', function (d) { return 'ideditor-pattern-' + d[0]; }) - .attr('width', 32) - .attr('height', 32) - .attr('patternUnits', 'userSpaceOnUse'); - - patterns - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', 32) - .attr('height', 32) - .attr('class', function (d) { return 'pattern-color-' + d[0]; }); - - patterns - .append('image') - .attr('x', 0) - .attr('y', 0) - .attr('width', 32) - .attr('height', 32) - .attr('xlink:href', function (d) { - return context.imagePath('pattern/' + d[1] + '.png'); - }); - - // add clip paths - defs.selectAll('clipPath') - .data([12, 18, 20, 32, 45]) - .enter() - .append('clipPath') - .attr('id', function (d) { return 'ideditor-clip-square-' + d; }) - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', function (d) { return d; }) - .attr('height', function (d) { return d; }); - - // add symbol spritesheets - defs - .call(drawDefs.addSprites, [ - 'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite' - ], true); - } - - - drawDefs.addSprites = function(selection, ids, overrideColors) { - var spritesheets = selection.selectAll('.spritesheet'); - var currData = spritesheets.data(); - var data = utilArrayUniq(currData.concat(ids)); - - spritesheets - .data(data) - .enter() - .append('g') - .attr('class', function(d) { return 'spritesheet spritesheet-' + d; }) - .each(function(d) { - var url = context.imagePath(d + '.svg'); - var node = select(this).node(); - - svg(url) - .then(function(svg) { - node.appendChild( - select(svg.documentElement).attr('id', 'ideditor-' + d).node() - ); - if (overrideColors && d !== 'iD-sprite') { // allow icon colors to be overridden.. - select(node).selectAll('path') - .attr('fill', 'currentColor'); - } - }) - .catch(function() { - /* ignore */ - }); - }); - }; - - - return drawDefs; - } - - /* global Mapillary:false */ - - - var apibase = 'https://a.mapillary.com/v3/'; - var viewercss = 'mapillary-js/mapillary.min.css'; - var viewerjs = 'mapillary-js/mapillary.min.js'; - var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; - var mapFeatureConfig = { - values: [ - 'construction--flat--crosswalk-plain', - 'marking--discrete--crosswalk-zebra', - 'object--banner', - 'object--bench', - 'object--bike-rack', - 'object--billboard', - 'object--catch-basin', - 'object--cctv-camera', - 'object--fire-hydrant', - 'object--mailbox', - 'object--manhole', - 'object--phone-booth', - 'object--sign--advertisement', - 'object--sign--information', - 'object--sign--store', - 'object--street-light', - 'object--support--utility-pole', - 'object--traffic-light--*', - 'object--traffic-light--pedestrians', - 'object--trash-can' - ].join(',') - }; - var maxResults = 1000; - var tileZoom = 14; - var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); - var dispatch$4 = dispatch('loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged'); - var _mlyFallback = false; - var _mlyCache; - var _mlyClicks; - var _mlySelectedImageKey; - var _mlyViewer; - - - function abortRequest$3(controller) { - controller.abort(); - } - - - function maxPageAtZoom(z) { - if (z < 15) return 2; - if (z === 15) return 5; - if (z === 16) return 10; - if (z === 17) return 20; - if (z === 18) return 40; - if (z > 18) return 80; - } - - - function loadTiles(which, url, projection) { - var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler$3.getTiles(projection); - - // abort inflight requests that are no longer needed - var cache = _mlyCache[which]; - Object.keys(cache.inflight).forEach(function(k) { - var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; }); - if (!wanted) { - abortRequest$3(cache.inflight[k]); - delete cache.inflight[k]; - } - }); - - tiles.forEach(function(tile) { - loadNextTilePage(which, currZoom, url, tile); - }); - } - - - function loadNextTilePage(which, currZoom, url, tile) { - var cache = _mlyCache[which]; - var rect = tile.extent.rectangle(); - var maxPages = maxPageAtZoom(currZoom); - var nextPage = cache.nextPage[tile.id] || 0; - var nextURL = cache.nextURL[tile.id] || url + - utilQsString({ - per_page: maxResults, - page: nextPage, - client_id: clientId, - bbox: [rect[0], rect[1], rect[2], rect[3]].join(','), - }); - - if (nextPage > maxPages) return; - - var id = tile.id + ',' + String(nextPage); - if (cache.loaded[id] || cache.inflight[id]) return; - - var controller = new AbortController(); - cache.inflight[id] = controller; - - var options = { - method: 'GET', - signal: controller.signal, - headers: { 'Content-Type': 'application/json' } - }; - - fetch(nextURL, options) - .then(function(response) { - if (!response.ok) { - throw new Error(response.status + ' ' + response.statusText); - } - var linkHeader = response.headers.get('Link'); - if (linkHeader) { - var pagination = parsePagination(linkHeader); - if (pagination.next) { - cache.nextURL[tile.id] = pagination.next; - } - } - return response.json(); - }) - .then(function(data) { - cache.loaded[id] = true; - delete cache.inflight[id]; - if (!data || !data.features || !data.features.length) { - throw new Error('No Data'); - } - - var features = data.features.map(function(feature) { - var loc = feature.geometry.coordinates; - var d; - - // An image (shown as a green dot on the map) is a single street photo with extra - // information such as location, camera angle (CA), camera model, and so on. - // Each image feature is a GeoJSON Point - if (which === 'images') { - d = { - loc: loc, - key: feature.properties.key, - ca: feature.properties.ca, - captured_at: feature.properties.captured_at, - captured_by: feature.properties.username, - pano: feature.properties.pano - }; - - cache.forImageKey[d.key] = d; // cache imageKey -> image - - // Mapillary organizes images as sequences. A sequence of images are continuously captured - // by a user at a give time. Sequences are shown on the map as green lines. - // Each sequence feature is a GeoJSON LineString - } else if (which === 'sequences') { - var sequenceKey = feature.properties.key; - cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString - feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) { - cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey - }); - return false; // because no `d` data worth loading into an rbush - - // An image detection is a semantic pixel area on an image. The area could indicate - // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point. - // Each image_detection feature is a GeoJSON Point (located where the image was taken) - } else if (which === 'image_detections') { - d = { - key: feature.properties.key, - image_key: feature.properties.image_key, - value: feature.properties.value, - package: feature.properties.package, - shape: feature.properties.shape - }; - - // cache imageKey -> image_detections - if (!cache.forImageKey[d.image_key]) { - cache.forImageKey[d.image_key] = []; - } - cache.forImageKey[d.image_key].push(d); - return false; // because no `d` data worth loading into an rbush - - - // A map feature is a real world object that can be shown on a map. It could be any object - // recognized from images, manually added in images, or added on the map. - // Each map feature is a GeoJSON Point (located where the feature is) - } else if (which === 'map_features' || which === 'points') { - d = { - loc: loc, - key: feature.properties.key, - value: feature.properties.value, - package: feature.properties.package, - detections: feature.properties.detections - }; - } - - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; - - }).filter(Boolean); - - if (cache.rtree && features) { - cache.rtree.load(features); - } - - if (data.features.length === maxResults) { // more pages to load - cache.nextPage[tile.id] = nextPage + 1; - loadNextTilePage(which, currZoom, url, tile); - } else { - cache.nextPage[tile.id] = Infinity; // no more pages to load - } - - if (which === 'images' || which === 'sequences') { - dispatch$4.call('loadedImages'); - } else if (which === 'map_features') { - dispatch$4.call('loadedSigns'); - } else if (which === 'points') { - dispatch$4.call('loadedMapFeatures'); - } - }) - .catch(function() { - cache.loaded[id] = true; - delete cache.inflight[id]; - }); - } - - // extract links to pages of API results - function parsePagination(links) { - return links.split(',').map(function(rel) { - var elements = rel.split(';'); - if (elements.length === 2) { - return [ - /<(.+)>/.exec(elements[0])[1], - /rel="(.+)"/.exec(elements[1])[1] - ]; - } else { - return ['','']; - } - }).reduce(function(pagination, val) { - pagination[val[1]] = val[0]; - return pagination; - }, {}); - } - - - // partition viewport into higher zoom tiles - function partitionViewport(projection) { - var z = geoScaleToZoom(projection.scale()); - var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 - var tiler = utilTiler().zoomExtent([z2, z2]); - - return tiler.getTiles(projection) - .map(function(tile) { return tile.extent; }); - } - - - // no more than `limit` results per partition. - function searchLimited(limit, projection, rtree) { - limit = limit || 5; - - return partitionViewport(projection) - .reduce(function(result, extent) { - var found = rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - - return (found.length ? result.concat(found) : result); - }, []); - } - - - - var serviceMapillary = { - - init: function() { - if (!_mlyCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$4, 'on'); - }, - - reset: function() { - if (_mlyCache) { - Object.values(_mlyCache.images.inflight).forEach(abortRequest$3); - Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3); - Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3); - Object.values(_mlyCache.points.inflight).forEach(abortRequest$3); - Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3); - } - - _mlyCache = { - images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {} }, - image_detections: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, forImageKey: {} }, - map_features: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() }, - points: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() }, - sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {}, lineString: {} } - }; - - _mlySelectedImageKey = null; - _mlyClicks = []; - }, - - - images: function(projection) { - var limit = 5; - return searchLimited(limit, projection, _mlyCache.images.rtree); - }, - - - signs: function(projection) { - var limit = 5; - return searchLimited(limit, projection, _mlyCache.map_features.rtree); - }, - - - mapFeatures: function(projection) { - var limit = 5; - return searchLimited(limit, projection, _mlyCache.points.rtree); - }, - - - cachedImage: function(imageKey) { - return _mlyCache.images.forImageKey[imageKey]; - }, - - - sequences: function(projection) { - var viewport = projection.clipExtent(); - var min = [viewport[0][0], viewport[1][1]]; - var max = [viewport[1][0], viewport[0][1]]; - var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - var sequenceKeys = {}; - - // all sequences for images in viewport - _mlyCache.images.rtree.search(bbox) - .forEach(function(d) { - var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key]; - if (sequenceKey) { - sequenceKeys[sequenceKey] = true; - } - }); - - // Return lineStrings for the sequences - return Object.keys(sequenceKeys).map(function(sequenceKey) { - return _mlyCache.sequences.lineString[sequenceKey]; - }); - }, - - - signsSupported: function() { - return true; - }, - - - loadImages: function(projection) { - loadTiles('images', apibase + 'images?sort_by=key&', projection); - loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection); - }, - - - loadSigns: function(projection) { - // if we are looking at signs, we'll actually need to fetch images too - loadTiles('images', apibase + 'images?sort_by=key&', projection); - loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection); - loadTiles('image_detections', apibase + 'image_detections?layers=trafficsigns&sort_by=key&', projection); - }, - - - loadMapFeatures: function(projection) { - // if we are looking at signs, we'll actually need to fetch images too - loadTiles('images', apibase + 'images?sort_by=key', projection); - loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection); - loadTiles('image_detections', apibase + 'image_detections?layers=points&sort_by=key&values=' + mapFeatureConfig.values + '&', projection); - }, - - - loadViewer: function(context) { - // add mly-wrapper - var wrap = context.container().select('.photoviewer') - .selectAll('.mly-wrapper') - .data([0]); - - wrap.enter() - .append('div') - .attr('id', 'ideditor-mly') - .attr('class', 'photo-wrapper mly-wrapper') - .classed('hide', true); - - // load mapillary-viewercss - select('head').selectAll('#ideditor-mapillary-viewercss') - .data([0]) - .enter() - .append('link') - .attr('id', 'ideditor-mapillary-viewercss') - .attr('rel', 'stylesheet') - .attr('href', context.asset(viewercss)); - - // load mapillary-viewerjs - select('head').selectAll('#ideditor-mapillary-viewerjs') - .data([0]) - .enter() - .append('script') - .attr('id', 'ideditor-mapillary-viewerjs') - .attr('src', context.asset(viewerjs)); - - // load mapillary signs sprite - var defs = context.container().select('defs'); - defs.call(svgDefs(context).addSprites, ['mapillary-sprite', 'mapillary-object-sprite'], false /* don't override colors */ ); - - // Register viewer resize handler - context.ui().photoviewer.on('resize.mapillary', function() { - if (_mlyViewer) { - _mlyViewer.resize(); - } - }); - }, - - - showViewer: function(context) { - var wrap = context.container().select('.photoviewer') - .classed('hide', false); - - var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); - - if (isHidden && _mlyViewer) { - wrap - .selectAll('.photo-wrapper:not(.mly-wrapper)') - .classed('hide', true); - - wrap - .selectAll('.photo-wrapper.mly-wrapper') - .classed('hide', false); - - _mlyViewer.resize(); - } - - return this; - }, - - - hideViewer: function(context) { - _mlySelectedImageKey = null; - - if (!_mlyFallback && _mlyViewer) { - _mlyViewer.getComponent('sequence').stop(); - } - - var viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(null); - - viewer - .classed('hide', true) - .selectAll('.photo-wrapper') - .classed('hide', true); - - context.container().selectAll('.viewfield-group, .sequence, .icon-detected') - .classed('currentView', false); - - return this.setStyles(context, null, true); - }, - - - parsePagination: parsePagination, - - - updateViewer: function(context, imageKey) { - if (!imageKey) return this; - - if (!_mlyViewer) { - this.initViewer(context, imageKey); - } else { - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - return this; - }, - - - initViewer: function(context, imageKey) { - var that = this; - if (window.Mapillary && imageKey) { - var opts = { - baseImageSize: 320, - component: { - cover: false, - keyboard: false, - tag: true - } - }; - - // Disable components requiring WebGL support - if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) { - _mlyFallback = true; - opts.component = { - cover: false, - direction: false, - imagePlane: false, - keyboard: false, - mouse: false, - sequence: false, - tag: false, - image: true, // fallback - navigation: true // fallback - }; - } - - _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts); - _mlyViewer.on('nodechanged', nodeChanged); - _mlyViewer.on('bearingchanged', bearingChanged); - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - // nodeChanged: called after the viewer has changed images and is ready. - // - // There is some logic here to batch up clicks into a _mlyClicks array - // because the user might click on a lot of markers quickly and nodechanged - // may be called out of order asychronously. - // - // Clicks are added to the array in `selectedImage` and removed here. - // - function nodeChanged(node) { - if (!_mlyFallback) { - _mlyViewer.getComponent('tag').removeAll(); // remove previous detections - } - - var clicks = _mlyClicks; - var index = clicks.indexOf(node.key); - var selectedKey = _mlySelectedImageKey; - - if (index > -1) { // `nodechanged` initiated from clicking on a marker.. - clicks.splice(index, 1); // remove the click - // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()` - // one more time to update the detections and attribution.. - if (node.key === selectedKey) { - that.selectImage(context, _mlySelectedImageKey, true); - } - } else { // `nodechanged` initiated from the Mapillary viewer controls.. - var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat]; - context.map().centerEase(loc); - that.selectImage(context, node.key, true); - } - } - - function bearingChanged(e) { - dispatch$4.call('bearingChanged', undefined, e); - } - }, - - - // Pass in the image key string as `imageKey`. - // This allows images to be selected from places that dont have access - // to the full image datum (like the street signs layer or the js viewer) - selectImage: function(context, imageKey, fromViewer) { - - _mlySelectedImageKey = imageKey; - - // Note the datum could be missing, but we'll try to carry on anyway. - // There just might be a delay before user sees detections, captured_at, etc. - var d = _mlyCache.images.forImageKey[imageKey]; - - var viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(d); - - imageKey = (d && d.key) || imageKey; - if (!fromViewer && imageKey) { - _mlyClicks.push(imageKey); - } - - this.setStyles(context, null, true); - - // if signs signs are shown, highlight the ones that appear in this image - context.container().selectAll('.layer-mapillary-signs .icon-detected') - .classed('currentView', function(d) { - return d.detections.some(function(detection) { - return detection.image_key === imageKey; - }); - }); - - if (d) { - this.updateDetections(d); - } - - return this; - }, - - - getSelectedImageKey: function() { - return _mlySelectedImageKey; - }, - - - getSequenceKeyForImageKey: function(imageKey) { - return _mlyCache.sequences.forImageKey[imageKey]; - }, - - - // Updates the currently highlighted sequence and selected bubble. - // Reset is only necessary when interacting with the viewport because - // this implicitly changes the currently selected bubble/sequence - setStyles: function(context, hovered, reset) { - if (reset) { // reset all layers - context.container().selectAll('.viewfield-group') - .classed('highlighted', false) - .classed('hovered', false) - .classed('currentView', false); - - context.container().selectAll('.sequence') - .classed('highlighted', false) - .classed('currentView', false); - } - - var hoveredImageKey = hovered && hovered.key; - var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey); - var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey]; - var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || []; - - var selectedImageKey = _mlySelectedImageKey; - var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey); - var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey]; - var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || []; - - // highlight sibling viewfields on either the selected or the hovered sequences - var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys); - - context.container().selectAll('.layer-mapillary .viewfield-group') - .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) - .classed('hovered', function(d) { return d.key === hoveredImageKey; }) - .classed('currentView', function(d) { return d.key === selectedImageKey; }); - - context.container().selectAll('.layer-mapillary .sequence') - .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) - .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; }); - - // update viewfields if needed - context.container().selectAll('.viewfield-group .viewfield') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano && d.key !== selectedImageKey) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - return this; - }, - - - updateDetections: function(d) { - if (!_mlyViewer || _mlyFallback) return; - - var imageKey = d && d.key; - if (!imageKey) return; - - var detections = _mlyCache.image_detections.forImageKey[imageKey] || []; - detections.forEach(function(data) { - var tag = makeTag(data); - if (tag) { - var tagComponent = _mlyViewer.getComponent('tag'); - tagComponent.add([tag]); - } - }); - - function makeTag(data) { - var valueParts = data.value.split('--'); - if (valueParts.length !== 3) return; - - var text = valueParts[1].replace(/-/g, ' '); - var tag; - - // Currently only two shapes - if (data.shape.type === 'Polygon') { - var polygonGeometry = new Mapillary - .TagComponent - .PolygonGeometry(data.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.OutlineTag( - data.key, - polygonGeometry, - { - text: text, - textColor: 0xffff00, - lineColor: 0xffff00, - lineWidth: 2, - fillColor: 0xffff00, - fillOpacity: 0.3, - } - ); - - } else if (data.shape.type === 'Point') { - var pointGeometry = new Mapillary - .TagComponent - .PointGeometry(data.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.SpotTag( - data.key, - pointGeometry, - { - text: text, - color: 0xffff00, - textColor: 0xffff00 - } - ); - } - - return tag; - } - }, - - - cache: function() { - return _mlyCache; - } - - }; - - function validationIssue(attrs) { - this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag') - this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag') - this.severity = attrs.severity; // required - 'warning' or 'error' - this.message = attrs.message; // required - function returning localized string - this.reference = attrs.reference; // optional - function(selection) to render reference information - this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue - this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue - this.data = attrs.data; // optional - object containing extra data for the fixes - this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes - this.hash = attrs.hash; // optional - string to further differentiate the issue - - this.id = generateID.apply(this); // generated - see below - this.autoFix = null; // generated - if autofix exists, will be set below - - // A unique, deterministic string hash. - // Issues with identical id values are considered identical. - function generateID() { - var parts = [this.type]; - - if (this.hash) { // subclasses can pass in their own differentiator - parts.push(this.hash); - } - - if (this.subtype) { - parts.push(this.subtype); - } - - // include the entities this issue is for - // (sort them so the id is deterministic) - if (this.entityIds) { - var entityKeys = this.entityIds.slice().sort(); - parts.push.apply(parts, entityKeys); - } - - return parts.join(':'); - } - - this.extent = function(resolver) { - if (this.loc) { - return geoExtent(this.loc); - } - if (this.entityIds && this.entityIds.length) { - return this.entityIds.reduce(function(extent, entityId) { - return extent.extend(resolver.entity(entityId).extent(resolver)); - }, geoExtent()); - } - return null; - }; - - this.fixes = function(context) { - var fixes = this.dynamicFixes ? this.dynamicFixes(context) : []; - var issue = this; - - if (issue.severity === 'warning') { - // allow ignoring any issue that's not an error - fixes.push(new validationIssueFix({ - title: _t('issues.fix.ignore_issue.title'), - icon: 'iD-icon-close', - onClick: function() { - context.validator().ignoreIssue(this.issue.id); - } - })); - } - - fixes.forEach(function(fix) { - fix.id = fix.title; - // add a reference to the issue for use in actions - fix.issue = issue; - if (fix.autoArgs) { - issue.autoFix = fix; - } - }); - return fixes; - }; - - } - - - function validationIssueFix(attrs) { - this.title = attrs.title; // Required - this.onClick = attrs.onClick; // Optional - the function to run to apply the fix - this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any - this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set - this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting. - this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run - - this.issue = null; // Generated link - added by validationIssue - } - - var buildRuleChecks = function() { - return { - equals: function (equals) { - return function(tags) { - return Object.keys(equals).every(function(k) { - return equals[k] === tags[k]; - }); - }; - }, - notEquals: function (notEquals) { - return function(tags) { - return Object.keys(notEquals).some(function(k) { - return notEquals[k] !== tags[k]; - }); - }; - }, - absence: function(absence) { - return function(tags) { - return Object.keys(tags).indexOf(absence) === -1; - }; - }, - presence: function(presence) { - return function(tags) { - return Object.keys(tags).indexOf(presence) > -1; - }; - }, - greaterThan: function(greaterThan) { - var key = Object.keys(greaterThan)[0]; - var value = greaterThan[key]; - - return function(tags) { - return tags[key] > value; - }; - }, - greaterThanEqual: function(greaterThanEqual) { - var key = Object.keys(greaterThanEqual)[0]; - var value = greaterThanEqual[key]; - - return function(tags) { - return tags[key] >= value; - }; - }, - lessThan: function(lessThan) { - var key = Object.keys(lessThan)[0]; - var value = lessThan[key]; - - return function(tags) { - return tags[key] < value; - }; - }, - lessThanEqual: function(lessThanEqual) { - var key = Object.keys(lessThanEqual)[0]; - var value = lessThanEqual[key]; - - return function(tags) { - return tags[key] <= value; - }; - }, - positiveRegex: function(positiveRegex) { - var tagKey = Object.keys(positiveRegex)[0]; - var expression = positiveRegex[tagKey].join('|'); - var regex = new RegExp(expression); - - return function(tags) { - return regex.test(tags[tagKey]); - }; - }, - negativeRegex: function(negativeRegex) { - var tagKey = Object.keys(negativeRegex)[0]; - var expression = negativeRegex[tagKey].join('|'); - var regex = new RegExp(expression); - - return function(tags) { - return !regex.test(tags[tagKey]); - }; - } - }; - }; - - var buildLineKeys = function() { - return { - highway: { - rest_area: true, - services: true - }, - railway: { - roundhouse: true, - station: true, - traverser: true, - turntable: true, - wash: true - } - }; - }; - - var serviceMapRules = { - init: function() { - this._ruleChecks = buildRuleChecks(); - this._validationRules = []; - this._areaKeys = osmAreaKeys; - this._lineKeys = buildLineKeys(); - }, - - // list of rules only relevant to tag checks... - filterRuleChecks: function(selector) { - var _ruleChecks = this._ruleChecks; - return Object.keys(selector).reduce(function(rules, key) { - if (['geometry', 'error', 'warning'].indexOf(key) === -1) { - rules.push(_ruleChecks[key](selector[key])); - } - return rules; - }, []); - }, - - // builds tagMap from mapcss-parse selector object... - buildTagMap: function(selector) { - var getRegexValues = function(regexes) { - return regexes.map(function(regex) { - return regex.replace(/\$|\^/g, ''); - }); - }; - - var tagMap = Object.keys(selector).reduce(function (expectedTags, key) { - var values; - var isRegex = /regex/gi.test(key); - var isEqual = /equals/gi.test(key); - - if (isRegex || isEqual) { - Object.keys(selector[key]).forEach(function(selectorKey) { - values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]); - - if (expectedTags.hasOwnProperty(selectorKey)) { - values = values.concat(expectedTags[selectorKey]); - } - - expectedTags[selectorKey] = values; - }); - - } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) { - var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0]; - - values = [selector[key][tagKey]]; - - if (expectedTags.hasOwnProperty(tagKey)) { - values = values.concat(expectedTags[tagKey]); - } - - expectedTags[tagKey] = values; - } - - return expectedTags; - }, {}); - - return tagMap; - }, - - // inspired by osmWay#isArea() - inferGeometry: function(tagMap) { - var _lineKeys = this._lineKeys; - var _areaKeys = this._areaKeys; - - var keyValueDoesNotImplyArea = function(key) { - return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0; - }; - var keyValueImpliesLine = function(key) { - return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0; - }; - - if (tagMap.hasOwnProperty('area')) { - if (tagMap.area.indexOf('yes') > -1) { - return 'area'; - } - if (tagMap.area.indexOf('no') > -1) { - return 'line'; - } - } - - for (var key in tagMap) { - if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) { - return 'area'; - } - if (key in _lineKeys && keyValueImpliesLine(key)) { - return 'area'; - } - } - - return 'line'; - }, - - // adds from mapcss-parse selector check... - addRule: function(selector) { - var rule = { - // checks relevant to mapcss-selector - checks: this.filterRuleChecks(selector), - // true if all conditions for a tag error are true.. - matches: function(entity) { - return this.checks.every(function(check) { - return check(entity.tags); - }); - }, - // borrowed from Way#isArea() - inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys), - geometryMatches: function(entity, graph) { - if (entity.type === 'node' || entity.type === 'relation') { - return selector.geometry === entity.type; - } else if (entity.type === 'way') { - return this.inferredGeometry === entity.geometry(graph); - } - }, - // when geometries match and tag matches are present, return a warning... - findIssues: function (entity, graph, issues) { - if (this.geometryMatches(entity, graph) && this.matches(entity)) { - var severity = Object.keys(selector).indexOf('error') > -1 - ? 'error' - : 'warning'; - var message = selector[severity]; - issues.push(new validationIssue({ - type: 'maprules', - severity: severity, - message: function() { - return message; - }, - entityIds: [entity.id] - })); - } - } - }; - this._validationRules.push(rule); - }, - - clearRules: function() { this._validationRules = []; }, - - // returns validationRules... - validationRules: function() { return this._validationRules; }, - - // returns ruleChecks - ruleChecks: function() { return this._ruleChecks; } - }; - - var apibase$1 = 'https://nominatim.openstreetmap.org/'; - var _inflight = {}; - var _nominatimCache; - - - var serviceNominatim = { - - init: function() { - _inflight = {}; - _nominatimCache = new RBush(); - }, - - reset: function() { - Object.values(_inflight).forEach(function(controller) { controller.abort(); }); - _inflight = {}; - _nominatimCache = new RBush(); - }, - - - countryCode: function (location, callback) { - this.reverse(location, function(err, result) { - if (err) { - return callback(err); - } else if (result.address) { - return callback(null, result.address.country_code); - } else { - return callback('Unable to geocode', null); - } - }); - }, - - - reverse: function (loc, callback) { - var cached = _nominatimCache.search( - { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] } - ); - - if (cached.length > 0) { - if (callback) callback(null, cached[0].data); - return; - } - - var params = { zoom: 13, format: 'json', addressdetails: 1, lat: loc[1], lon: loc[0] }; - var url = apibase$1 + 'reverse?' + utilQsString(params); - - if (_inflight[url]) return; - var controller = new AbortController(); - _inflight[url] = controller; - - d3_json(url, { signal: controller.signal }) - .then(function(result) { - delete _inflight[url]; - if (result && result.error) { - throw new Error(result.error); - } - var extent = geoExtent(loc).padByMeters(200); - _nominatimCache.insert(Object.assign(extent.bbox(), {data: result})); - if (callback) callback(null, result); - }) - .catch(function(err) { - delete _inflight[url]; - if (err.name === 'AbortError') return; - if (callback) callback(err.message); - }); - }, - - - search: function (val, callback) { - var searchVal = encodeURIComponent(val); - var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json'; - - if (_inflight[url]) return; - var controller = new AbortController(); - _inflight[url] = controller; - - d3_json(url, { signal: controller.signal }) - .then(function(result) { - delete _inflight[url]; - if (result && result.error) { - throw new Error(result.error); - } - if (callback) callback(null, result); - }) - .catch(function(err) { - delete _inflight[url]; - if (err.name === 'AbortError') return; - if (callback) callback(err.message); - }); - } - - }; - - var apibase$2 = 'https://openstreetcam.org'; - var maxResults$1 = 1000; - var tileZoom$1 = 14; - var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true); - var dispatch$5 = dispatch('loadedImages'); - var imgZoom = d3_zoom() - .extent([[0, 0], [320, 240]]) - .translateExtent([[0, 0], [320, 240]]) - .scaleExtent([1, 15]); - var _oscCache; - var _oscSelectedImage; - - - function abortRequest$4(controller) { - controller.abort(); - } - - - function maxPageAtZoom$1(z) { - if (z < 15) return 2; - if (z === 15) return 5; - if (z === 16) return 10; - if (z === 17) return 20; - if (z === 18) return 40; - if (z > 18) return 80; - } - - - function loadTiles$1(which, url, projection) { - var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler$4.getTiles(projection); - - // abort inflight requests that are no longer needed - var cache = _oscCache[which]; - Object.keys(cache.inflight).forEach(function(k) { - var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; }); - if (!wanted) { - abortRequest$4(cache.inflight[k]); - delete cache.inflight[k]; - } - }); - - tiles.forEach(function(tile) { - loadNextTilePage$1(which, currZoom, url, tile); - }); - } - - - function loadNextTilePage$1(which, currZoom, url, tile) { - var cache = _oscCache[which]; - var bbox = tile.extent.bbox(); - var maxPages = maxPageAtZoom$1(currZoom); - var nextPage = cache.nextPage[tile.id] || 1; - var params = utilQsString({ - ipp: maxResults$1, - page: nextPage, - // client_id: clientId, - bbTopLeft: [bbox.maxY, bbox.minX].join(','), - bbBottomRight: [bbox.minY, bbox.maxX].join(',') - }, true); - - if (nextPage > maxPages) return; - - var id = tile.id + ',' + String(nextPage); - if (cache.loaded[id] || cache.inflight[id]) return; - - var controller = new AbortController(); - cache.inflight[id] = controller; - - var options = { - method: 'POST', - signal: controller.signal, - body: params, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - }; - - d3_json(url, options) - .then(function(data) { - cache.loaded[id] = true; - delete cache.inflight[id]; - if (!data || !data.currentPageItems || !data.currentPageItems.length) { - throw new Error('No Data'); - } - - var features = data.currentPageItems.map(function(item) { - var loc = [+item.lng, +item.lat]; - var d; - - if (which === 'images') { - d = { - loc: loc, - key: item.id, - ca: +item.heading, - captured_at: (item.shot_date || item.date_added), - captured_by: item.username, - imagePath: item.lth_name, - sequence_id: item.sequence_id, - sequence_index: +item.sequence_index - }; - - // cache sequence info - var seq = _oscCache.sequences[d.sequence_id]; - if (!seq) { - seq = { rotation: 0, images: [] }; - _oscCache.sequences[d.sequence_id] = seq; - } - seq.images[d.sequence_index] = d; - } - - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; - }); - - cache.rtree.load(features); - - if (data.currentPageItems.length === maxResults$1) { // more pages to load - cache.nextPage[tile.id] = nextPage + 1; - loadNextTilePage$1(which, currZoom, url, tile); - } else { - cache.nextPage[tile.id] = Infinity; // no more pages to load - } - - if (which === 'images') { - dispatch$5.call('loadedImages'); - } - }) - .catch(function() { - cache.loaded[id] = true; - delete cache.inflight[id]; - }); - } - - - // partition viewport into higher zoom tiles - function partitionViewport$1(projection) { - var z = geoScaleToZoom(projection.scale()); - var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 - var tiler = utilTiler().zoomExtent([z2, z2]); - - return tiler.getTiles(projection) - .map(function(tile) { return tile.extent; }); - } - - - // no more than `limit` results per partition. - function searchLimited$1(limit, projection, rtree) { - limit = limit || 5; - - return partitionViewport$1(projection) - .reduce(function(result, extent) { - var found = rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - - return (found.length ? result.concat(found) : result); - }, []); - } - - - var serviceOpenstreetcam = { - - init: function() { - if (!_oscCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$5, 'on'); - }, - - reset: function() { - if (_oscCache) { - Object.values(_oscCache.images.inflight).forEach(abortRequest$4); - } - - _oscCache = { - images: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush() }, - sequences: {} - }; - - _oscSelectedImage = null; - }, - - - images: function(projection) { - var limit = 5; - return searchLimited$1(limit, projection, _oscCache.images.rtree); - }, - - - sequences: function(projection) { - var viewport = projection.clipExtent(); - var min = [viewport[0][0], viewport[1][1]]; - var max = [viewport[1][0], viewport[0][1]]; - var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - var sequenceKeys = {}; - - // all sequences for images in viewport - _oscCache.images.rtree.search(bbox) - .forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; }); - - // make linestrings from those sequences - var lineStrings = []; - Object.keys(sequenceKeys) - .forEach(function(sequenceKey) { - var seq = _oscCache.sequences[sequenceKey]; - var images = seq && seq.images; - if (images) { - lineStrings.push({ - type: 'LineString', - coordinates: images.map(function (d) { return d.loc; }).filter(Boolean), - properties: { key: sequenceKey } - }); - } - }); - return lineStrings; - }, - - - loadImages: function(projection) { - var url = apibase$2 + '/1.0/list/nearby-photos/'; - loadTiles$1('images', url, projection); - }, - - - loadViewer: function(context) { - var that = this; - - // add osc-wrapper - var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper') - .data([0]); - - var wrapEnter = wrap.enter() - .append('div') - .attr('class', 'photo-wrapper osc-wrapper') - .classed('hide', true) - .call(imgZoom.on('zoom', zoomPan)) - .on('dblclick.zoom', null); - - wrapEnter - .append('div') - .attr('class', 'photo-attribution fillD'); - - var controlsEnter = wrapEnter - .append('div') - .attr('class', 'photo-controls-wrap') - .append('div') - .attr('class', 'photo-controls'); - - controlsEnter - .append('button') - .on('click.back', step(-1)) - .text('◄'); - - controlsEnter - .append('button') - .on('click.rotate-ccw', rotate(-90)) - .text('⤿'); - - controlsEnter - .append('button') - .on('click.rotate-cw', rotate(90)) - .text('⤾'); - - controlsEnter - .append('button') - .on('click.forward', step(1)) - .text('►'); - - wrapEnter - .append('div') - .attr('class', 'osc-image-wrap'); - - - // Register viewer resize handler - context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) { - imgZoom = d3_zoom() - .extent([[0, 0], dimensions]) - .translateExtent([[0, 0], dimensions]) - .scaleExtent([1, 15]) - .on('zoom', zoomPan); - }); - - - function zoomPan() { - var t = event.transform; - context.container().select('.photoviewer .osc-image-wrap') - .call(utilSetTransform, t.x, t.y, t.k); - } - - - function rotate(deg) { - return function() { - if (!_oscSelectedImage) return; - var sequenceKey = _oscSelectedImage.sequence_id; - var sequence = _oscCache.sequences[sequenceKey]; - if (!sequence) return; - - var r = sequence.rotation || 0; - r += deg; - - if (r > 180) r -= 360; - if (r < -180) r += 360; - sequence.rotation = r; - - var wrap = context.container().select('.photoviewer .osc-wrapper'); - - wrap - .transition() - .duration(100) - .call(imgZoom.transform, identity$2); - - wrap.selectAll('.osc-image') - .transition() - .duration(100) - .style('transform', 'rotate(' + r + 'deg)'); - }; - } - - function step(stepBy) { - return function() { - if (!_oscSelectedImage) return; - var sequenceKey = _oscSelectedImage.sequence_id; - var sequence = _oscCache.sequences[sequenceKey]; - if (!sequence) return; - - var nextIndex = _oscSelectedImage.sequence_index + stepBy; - var nextImage = sequence.images[nextIndex]; - if (!nextImage) return; - - context.map().centerEase(nextImage.loc); - - that - .selectImage(context, nextImage) - .updateViewer(context, nextImage); - }; - } - }, - - - showViewer: function(context) { - var viewer = context.container().select('.photoviewer') - .classed('hide', false); - - var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size(); - - if (isHidden) { - viewer - .selectAll('.photo-wrapper:not(.osc-wrapper)') - .classed('hide', true); - - viewer - .selectAll('.photo-wrapper.osc-wrapper') - .classed('hide', false); - } - - return this; - }, - - - hideViewer: function(context) { - _oscSelectedImage = null; - - var viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(null); - - viewer - .classed('hide', true) - .selectAll('.photo-wrapper') - .classed('hide', true); - - context.container().selectAll('.viewfield-group, .sequence, .icon-sign') - .classed('currentView', false); - - return this.setStyles(context, null, true); - }, - - - updateViewer: function(context, d) { - var wrap = context.container().select('.photoviewer .osc-wrapper'); - var imageWrap = wrap.selectAll('.osc-image-wrap'); - var attribution = wrap.selectAll('.photo-attribution').html(''); - - wrap - .transition() - .duration(100) - .call(imgZoom.transform, identity$2); - - imageWrap - .selectAll('.osc-image') - .remove(); - - if (d) { - var sequence = _oscCache.sequences[d.sequence_id]; - var r = (sequence && sequence.rotation) || 0; - - imageWrap - .append('img') - .attr('class', 'osc-image') - .attr('src', apibase$2 + '/' + d.imagePath) - .style('transform', 'rotate(' + r + 'deg)'); - - if (d.captured_by) { - attribution - .append('a') - .attr('class', 'captured_by') - .attr('target', '_blank') - .attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)) - .text('@' + d.captured_by); - - attribution - .append('span') - .text('|'); - } - - if (d.captured_at) { - attribution - .append('span') - .attr('class', 'captured_at') - .text(localeDateString(d.captured_at)); - - attribution - .append('span') - .text('|'); - } - - attribution - .append('a') - .attr('class', 'image-link') - .attr('target', '_blank') - .attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index) - .text('openstreetcam.org'); - } - - return this; - - - function localeDateString(s) { - if (!s) return null; - var options = { day: 'numeric', month: 'short', year: 'numeric' }; - var d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(_mainLocalizer.localeCode(), options); - } - }, - - - selectImage: function(context, d) { - _oscSelectedImage = d; - var viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(d); - - this.setStyles(context, null, true); - - context.container().selectAll('.icon-sign') - .classed('currentView', false); - - return this; - }, - - - getSelectedImage: function() { - return _oscSelectedImage; - }, - - - getSequenceKeyForImage: function(d) { - return d && d.sequence_id; - }, - - - // Updates the currently highlighted sequence and selected bubble. - // Reset is only necessary when interacting with the viewport because - // this implicitly changes the currently selected bubble/sequence - setStyles: function(context, hovered, reset) { - if (reset) { // reset all layers - context.container().selectAll('.viewfield-group') - .classed('highlighted', false) - .classed('hovered', false) - .classed('currentView', false); - - context.container().selectAll('.sequence') - .classed('highlighted', false) - .classed('currentView', false); - } - - var hoveredImageKey = hovered && hovered.key; - var hoveredSequenceKey = this.getSequenceKeyForImage(hovered); - var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey]; - var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || []; - - var viewer = context.container().select('.photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - var selectedImageKey = selected && selected.key; - var selectedSequenceKey = this.getSequenceKeyForImage(selected); - var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey]; - var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || []; - - // highlight sibling viewfields on either the selected or the hovered sequences - var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys); - - context.container().selectAll('.layer-openstreetcam .viewfield-group') - .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) - .classed('hovered', function(d) { return d.key === hoveredImageKey; }) - .classed('currentView', function(d) { return d.key === selectedImageKey; }); - - context.container().selectAll('.layer-openstreetcam .sequence') - .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) - .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; }); - - // update viewfields if needed - context.container().selectAll('.viewfield-group .viewfield') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano && d.key !== selectedImageKey) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - return this; - }, - - - cache: function() { - return _oscCache; - } - - }; - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject$1(value) { - var type = typeof value; - return value != null && (type == 'object' || type == 'function'); - } - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - - /** Detect free variable `self`. */ - var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - - /** Used as a reference to the global object. */ - var root$2 = freeGlobal || freeSelf || Function('return this')(); - - /** - * Gets the timestamp of the number of milliseconds that have elapsed since - * the Unix epoch (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Date - * @returns {number} Returns the timestamp. - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => Logs the number of milliseconds it took for the deferred invocation. - */ - var now$1 = function() { - return root$2.Date.now(); - }; - - /** Built-in value references. */ - var Symbol$1 = root$2.Symbol; - - /** Used for built-in method references. */ - var objectProto = Object.prototype; - - /** Used to check objects for own properties. */ - var hasOwnProperty$2 = objectProto.hasOwnProperty; - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString = objectProto.toString; - - /** Built-in value references. */ - var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined; - - /** - * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the raw `toStringTag`. - */ - function getRawTag(value) { - var isOwn = hasOwnProperty$2.call(value, symToStringTag), - tag = value[symToStringTag]; - - try { - value[symToStringTag] = undefined; - var unmasked = true; - } catch (e) {} - - var result = nativeObjectToString.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag] = tag; - } else { - delete value[symToStringTag]; - } - } - return result; - } - - /** Used for built-in method references. */ - var objectProto$1 = Object.prototype; - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString$1 = objectProto$1.toString; - - /** - * Converts `value` to a string using `Object.prototype.toString`. - * - * @private - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - */ - function objectToString$2(value) { - return nativeObjectToString$1.call(value); - } - - /** `Object#toString` result references. */ - var nullTag = '[object Null]', - undefinedTag = '[object Undefined]'; - - /** Built-in value references. */ - var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined; - - /** - * The base implementation of `getTag` without fallbacks for buggy environments. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return (symToStringTag$1 && symToStringTag$1 in Object(value)) - ? getRawTag(value) - : objectToString$2(value); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return value != null && typeof value == 'object'; - } - - /** `Object#toString` result references. */ - var symbolTag = '[object Symbol]'; - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol$4(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && baseGetTag(value) == symbolTag); - } - - /** Used as references for various `Number` constants. */ - var NAN = 0 / 0; - - /** Used to match leading and trailing whitespace. */ - var reTrim = /^\s+|\s+$/g; - - /** Used to detect bad signed hexadecimal string values. */ - var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; - - /** Used to detect binary string values. */ - var reIsBinary = /^0b[01]+$/i; - - /** Used to detect octal string values. */ - var reIsOctal = /^0o[0-7]+$/i; - - /** Built-in method references without a dependency on `root`. */ - var freeParseInt = parseInt; - - /** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ - function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol$4(value)) { - return NAN; - } - if (isObject$1(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject$1(other) ? (other + '') : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = value.replace(reTrim, ''); - var isBinary = reIsBinary.test(value); - return (isBinary || reIsOctal.test(value)) - ? freeParseInt(value.slice(2), isBinary ? 2 : 8) - : (reIsBadHex.test(value) ? NAN : +value); - } - - /** Error message constants. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeMax = Math.max, - nativeMin = Math.min; - - /** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed `func` invocations and a `flush` method to immediately invoke them. - * Provide `options` to indicate whether `func` should be invoked on the - * leading and/or trailing edge of the `wait` timeout. The `func` is invoked - * with the last arguments provided to the debounced function. Subsequent - * calls to the debounced function return the result of the last `func` - * invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); - * var source = new EventSource('/stream'); - * jQuery(source).on('message', debounced); - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel); - */ - function debounce(func, wait, options) { - var lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime, - lastInvokeTime = 0, - leading = false, - maxing = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - wait = toNumber(wait) || 0; - if (isObject$1(options)) { - leading = !!options.leading; - maxing = 'maxWait' in options; - maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function invokeFunc(time) { - var args = lastArgs, - thisArg = lastThis; - - lastArgs = lastThis = undefined; - lastInvokeTime = time; - result = func.apply(thisArg, args); - return result; - } - - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time; - // Start the timer for the trailing edge. - timerId = setTimeout(timerExpired, wait); - // Invoke the leading edge. - return leading ? invokeFunc(time) : result; - } - - function remainingWait(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime, - timeWaiting = wait - timeSinceLastCall; - - return maxing - ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) - : timeWaiting; - } - - function shouldInvoke(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime; - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return (lastCallTime === undefined || (timeSinceLastCall >= wait) || - (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); - } - - function timerExpired() { - var time = now$1(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - // Restart the timer. - timerId = setTimeout(timerExpired, remainingWait(time)); - } - - function trailingEdge(time) { - timerId = undefined; - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time); - } - lastArgs = lastThis = undefined; - return result; - } - - function cancel() { - if (timerId !== undefined) { - clearTimeout(timerId); - } - lastInvokeTime = 0; - lastArgs = lastCallTime = lastThis = timerId = undefined; - } - - function flush() { - return timerId === undefined ? result : trailingEdge(now$1()); - } - - function debounced() { - var time = now$1(), - isInvoking = shouldInvoke(time); - - lastArgs = arguments; - lastThis = this; - lastCallTime = time; - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime); - } - if (maxing) { - // Handle invocations in a tight loop. - clearTimeout(timerId); - timerId = setTimeout(timerExpired, wait); - return invokeFunc(lastCallTime); - } - } - if (timerId === undefined) { - timerId = setTimeout(timerExpired, wait); - } - return result; - } - debounced.cancel = cancel; - debounced.flush = flush; - return debounced; - } - - /** Error message constants. */ - var FUNC_ERROR_TEXT$1 = 'Expected a function'; - - /** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed `func` invocations and a `flush` method to - * immediately invoke them. Provide `options` to indicate whether `func` - * should be invoked on the leading and/or trailing edge of the `wait` - * timeout. The `func` is invoked with the last arguments provided to the - * throttled function. Subsequent calls to the throttled function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the throttled function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=true] - * Specify invoking on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // Avoid excessively updating the position while scrolling. - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. - * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); - * jQuery(element).on('click', throttled); - * - * // Cancel the trailing throttled invocation. - * jQuery(window).on('popstate', throttled.cancel); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$1); - } - if (isObject$1(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { - 'leading': leading, - 'maxWait': wait, - 'trailing': trailing - }); - } - - var hashes = createCommonjsModule(function (module, exports) { - /** - * jshashes - https://github.com/h2non/jshashes - * Released under the "New BSD" license - * - * Algorithms specification: - * - * MD5 - http://www.ietf.org/rfc/rfc1321.txt - * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html - * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf - * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf - * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf - * HMAC - http://www.ietf.org/rfc/rfc2104.txt - */ - (function() { - var Hashes; - - function utf8Encode(str) { - var x, y, output = '', - i = -1, - l; - - if (str && str.length) { - l = str.length; - while ((i += 1) < l) { - /* Decode utf-16 surrogate pairs */ - x = str.charCodeAt(i); - y = i + 1 < l ? str.charCodeAt(i + 1) : 0; - if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) { - x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); - i += 1; - } - /* Encode output as utf-8 */ - if (x <= 0x7F) { - output += String.fromCharCode(x); - } else if (x <= 0x7FF) { - output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F), - 0x80 | (x & 0x3F)); - } else if (x <= 0xFFFF) { - output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), - 0x80 | ((x >>> 6) & 0x3F), - 0x80 | (x & 0x3F)); - } else if (x <= 0x1FFFFF) { - output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), - 0x80 | ((x >>> 12) & 0x3F), - 0x80 | ((x >>> 6) & 0x3F), - 0x80 | (x & 0x3F)); - } - } - } - return output; - } - - function utf8Decode(str) { - var i, ac, c1, c2, c3, arr = [], - l; - i = ac = c1 = c2 = c3 = 0; - - if (str && str.length) { - l = str.length; - str += ''; - - while (i < l) { - c1 = str.charCodeAt(i); - ac += 1; - if (c1 < 128) { - arr[ac] = String.fromCharCode(c1); - i += 1; - } else if (c1 > 191 && c1 < 224) { - c2 = str.charCodeAt(i + 1); - arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); - i += 2; - } else { - c2 = str.charCodeAt(i + 1); - c3 = str.charCodeAt(i + 2); - arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } - } - } - return arr.join(''); - } - - /** - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - - function safe_add(x, y) { - var lsw = (x & 0xFFFF) + (y & 0xFFFF), - msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xFFFF); - } - - /** - * Bitwise rotate a 32-bit number to the left. - */ - - function bit_rol(num, cnt) { - return (num << cnt) | (num >>> (32 - cnt)); - } - - /** - * Convert a raw string to a hex string - */ - - function rstr2hex(input, hexcase) { - var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef', - output = '', - x, i = 0, - l = input.length; - for (; i < l; i += 1) { - x = input.charCodeAt(i); - output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F); - } - return output; - } - - /** - * Convert an array of big-endian words to a string - */ - - function binb2rstr(input) { - var i, l = input.length * 32, - output = ''; - for (i = 0; i < l; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF); - } - return output; - } - - /** - * Convert an array of little-endian words to a string - */ - - function binl2rstr(input) { - var i, l = input.length * 32, - output = ''; - for (i = 0; i < l; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); - } - return output; - } - - /** - * Convert a raw string to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - */ - - function rstr2binl(input) { - var i, l = input.length * 8, - output = Array(input.length >> 2), - lo = output.length; - for (i = 0; i < lo; i += 1) { - output[i] = 0; - } - for (i = 0; i < l; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32); - } - return output; - } - - /** - * Convert a raw string to an array of big-endian words - * Characters >255 have their high-byte silently ignored. - */ - - function rstr2binb(input) { - var i, l = input.length * 8, - output = Array(input.length >> 2), - lo = output.length; - for (i = 0; i < lo; i += 1) { - output[i] = 0; - } - for (i = 0; i < l; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); - } - return output; - } - - /** - * Convert a raw string to an arbitrary string encoding - */ - - function rstr2any(input, encoding) { - var divisor = encoding.length, - remainders = Array(), - i, q, x, ld, quotient, dividend, output, full_length; - - /* Convert to an array of 16-bit big-endian values, forming the dividend */ - dividend = Array(Math.ceil(input.length / 2)); - ld = dividend.length; - for (i = 0; i < ld; i += 1) { - dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); - } - - /** - * Repeatedly perform a long division. The binary array forms the dividend, - * the length of the encoding is the divisor. Once computed, the quotient - * forms the dividend for the next step. We stop when the dividend is zerHashes. - * All remainders are stored for later use. - */ - while (dividend.length > 0) { - quotient = Array(); - x = 0; - for (i = 0; i < dividend.length; i += 1) { - x = (x << 16) + dividend[i]; - q = Math.floor(x / divisor); - x -= q * divisor; - if (quotient.length > 0 || q > 0) { - quotient[quotient.length] = q; - } - } - remainders[remainders.length] = x; - dividend = quotient; - } - - /* Convert the remainders to the output string */ - output = ''; - for (i = remainders.length - 1; i >= 0; i--) { - output += encoding.charAt(remainders[i]); - } - - /* Append leading zero equivalents */ - full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2))); - for (i = output.length; i < full_length; i += 1) { - output = encoding[0] + output; - } - return output; - } - - /** - * Convert a raw string to a base-64 string - */ - - function rstr2b64(input, b64pad) { - var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - output = '', - len = input.length, - i, j, triplet; - b64pad = b64pad || '='; - for (i = 0; i < len; i += 3) { - triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0); - for (j = 0; j < 4; j += 1) { - if (i * 8 + j * 6 > input.length * 8) { - output += b64pad; - } else { - output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); - } - } - } - return output; - } - - Hashes = { - /** - * @property {String} version - * @readonly - */ - VERSION: '1.0.6', - /** - * @member Hashes - * @class Base64 - * @constructor - */ - Base64: function() { - // private properties - var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - pad = '=', // default pad according with the RFC standard - utf8 = true; // by default enable UTF-8 support encoding - - // public method for encoding - this.encode = function(input) { - var i, j, triplet, - output = '', - len = input.length; - - pad = pad || '='; - input = (utf8) ? utf8Encode(input) : input; - - for (i = 0; i < len; i += 3) { - triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0); - for (j = 0; j < 4; j += 1) { - if (i * 8 + j * 6 > len * 8) { - output += pad; - } else { - output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); - } - } - } - return output; - }; - - // public method for decoding - this.decode = function(input) { - // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - var i, o1, o2, o3, h1, h2, h3, h4, bits, ac, - dec = '', - arr = []; - if (!input) { - return input; - } - - i = ac = 0; - input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '=' - //input += ''; - - do { // unpack four hexets into three octets using index points in b64 - h1 = tab.indexOf(input.charAt(i += 1)); - h2 = tab.indexOf(input.charAt(i += 1)); - h3 = tab.indexOf(input.charAt(i += 1)); - h4 = tab.indexOf(input.charAt(i += 1)); - - bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; - - o1 = bits >> 16 & 0xff; - o2 = bits >> 8 & 0xff; - o3 = bits & 0xff; - ac += 1; - - if (h3 === 64) { - arr[ac] = String.fromCharCode(o1); - } else if (h4 === 64) { - arr[ac] = String.fromCharCode(o1, o2); - } else { - arr[ac] = String.fromCharCode(o1, o2, o3); - } - } while (i < input.length); - - dec = arr.join(''); - dec = (utf8) ? utf8Decode(dec) : dec; - - return dec; - }; - - // set custom pad string - this.setPad = function(str) { - pad = str || pad; - return this; - }; - // set custom tab string characters - this.setTab = function(str) { - tab = str || tab; - return this; - }; - this.setUTF8 = function(bool) { - if (typeof bool === 'boolean') { - utf8 = bool; - } - return this; - }; - }, - - /** - * CRC-32 calculation - * @member Hashes - * @method CRC32 - * @static - * @param {String} str Input String - * @return {String} - */ - CRC32: function(str) { - var crc = 0, - x = 0, - y = 0, - table, i, iTop; - str = utf8Encode(str); - - table = [ - '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ', - '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ', - '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ', - '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ', - 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ', - '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ', - 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ', - '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ', - 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ', - '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ', - 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ', - '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ', - 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ', - '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ', - '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ', - '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ', - '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ', - 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ', - '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ', - 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ', - '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ', - 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ', - '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ', - 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ', - '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ', - 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D' - ].join(''); - - crc = crc ^ (-1); - for (i = 0, iTop = str.length; i < iTop; i += 1) { - y = (crc ^ str.charCodeAt(i)) & 0xFF; - x = '0x' + table.substr(y * 9, 8); - crc = (crc >>> 8) ^ x; - } - // always return a positive number (that's what >>> 0 does) - return (crc ^ (-1)) >>> 0; - }, - /** - * @member Hashes - * @class MD5 - * @constructor - * @param {Object} [config] - * - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * See for more infHashes. - */ - MD5: function(options) { - /** - * Private config properties. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase} - */ - var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase - b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance - utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding - - // privileged (public) methods - this.hex = function(s) { - return rstr2hex(rstr(s), hexcase); - }; - this.b64 = function(s) { - return rstr2b64(rstr(s), b64pad); - }; - this.any = function(s, e) { - return rstr2any(rstr(s), e); - }; - this.raw = function(s) { - return rstr(s); - }; - this.hex_hmac = function(k, d) { - return rstr2hex(rstr_hmac(k, d), hexcase); - }; - this.b64_hmac = function(k, d) { - return rstr2b64(rstr_hmac(k, d), b64pad); - }; - this.any_hmac = function(k, d, e) { - return rstr2any(rstr_hmac(k, d), e); - }; - /** - * Perform a simple self-test to see if the VM is working - * @return {String} Hexadecimal hash sample - */ - this.vm_test = function() { - return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; - }; - /** - * Enable/disable uppercase hexadecimal returned string - * @param {Boolean} - * @return {Object} this - */ - this.setUpperCase = function(a) { - if (typeof a === 'boolean') { - hexcase = a; - } - return this; - }; - /** - * Defines a base64 pad string - * @param {String} Pad - * @return {Object} this - */ - this.setPad = function(a) { - b64pad = a || b64pad; - return this; - }; - /** - * Defines a base64 pad string - * @param {Boolean} - * @return {Object} [this] - */ - this.setUTF8 = function(a) { - if (typeof a === 'boolean') { - utf8 = a; - } - return this; - }; - - // private methods - - /** - * Calculate the MD5 of a raw string - */ - - function rstr(s) { - s = (utf8) ? utf8Encode(s) : s; - return binl2rstr(binl(rstr2binl(s), s.length * 8)); - } - - /** - * Calculate the HMAC-MD5, of a key and some data (raw strings) - */ - - function rstr_hmac(key, data) { - var bkey, ipad, opad, hash, i; - - key = (utf8) ? utf8Encode(key) : key; - data = (utf8) ? utf8Encode(data) : data; - bkey = rstr2binl(key); - if (bkey.length > 16) { - bkey = binl(bkey, key.length * 8); - } - - ipad = Array(16), opad = Array(16); - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; - } - hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8); - return binl2rstr(binl(opad.concat(hash), 512 + 128)); - } - - /** - * Calculate the MD5 of an array of little-endian words, and a bit length. - */ - - function binl(x, len) { - var i, olda, oldb, oldc, oldd, - a = 1732584193, - b = -271733879, - c = -1732584194, - d = 271733878; - - /* append padding */ - x[len >> 5] |= 0x80 << ((len) % 32); - x[(((len + 64) >>> 9) << 4) + 14] = len; - - for (i = 0; i < x.length; i += 16) { - olda = a; - oldb = b; - oldc = c; - oldd = d; - - a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); - d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); - c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); - b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); - a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); - d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); - c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); - b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); - a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); - d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); - c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); - b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); - a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); - d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); - c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); - b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); - - a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); - d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); - c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); - b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); - a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); - d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); - c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); - b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); - a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); - d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); - c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); - b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); - a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); - d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); - c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); - b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); - - a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); - d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); - c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); - b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); - a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); - d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); - c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); - b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); - a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); - d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); - c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); - b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); - a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); - d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); - c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); - b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); - - a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); - d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); - c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); - b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); - a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); - d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); - c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); - b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); - a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); - d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); - c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); - b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); - a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); - d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); - c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); - b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); - - a = safe_add(a, olda); - b = safe_add(b, oldb); - c = safe_add(c, oldc); - d = safe_add(d, oldd); - } - return Array(a, b, c, d); - } - - /** - * These functions implement the four basic operations the algorithm uses. - */ - - function md5_cmn(q, a, b, x, s, t) { - return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); - } - - function md5_ff(a, b, c, d, x, s, t) { - return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); - } - - function md5_gg(a, b, c, d, x, s, t) { - return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); - } - - function md5_hh(a, b, c, d, x, s, t) { - return md5_cmn(b ^ c ^ d, a, b, x, s, t); - } - - function md5_ii(a, b, c, d, x, s, t) { - return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); - } - }, - /** - * @member Hashes - * @class Hashes.SHA1 - * @param {Object} [config] - * @constructor - * - * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1 - * Version 2.2 Copyright Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * See http://pajhome.org.uk/crypt/md5 for details. - */ - SHA1: function(options) { - /** - * Private config properties. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase} - */ - var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase - b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance - utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding - - // public methods - this.hex = function(s) { - return rstr2hex(rstr(s), hexcase); - }; - this.b64 = function(s) { - return rstr2b64(rstr(s), b64pad); - }; - this.any = function(s, e) { - return rstr2any(rstr(s), e); - }; - this.raw = function(s) { - return rstr(s); - }; - this.hex_hmac = function(k, d) { - return rstr2hex(rstr_hmac(k, d)); - }; - this.b64_hmac = function(k, d) { - return rstr2b64(rstr_hmac(k, d), b64pad); - }; - this.any_hmac = function(k, d, e) { - return rstr2any(rstr_hmac(k, d), e); - }; - /** - * Perform a simple self-test to see if the VM is working - * @return {String} Hexadecimal hash sample - * @public - */ - this.vm_test = function() { - return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; - }; - /** - * @description Enable/disable uppercase hexadecimal returned string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUpperCase = function(a) { - if (typeof a === 'boolean') { - hexcase = a; - } - return this; - }; - /** - * @description Defines a base64 pad string - * @param {string} Pad - * @return {Object} this - * @public - */ - this.setPad = function(a) { - b64pad = a || b64pad; - return this; - }; - /** - * @description Defines a base64 pad string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUTF8 = function(a) { - if (typeof a === 'boolean') { - utf8 = a; - } - return this; - }; - - // private methods - - /** - * Calculate the SHA-512 of a raw string - */ - - function rstr(s) { - s = (utf8) ? utf8Encode(s) : s; - return binb2rstr(binb(rstr2binb(s), s.length * 8)); - } - - /** - * Calculate the HMAC-SHA1 of a key and some data (raw strings) - */ - - function rstr_hmac(key, data) { - var bkey, ipad, opad, i, hash; - key = (utf8) ? utf8Encode(key) : key; - data = (utf8) ? utf8Encode(data) : data; - bkey = rstr2binb(key); - - if (bkey.length > 16) { - bkey = binb(bkey, key.length * 8); - } - ipad = Array(16), opad = Array(16); - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; - } - hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8); - return binb2rstr(binb(opad.concat(hash), 512 + 160)); - } - - /** - * Calculate the SHA-1 of an array of big-endian words, and a bit length - */ - - function binb(x, len) { - var i, j, t, olda, oldb, oldc, oldd, olde, - w = Array(80), - a = 1732584193, - b = -271733879, - c = -1732584194, - d = 271733878, - e = -1009589776; - - /* append padding */ - x[len >> 5] |= 0x80 << (24 - len % 32); - x[((len + 64 >> 9) << 4) + 15] = len; - - for (i = 0; i < x.length; i += 16) { - olda = a; - oldb = b; - oldc = c; - oldd = d; - olde = e; - - for (j = 0; j < 80; j += 1) { - if (j < 16) { - w[j] = x[i + j]; - } else { - w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); - } - t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), - safe_add(safe_add(e, w[j]), sha1_kt(j))); - e = d; - d = c; - c = bit_rol(b, 30); - b = a; - a = t; - } - - a = safe_add(a, olda); - b = safe_add(b, oldb); - c = safe_add(c, oldc); - d = safe_add(d, oldd); - e = safe_add(e, olde); - } - return Array(a, b, c, d, e); - } - - /** - * Perform the appropriate triplet combination function for the current - * iteration - */ - - function sha1_ft(t, b, c, d) { - if (t < 20) { - return (b & c) | ((~b) & d); - } - if (t < 40) { - return b ^ c ^ d; - } - if (t < 60) { - return (b & c) | (b & d) | (c & d); - } - return b ^ c ^ d; - } - - /** - * Determine the appropriate additive constant for the current iteration - */ - - function sha1_kt(t) { - return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : - (t < 60) ? -1894007588 : -899497514; - } - }, - /** - * @class Hashes.SHA256 - * @param {config} - * - * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2 - * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * See http://pajhome.org.uk/crypt/md5 for details. - * Also http://anmar.eu.org/projects/jssha2/ - */ - SHA256: function(options) { - /** - * Private properties configuration variables. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - * @see this.setUpperCase() method - * @see this.setPad() method - */ - var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase */ - b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', - /* base-64 pad character. Default '=' for strict RFC compliance */ - utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, - /* enable/disable utf8 encoding */ - sha256_K; - - /* privileged (public) methods */ - this.hex = function(s) { - return rstr2hex(rstr(s, utf8)); - }; - this.b64 = function(s) { - return rstr2b64(rstr(s, utf8), b64pad); - }; - this.any = function(s, e) { - return rstr2any(rstr(s, utf8), e); - }; - this.raw = function(s) { - return rstr(s, utf8); - }; - this.hex_hmac = function(k, d) { - return rstr2hex(rstr_hmac(k, d)); - }; - this.b64_hmac = function(k, d) { - return rstr2b64(rstr_hmac(k, d), b64pad); - }; - this.any_hmac = function(k, d, e) { - return rstr2any(rstr_hmac(k, d), e); - }; - /** - * Perform a simple self-test to see if the VM is working - * @return {String} Hexadecimal hash sample - * @public - */ - this.vm_test = function() { - return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; - }; - /** - * Enable/disable uppercase hexadecimal returned string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUpperCase = function(a) { - if (typeof a === 'boolean') { - hexcase = a; - } - return this; - }; - /** - * @description Defines a base64 pad string - * @param {string} Pad - * @return {Object} this - * @public - */ - this.setPad = function(a) { - b64pad = a || b64pad; - return this; - }; - /** - * Defines a base64 pad string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUTF8 = function(a) { - if (typeof a === 'boolean') { - utf8 = a; - } - return this; - }; - - // private methods - - /** - * Calculate the SHA-512 of a raw string - */ - - function rstr(s, utf8) { - s = (utf8) ? utf8Encode(s) : s; - return binb2rstr(binb(rstr2binb(s), s.length * 8)); - } - - /** - * Calculate the HMAC-sha256 of a key and some data (raw strings) - */ - - function rstr_hmac(key, data) { - key = (utf8) ? utf8Encode(key) : key; - data = (utf8) ? utf8Encode(data) : data; - var hash, i = 0, - bkey = rstr2binb(key), - ipad = Array(16), - opad = Array(16); - - if (bkey.length > 16) { - bkey = binb(bkey, key.length * 8); - } - - for (; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; - } - - hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8); - return binb2rstr(binb(opad.concat(hash), 512 + 256)); - } - - /* - * Main sha256 function, with its support functions - */ - - function sha256_S(X, n) { - return (X >>> n) | (X << (32 - n)); - } - - function sha256_R(X, n) { - return (X >>> n); - } - - function sha256_Ch(x, y, z) { - return ((x & y) ^ ((~x) & z)); - } - - function sha256_Maj(x, y, z) { - return ((x & y) ^ (x & z) ^ (y & z)); - } - - function sha256_Sigma0256(x) { - return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22)); - } - - function sha256_Sigma1256(x) { - return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25)); - } - - function sha256_Gamma0256(x) { - return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3)); - } - - function sha256_Gamma1256(x) { - return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10)); - } - - sha256_K = [ - 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, - 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, - 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, - 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, - 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, - 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, - 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998 - ]; - - function binb(m, l) { - var HASH = [1779033703, -1150833019, 1013904242, -1521486534, - 1359893119, -1694144372, 528734635, 1541459225 - ]; - var W = new Array(64); - var a, b, c, d, e, f, g, h; - var i, j, T1, T2; - - /* append padding */ - m[l >> 5] |= 0x80 << (24 - l % 32); - m[((l + 64 >> 9) << 4) + 15] = l; - - for (i = 0; i < m.length; i += 16) { - a = HASH[0]; - b = HASH[1]; - c = HASH[2]; - d = HASH[3]; - e = HASH[4]; - f = HASH[5]; - g = HASH[6]; - h = HASH[7]; - - for (j = 0; j < 64; j += 1) { - if (j < 16) { - W[j] = m[j + i]; - } else { - W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), - sha256_Gamma0256(W[j - 15])), W[j - 16]); - } - - T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), - sha256_K[j]), W[j]); - T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c)); - h = g; - g = f; - f = e; - e = safe_add(d, T1); - d = c; - c = b; - b = a; - a = safe_add(T1, T2); - } - - HASH[0] = safe_add(a, HASH[0]); - HASH[1] = safe_add(b, HASH[1]); - HASH[2] = safe_add(c, HASH[2]); - HASH[3] = safe_add(d, HASH[3]); - HASH[4] = safe_add(e, HASH[4]); - HASH[5] = safe_add(f, HASH[5]); - HASH[6] = safe_add(g, HASH[6]); - HASH[7] = safe_add(h, HASH[7]); - } - return HASH; - } - - }, - - /** - * @class Hashes.SHA512 - * @param {config} - * - * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2 - * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * See http://pajhome.org.uk/crypt/md5 for details. - */ - SHA512: function(options) { - /** - * Private properties configuration variables. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - * @see this.setUpperCase() method - * @see this.setPad() method - */ - var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, - /* hexadecimal output case format. false - lowercase; true - uppercase */ - b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', - /* base-64 pad character. Default '=' for strict RFC compliance */ - utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, - /* enable/disable utf8 encoding */ - sha512_k; - - /* privileged (public) methods */ - this.hex = function(s) { - return rstr2hex(rstr(s)); - }; - this.b64 = function(s) { - return rstr2b64(rstr(s), b64pad); - }; - this.any = function(s, e) { - return rstr2any(rstr(s), e); - }; - this.raw = function(s) { - return rstr(s); - }; - this.hex_hmac = function(k, d) { - return rstr2hex(rstr_hmac(k, d)); - }; - this.b64_hmac = function(k, d) { - return rstr2b64(rstr_hmac(k, d), b64pad); - }; - this.any_hmac = function(k, d, e) { - return rstr2any(rstr_hmac(k, d), e); - }; - /** - * Perform a simple self-test to see if the VM is working - * @return {String} Hexadecimal hash sample - * @public - */ - this.vm_test = function() { - return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; - }; - /** - * @description Enable/disable uppercase hexadecimal returned string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUpperCase = function(a) { - if (typeof a === 'boolean') { - hexcase = a; - } - return this; - }; - /** - * @description Defines a base64 pad string - * @param {string} Pad - * @return {Object} this - * @public - */ - this.setPad = function(a) { - b64pad = a || b64pad; - return this; - }; - /** - * @description Defines a base64 pad string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUTF8 = function(a) { - if (typeof a === 'boolean') { - utf8 = a; - } - return this; - }; - - /* private methods */ - - /** - * Calculate the SHA-512 of a raw string - */ - - function rstr(s) { - s = (utf8) ? utf8Encode(s) : s; - return binb2rstr(binb(rstr2binb(s), s.length * 8)); - } - /* - * Calculate the HMAC-SHA-512 of a key and some data (raw strings) - */ - - function rstr_hmac(key, data) { - key = (utf8) ? utf8Encode(key) : key; - data = (utf8) ? utf8Encode(data) : data; - - var hash, i = 0, - bkey = rstr2binb(key), - ipad = Array(32), - opad = Array(32); - - if (bkey.length > 32) { - bkey = binb(bkey, key.length * 8); - } - - for (; i < 32; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; - } - - hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8); - return binb2rstr(binb(opad.concat(hash), 1024 + 512)); - } - - /** - * Calculate the SHA-512 of an array of big-endian dwords, and a bit length - */ - - function binb(x, len) { - var j, i, l, - W = new Array(80), - hash = new Array(16), - //Initial hash values - H = [ - new int64(0x6a09e667, -205731576), - new int64(-1150833019, -2067093701), - new int64(0x3c6ef372, -23791573), - new int64(-1521486534, 0x5f1d36f1), - new int64(0x510e527f, -1377402159), - new int64(-1694144372, 0x2b3e6c1f), - new int64(0x1f83d9ab, -79577749), - new int64(0x5be0cd19, 0x137e2179) - ], - T1 = new int64(0, 0), - T2 = new int64(0, 0), - a = new int64(0, 0), - b = new int64(0, 0), - c = new int64(0, 0), - d = new int64(0, 0), - e = new int64(0, 0), - f = new int64(0, 0), - g = new int64(0, 0), - h = new int64(0, 0), - //Temporary variables not specified by the document - s0 = new int64(0, 0), - s1 = new int64(0, 0), - Ch = new int64(0, 0), - Maj = new int64(0, 0), - r1 = new int64(0, 0), - r2 = new int64(0, 0), - r3 = new int64(0, 0); - - if (sha512_k === undefined) { - //SHA512 constants - sha512_k = [ - new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd), - new int64(-1245643825, -330482897), new int64(-373957723, -2121671748), - new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031), - new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736), - new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe), - new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302), - new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1), - new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428), - new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3), - new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65), - new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483), - new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459), - new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210), - new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340), - new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395), - new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70), - new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926), - new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473), - new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8), - new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b), - new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023), - new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30), - new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910), - new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8), - new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53), - new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016), - new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893), - new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397), - new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60), - new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec), - new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047), - new int64(-1090935817, -1295615723), new int64(-965641998, -479046869), - new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207), - new int64(-354779690, -840897762), new int64(-176337025, -294727304), - new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026), - new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b), - new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493), - new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620), - new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430), - new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817) - ]; - } - - for (i = 0; i < 80; i += 1) { - W[i] = new int64(0, 0); - } - - // append padding to the source string. The format is described in the FIPS. - x[len >> 5] |= 0x80 << (24 - (len & 0x1f)); - x[((len + 128 >> 10) << 5) + 31] = len; - l = x.length; - for (i = 0; i < l; i += 32) { //32 dwords is the block size - int64copy(a, H[0]); - int64copy(b, H[1]); - int64copy(c, H[2]); - int64copy(d, H[3]); - int64copy(e, H[4]); - int64copy(f, H[5]); - int64copy(g, H[6]); - int64copy(h, H[7]); - - for (j = 0; j < 16; j += 1) { - W[j].h = x[i + 2 * j]; - W[j].l = x[i + 2 * j + 1]; - } - - for (j = 16; j < 80; j += 1) { - //sigma1 - int64rrot(r1, W[j - 2], 19); - int64revrrot(r2, W[j - 2], 29); - int64shr(r3, W[j - 2], 6); - s1.l = r1.l ^ r2.l ^ r3.l; - s1.h = r1.h ^ r2.h ^ r3.h; - //sigma0 - int64rrot(r1, W[j - 15], 1); - int64rrot(r2, W[j - 15], 8); - int64shr(r3, W[j - 15], 7); - s0.l = r1.l ^ r2.l ^ r3.l; - s0.h = r1.h ^ r2.h ^ r3.h; - - int64add4(W[j], s1, W[j - 7], s0, W[j - 16]); - } - - for (j = 0; j < 80; j += 1) { - //Ch - Ch.l = (e.l & f.l) ^ (~e.l & g.l); - Ch.h = (e.h & f.h) ^ (~e.h & g.h); - - //Sigma1 - int64rrot(r1, e, 14); - int64rrot(r2, e, 18); - int64revrrot(r3, e, 9); - s1.l = r1.l ^ r2.l ^ r3.l; - s1.h = r1.h ^ r2.h ^ r3.h; - - //Sigma0 - int64rrot(r1, a, 28); - int64revrrot(r2, a, 2); - int64revrrot(r3, a, 7); - s0.l = r1.l ^ r2.l ^ r3.l; - s0.h = r1.h ^ r2.h ^ r3.h; - - //Maj - Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l); - Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h); - - int64add5(T1, h, s1, Ch, sha512_k[j], W[j]); - int64add(T2, s0, Maj); - - int64copy(h, g); - int64copy(g, f); - int64copy(f, e); - int64add(e, d, T1); - int64copy(d, c); - int64copy(c, b); - int64copy(b, a); - int64add(a, T1, T2); - } - int64add(H[0], H[0], a); - int64add(H[1], H[1], b); - int64add(H[2], H[2], c); - int64add(H[3], H[3], d); - int64add(H[4], H[4], e); - int64add(H[5], H[5], f); - int64add(H[6], H[6], g); - int64add(H[7], H[7], h); - } - - //represent the hash as an array of 32-bit dwords - for (i = 0; i < 8; i += 1) { - hash[2 * i] = H[i].h; - hash[2 * i + 1] = H[i].l; - } - return hash; - } - - //A constructor for 64-bit numbers - - function int64(h, l) { - this.h = h; - this.l = l; - //this.toString = int64toString; - } - - //Copies src into dst, assuming both are 64-bit numbers - - function int64copy(dst, src) { - dst.h = src.h; - dst.l = src.l; - } - - //Right-rotates a 64-bit number by shift - //Won't handle cases of shift>=32 - //The function revrrot() is for that - - function int64rrot(dst, x, shift) { - dst.l = (x.l >>> shift) | (x.h << (32 - shift)); - dst.h = (x.h >>> shift) | (x.l << (32 - shift)); - } - - //Reverses the dwords of the source and then rotates right by shift. - //This is equivalent to rotation by 32+shift - - function int64revrrot(dst, x, shift) { - dst.l = (x.h >>> shift) | (x.l << (32 - shift)); - dst.h = (x.l >>> shift) | (x.h << (32 - shift)); - } - - //Bitwise-shifts right a 64-bit number by shift - //Won't handle shift>=32, but it's never needed in SHA512 - - function int64shr(dst, x, shift) { - dst.l = (x.l >>> shift) | (x.h << (32 - shift)); - dst.h = (x.h >>> shift); - } - - //Adds two 64-bit numbers - //Like the original implementation, does not rely on 32-bit operations - - function int64add(dst, x, y) { - var w0 = (x.l & 0xffff) + (y.l & 0xffff); - var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16); - var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16); - var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16); - dst.l = (w0 & 0xffff) | (w1 << 16); - dst.h = (w2 & 0xffff) | (w3 << 16); - } - - //Same, except with 4 addends. Works faster than adding them one by one. - - function int64add4(dst, a, b, c, d) { - var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff); - var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16); - var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16); - var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16); - dst.l = (w0 & 0xffff) | (w1 << 16); - dst.h = (w2 & 0xffff) | (w3 << 16); - } - - //Same, except with 5 addends - - function int64add5(dst, a, b, c, d, e) { - var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff), - w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16), - w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16), - w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16); - dst.l = (w0 & 0xffff) | (w1 << 16); - dst.h = (w2 & 0xffff) | (w3 << 16); - } - }, - /** - * @class Hashes.RMD160 - * @constructor - * @param {Object} [config] - * - * A JavaScript implementation of the RIPEMD-160 Algorithm - * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * See http://pajhome.org.uk/crypt/md5 for details. - * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/ - */ - RMD160: function(options) { - /** - * Private properties configuration variables. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - * @see this.setUpperCase() method - * @see this.setPad() method - */ - var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, - /* hexadecimal output case format. false - lowercase; true - uppercase */ - b64pad = (options && typeof options.pad === 'string') ? options.pa : '=', - /* base-64 pad character. Default '=' for strict RFC compliance */ - utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true, - /* enable/disable utf8 encoding */ - rmd160_r1 = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 - ], - rmd160_r2 = [ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 - ], - rmd160_s1 = [ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 - ], - rmd160_s2 = [ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 - ]; - - /* privileged (public) methods */ - this.hex = function(s) { - return rstr2hex(rstr(s)); - }; - this.b64 = function(s) { - return rstr2b64(rstr(s), b64pad); - }; - this.any = function(s, e) { - return rstr2any(rstr(s), e); - }; - this.raw = function(s) { - return rstr(s); - }; - this.hex_hmac = function(k, d) { - return rstr2hex(rstr_hmac(k, d)); - }; - this.b64_hmac = function(k, d) { - return rstr2b64(rstr_hmac(k, d), b64pad); - }; - this.any_hmac = function(k, d, e) { - return rstr2any(rstr_hmac(k, d), e); - }; - /** - * Perform a simple self-test to see if the VM is working - * @return {String} Hexadecimal hash sample - * @public - */ - this.vm_test = function() { - return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72'; - }; - /** - * @description Enable/disable uppercase hexadecimal returned string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUpperCase = function(a) { - if (typeof a === 'boolean') { - hexcase = a; - } - return this; - }; - /** - * @description Defines a base64 pad string - * @param {string} Pad - * @return {Object} this - * @public - */ - this.setPad = function(a) { - if (typeof a !== 'undefined') { - b64pad = a; - } - return this; - }; - /** - * @description Defines a base64 pad string - * @param {boolean} - * @return {Object} this - * @public - */ - this.setUTF8 = function(a) { - if (typeof a === 'boolean') { - utf8 = a; - } - return this; - }; - - /* private methods */ - - /** - * Calculate the rmd160 of a raw string - */ - - function rstr(s) { - s = (utf8) ? utf8Encode(s) : s; - return binl2rstr(binl(rstr2binl(s), s.length * 8)); - } - - /** - * Calculate the HMAC-rmd160 of a key and some data (raw strings) - */ - - function rstr_hmac(key, data) { - key = (utf8) ? utf8Encode(key) : key; - data = (utf8) ? utf8Encode(data) : data; - var i, hash, - bkey = rstr2binl(key), - ipad = Array(16), - opad = Array(16); - - if (bkey.length > 16) { - bkey = binl(bkey, key.length * 8); - } - - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; - } - hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8); - return binl2rstr(binl(opad.concat(hash), 512 + 160)); - } - - /** - * Convert an array of little-endian words to a string - */ - - function binl2rstr(input) { - var i, output = '', - l = input.length * 32; - for (i = 0; i < l; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); - } - return output; - } - - /** - * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length. - */ - - function binl(x, len) { - var T, j, i, l, - h0 = 0x67452301, - h1 = 0xefcdab89, - h2 = 0x98badcfe, - h3 = 0x10325476, - h4 = 0xc3d2e1f0, - A1, B1, C1, D1, E1, - A2, B2, C2, D2, E2; - - /* append padding */ - x[len >> 5] |= 0x80 << (len % 32); - x[(((len + 64) >>> 9) << 4) + 14] = len; - l = x.length; - - for (i = 0; i < l; i += 16) { - A1 = A2 = h0; - B1 = B2 = h1; - C1 = C2 = h2; - D1 = D2 = h3; - E1 = E2 = h4; - for (j = 0; j <= 79; j += 1) { - T = safe_add(A1, rmd160_f(j, B1, C1, D1)); - T = safe_add(T, x[i + rmd160_r1[j]]); - T = safe_add(T, rmd160_K1(j)); - T = safe_add(bit_rol(T, rmd160_s1[j]), E1); - A1 = E1; - E1 = D1; - D1 = bit_rol(C1, 10); - C1 = B1; - B1 = T; - T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2)); - T = safe_add(T, x[i + rmd160_r2[j]]); - T = safe_add(T, rmd160_K2(j)); - T = safe_add(bit_rol(T, rmd160_s2[j]), E2); - A2 = E2; - E2 = D2; - D2 = bit_rol(C2, 10); - C2 = B2; - B2 = T; - } - - T = safe_add(h1, safe_add(C1, D2)); - h1 = safe_add(h2, safe_add(D1, E2)); - h2 = safe_add(h3, safe_add(E1, A2)); - h3 = safe_add(h4, safe_add(A1, B2)); - h4 = safe_add(h0, safe_add(B1, C2)); - h0 = T; - } - return [h0, h1, h2, h3, h4]; - } - - // specific algorithm methods - - function rmd160_f(j, x, y, z) { - return (0 <= j && j <= 15) ? (x ^ y ^ z) : - (16 <= j && j <= 31) ? (x & y) | (~x & z) : - (32 <= j && j <= 47) ? (x | ~y) ^ z : - (48 <= j && j <= 63) ? (x & z) | (y & ~z) : - (64 <= j && j <= 79) ? x ^ (y | ~z) : - 'rmd160_f: j out of range'; - } - - function rmd160_K1(j) { - return (0 <= j && j <= 15) ? 0x00000000 : - (16 <= j && j <= 31) ? 0x5a827999 : - (32 <= j && j <= 47) ? 0x6ed9eba1 : - (48 <= j && j <= 63) ? 0x8f1bbcdc : - (64 <= j && j <= 79) ? 0xa953fd4e : - 'rmd160_K1: j out of range'; - } - - function rmd160_K2(j) { - return (0 <= j && j <= 15) ? 0x50a28be6 : - (16 <= j && j <= 31) ? 0x5c4dd124 : - (32 <= j && j <= 47) ? 0x6d703ef3 : - (48 <= j && j <= 63) ? 0x7a6d76e9 : - (64 <= j && j <= 79) ? 0x00000000 : - 'rmd160_K2: j out of range'; - } - } - }; - - // exposes Hashes - (function(window, undefined$1) { - var freeExports = false; - { - freeExports = exports; - if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) { - window = commonjsGlobal; - } - } - - if (typeof undefined$1 === 'function' && typeof undefined$1.amd === 'object' && undefined$1.amd) { - // define as an anonymous module, so, through path mapping, it can be aliased - undefined$1(function() { - return Hashes; - }); - } else if (freeExports) { - // in Node.js or RingoJS v0.8.0+ - if ( module && module.exports === freeExports) { - module.exports = Hashes; - } - // in Narwhal or RingoJS v0.7.0- - else { - freeExports.Hashes = Hashes; - } - } else { - // in a browser or Rhino - window.Hashes = Hashes; - } - }(this)); - }()); // IIFE - }); - - var immutable = extend$2; - - var hasOwnProperty$3 = Object.prototype.hasOwnProperty; - - function extend$2() { - var target = {}; - - for (var i = 0; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (hasOwnProperty$3.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target - } - - var sha1 = new hashes.SHA1(); - - var ohauth = {}; - - ohauth.qsString = function(obj) { - return Object.keys(obj).sort().map(function(key) { - return ohauth.percentEncode(key) + '=' + - ohauth.percentEncode(obj[key]); - }).join('&'); - }; - - ohauth.stringQs = function(str) { - return str.split('&').filter(function (pair) { - return pair !== ''; - }).reduce(function(obj, pair){ - var parts = pair.split('='); - obj[decodeURIComponent(parts[0])] = (null === parts[1]) ? - '' : decodeURIComponent(parts[1]); - return obj; - }, {}); - }; - - ohauth.rawxhr = function(method, url, data, headers, callback) { - var xhr = new XMLHttpRequest(), - twoHundred = /^20\d$/; - xhr.onreadystatechange = function() { - if (4 === xhr.readyState && 0 !== xhr.status) { - if (twoHundred.test(xhr.status)) callback(null, xhr); - else return callback(xhr, null); - } - }; - xhr.onerror = function(e) { return callback(e, null); }; - xhr.open(method, url, true); - for (var h in headers) xhr.setRequestHeader(h, headers[h]); - xhr.send(data); - return xhr; - }; - - ohauth.xhr = function(method, url, auth, data, options, callback) { - var headers = (options && options.header) || { - 'Content-Type': 'application/x-www-form-urlencoded' - }; - headers.Authorization = 'OAuth ' + ohauth.authHeader(auth); - return ohauth.rawxhr(method, url, data, headers, callback); - }; - - ohauth.nonce = function() { - for (var o = ''; o.length < 6;) { - o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)]; - } - return o; - }; - - ohauth.authHeader = function(obj) { - return Object.keys(obj).sort().map(function(key) { - return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"'; - }).join(', '); - }; - - ohauth.timestamp = function() { return ~~((+new Date()) / 1000); }; - - ohauth.percentEncode = function(s) { - return encodeURIComponent(s) - .replace(/\!/g, '%21').replace(/\'/g, '%27') - .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29'); - }; - - ohauth.baseString = function(method, url, params) { - if (params.oauth_signature) delete params.oauth_signature; - return [ - method, - ohauth.percentEncode(url), - ohauth.percentEncode(ohauth.qsString(params))].join('&'); - }; - - ohauth.signature = function(oauth_secret, token_secret, baseString) { - return sha1.b64_hmac( - ohauth.percentEncode(oauth_secret) + '&' + - ohauth.percentEncode(token_secret), - baseString); - }; - - /** - * Takes an options object for configuration (consumer_key, - * consumer_secret, version, signature_method, token, token_secret) - * and returns a function that generates the Authorization header - * for given data. - * - * The returned function takes these parameters: - * - method: GET/POST/... - * - uri: full URI with protocol, port, path and query string - * - extra_params: any extra parameters (that are passed in the POST data), - * can be an object or a from-urlencoded string. - * - * Returned function returns full OAuth header with "OAuth" string in it. - */ - - ohauth.headerGenerator = function(options) { - options = options || {}; - var consumer_key = options.consumer_key || '', - consumer_secret = options.consumer_secret || '', - signature_method = options.signature_method || 'HMAC-SHA1', - version = options.version || '1.0', - token = options.token || '', - token_secret = options.token_secret || ''; - - return function(method, uri, extra_params) { - method = method.toUpperCase(); - if (typeof extra_params === 'string' && extra_params.length > 0) { - extra_params = ohauth.stringQs(extra_params); - } - - var uri_parts = uri.split('?', 2), - base_uri = uri_parts[0]; - - var query_params = uri_parts.length === 2 ? - ohauth.stringQs(uri_parts[1]) : {}; - - var oauth_params = { - oauth_consumer_key: consumer_key, - oauth_signature_method: signature_method, - oauth_version: version, - oauth_timestamp: ohauth.timestamp(), - oauth_nonce: ohauth.nonce() - }; - - if (token) oauth_params.oauth_token = token; - - var all_params = immutable({}, oauth_params, query_params, extra_params), - base_str = ohauth.baseString(method, base_uri, all_params); - - oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str); - - return 'OAuth ' + ohauth.authHeader(oauth_params); - }; - }; - - var ohauth_1 = ohauth; - - var resolveUrl$1 = createCommonjsModule(function (module, exports) { - // Copyright 2014 Simon Lydell - // X11 (“MIT”) Licensed. (See LICENSE.) - - void (function(root, factory) { - { - module.exports = factory(); - } - }(commonjsGlobal, function() { - - function resolveUrl(/* ...urls */) { - var numUrls = arguments.length; - - if (numUrls === 0) { - throw new Error("resolveUrl requires at least one argument; got none.") - } - - var base = document.createElement("base"); - base.href = arguments[0]; - - if (numUrls === 1) { - return base.href - } - - var head = document.getElementsByTagName("head")[0]; - head.insertBefore(base, head.firstChild); - - var a = document.createElement("a"); - var resolved; - - for (var index = 1; index < numUrls; index++) { - a.href = arguments[index]; - resolved = a.href; - base.href = resolved; - } - - head.removeChild(base); - - return resolved - } - - return resolveUrl - - })); - }); - - var assign$1 = make_assign(); - var create$8 = make_create(); - var trim = make_trim(); - var Global = (typeof window !== 'undefined' ? window : commonjsGlobal); - - var util = { - assign: assign$1, - create: create$8, - trim: trim, - bind: bind$3, - slice: slice$5, - each: each, - map: map$5, - pluck: pluck, - isList: isList, - isFunction: isFunction$2, - isObject: isObject$2, - Global: Global - }; - - function make_assign() { - if (Object.assign) { - return Object.assign - } else { - return function shimAssign(obj, props1, props2, etc) { - for (var i = 1; i < arguments.length; i++) { - each(Object(arguments[i]), function(val, key) { - obj[key] = val; - }); - } - return obj - } - } - } - - function make_create() { - if (Object.create) { - return function create(obj, assignProps1, assignProps2, etc) { - var assignArgsList = slice$5(arguments, 1); - return assign$1.apply(this, [Object.create(obj)].concat(assignArgsList)) - } - } else { - function F() {} // eslint-disable-line no-inner-declarations - return function create(obj, assignProps1, assignProps2, etc) { - var assignArgsList = slice$5(arguments, 1); - F.prototype = obj; - return assign$1.apply(this, [new F()].concat(assignArgsList)) - } - } - } - - function make_trim() { - if (String.prototype.trim) { - return function trim(str) { - return String.prototype.trim.call(str) - } - } else { - return function trim(str) { - return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') - } - } - } - - function bind$3(obj, fn) { - return function() { - return fn.apply(obj, Array.prototype.slice.call(arguments, 0)) - } - } - - function slice$5(arr, index) { - return Array.prototype.slice.call(arr, index || 0) - } - - function each(obj, fn) { - pluck(obj, function(val, key) { - fn(val, key); - return false - }); - } - - function map$5(obj, fn) { - var res = (isList(obj) ? [] : {}); - pluck(obj, function(v, k) { - res[k] = fn(v, k); - return false - }); - return res - } - - function pluck(obj, fn) { - if (isList(obj)) { - for (var i=0; i= 0; i--) { - var key = localStorage$1().key(i); - fn(read(key), key); - } - } - - function remove$2(key) { - return localStorage$1().removeItem(key) - } - - function clearAll() { - return localStorage$1().clear() - } - - // oldFF-globalStorage provides storage for Firefox - // versions 6 and 7, where no localStorage, etc - // is available. - - - var Global$2 = util.Global; - - var oldFFGlobalStorage = { - name: 'oldFF-globalStorage', - read: read$1, - write: write$1, - each: each$3, - remove: remove$3, - clearAll: clearAll$1, - }; - - var globalStorage = Global$2.globalStorage; - - function read$1(key) { - return globalStorage[key] - } - - function write$1(key, data) { - globalStorage[key] = data; - } - - function each$3(fn) { - for (var i = globalStorage.length - 1; i >= 0; i--) { - var key = globalStorage.key(i); - fn(globalStorage[key], key); - } - } - - function remove$3(key) { - return globalStorage.removeItem(key) - } - - function clearAll$1() { - each$3(function(key, _) { - delete globalStorage[key]; - }); - } - - // oldIE-userDataStorage provides storage for Internet Explorer - // versions 6 and 7, where no localStorage, sessionStorage, etc - // is available. - - - var Global$3 = util.Global; - - var oldIEUserDataStorage = { - name: 'oldIE-userDataStorage', - write: write$2, - read: read$2, - each: each$4, - remove: remove$4, - clearAll: clearAll$2, - }; - - var storageName = 'storejs'; - var doc = Global$3.document; - var _withStorageEl = _makeIEStorageElFunction(); - var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x - - function write$2(unfixedKey, data) { - if (disable) { return } - var fixedKey = fixKey(unfixedKey); - _withStorageEl(function(storageEl) { - storageEl.setAttribute(fixedKey, data); - storageEl.save(storageName); - }); - } - - function read$2(unfixedKey) { - if (disable) { return } - var fixedKey = fixKey(unfixedKey); - var res = null; - _withStorageEl(function(storageEl) { - res = storageEl.getAttribute(fixedKey); - }); - return res - } - - function each$4(callback) { - _withStorageEl(function(storageEl) { - var attributes = storageEl.XMLDocument.documentElement.attributes; - for (var i=attributes.length-1; i>=0; i--) { - var attr = attributes[i]; - callback(storageEl.getAttribute(attr.name), attr.name); - } - }); - } - - function remove$4(unfixedKey) { - var fixedKey = fixKey(unfixedKey); - _withStorageEl(function(storageEl) { - storageEl.removeAttribute(fixedKey); - storageEl.save(storageName); - }); - } - - function clearAll$2() { - _withStorageEl(function(storageEl) { - var attributes = storageEl.XMLDocument.documentElement.attributes; - storageEl.load(storageName); - for (var i=attributes.length-1; i>=0; i--) { - storageEl.removeAttribute(attributes[i].name); - } - storageEl.save(storageName); - }); - } - - // Helpers - ////////// - - // In IE7, keys cannot start with a digit or contain certain chars. - // See https://github.com/marcuswestin/store.js/issues/40 - // See https://github.com/marcuswestin/store.js/issues/83 - var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g"); - function fixKey(key) { - return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___') - } - - function _makeIEStorageElFunction() { - if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) { - return null - } - var scriptTag = 'script', - storageOwner, - storageContainer, - storageEl; - - // Since #userData storage applies only to specific paths, we need to - // somehow link our data to a specific path. We choose /favicon.ico - // as a pretty safe option, since all browsers already make a request to - // this URL anyway and being a 404 will not hurt us here. We wrap an - // iframe pointing to the favicon in an ActiveXObject(htmlfile) object - // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx) - // since the iframe access rules appear to allow direct access and - // manipulation of the document element, even for a 404 page. This - // document can be used instead of the current document (which would - // have been limited to the current path) to perform #userData storage. - try { - /* global ActiveXObject */ - storageContainer = new ActiveXObject('htmlfile'); - storageContainer.open(); - storageContainer.write('<'+scriptTag+'>document.w=window'); - storageContainer.close(); - storageOwner = storageContainer.w.frames[0].document; - storageEl = storageOwner.createElement('div'); - } catch(e) { - // somehow ActiveXObject instantiation failed (perhaps some special - // security settings or otherwse), fall back to per-path storage - storageEl = doc.createElement('div'); - storageOwner = doc.body; - } - - return function(storeFunction) { - var args = [].slice.call(arguments, 0); - args.unshift(storageEl); - // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx - // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx - storageOwner.appendChild(storageEl); - storageEl.addBehavior('#default#userData'); - storageEl.load(storageName); - storeFunction.apply(this, args); - storageOwner.removeChild(storageEl); - return - } - } - - // cookieStorage is useful Safari private browser mode, where localStorage - // doesn't work but cookies do. This implementation is adopted from - // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage - - - var Global$4 = util.Global; - var trim$1 = util.trim; - - var cookieStorage = { - name: 'cookieStorage', - read: read$3, - write: write$3, - each: each$5, - remove: remove$5, - clearAll: clearAll$3, - }; - - var doc$1 = Global$4.document; - - function read$3(key) { - if (!key || !_has(key)) { return null } - var regexpStr = "(?:^|.*;\\s*)" + - escape(key).replace(/[\-\.\+\*]/g, "\\$&") + - "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"; - return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1")) - } - - function each$5(callback) { - var cookies = doc$1.cookie.split(/; ?/g); - for (var i = cookies.length - 1; i >= 0; i--) { - if (!trim$1(cookies[i])) { - continue - } - var kvp = cookies[i].split('='); - var key = unescape(kvp[0]); - var val = unescape(kvp[1]); - callback(val, key); - } - } - - function write$3(key, data) { - if(!key) { return } - doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/"; - } - - function remove$5(key) { - if (!key || !_has(key)) { - return - } - doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/"; - } - - function clearAll$3() { - each$5(function(_, key) { - remove$5(key); - }); - } - - function _has(key) { - return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie) - } - - var Global$5 = util.Global; - - var sessionStorage_1 = { - name: 'sessionStorage', - read: read$4, - write: write$4, - each: each$6, - remove: remove$6, - clearAll: clearAll$4 - }; - - function sessionStorage() { - return Global$5.sessionStorage - } - - function read$4(key) { - return sessionStorage().getItem(key) - } - - function write$4(key, data) { - return sessionStorage().setItem(key, data) - } - - function each$6(fn) { - for (var i = sessionStorage().length - 1; i >= 0; i--) { - var key = sessionStorage().key(i); - fn(read$4(key), key); - } - } - - function remove$6(key) { - return sessionStorage().removeItem(key) - } - - function clearAll$4() { - return sessionStorage().clear() - } - - // memoryStorage is a useful last fallback to ensure that the store - // is functions (meaning store.get(), store.set(), etc will all function). - // However, stored values will not persist when the browser navigates to - // a new page or reloads the current page. - - var memoryStorage_1 = { - name: 'memoryStorage', - read: read$5, - write: write$5, - each: each$7, - remove: remove$7, - clearAll: clearAll$5, - }; - - var memoryStorage = {}; - - function read$5(key) { - return memoryStorage[key] - } - - function write$5(key, data) { - memoryStorage[key] = data; - } - - function each$7(callback) { - for (var key in memoryStorage) { - if (memoryStorage.hasOwnProperty(key)) { - callback(memoryStorage[key], key); - } - } - } - - function remove$7(key) { - delete memoryStorage[key]; - } - - function clearAll$5(key) { - memoryStorage = {}; - } - - var all = [ - // Listed in order of usage preference - localStorage_1, - oldFFGlobalStorage, - oldIEUserDataStorage, - cookieStorage, - sessionStorage_1, - memoryStorage_1 - ]; - - /* eslint-disable */ - - // json2.js - // 2016-10-28 - // Public Domain. - // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - // See http://www.JSON.org/js.html - // This code should be minified before deployment. - // See http://javascript.crockford.com/jsmin.html - - // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - // NOT CONTROL. - - // This file creates a global JSON object containing two methods: stringify - // and parse. This file provides the ES5 JSON capability to ES3 systems. - // If a project might run on IE8 or earlier, then this file should be included. - // This file does nothing on ES5 systems. - - // JSON.stringify(value, replacer, space) - // value any JavaScript value, usually an object or array. - // replacer an optional parameter that determines how object - // values are stringified for objects. It can be a - // function or an array of strings. - // space an optional parameter that specifies the indentation - // of nested structures. If it is omitted, the text will - // be packed without extra whitespace. If it is a number, - // it will specify the number of spaces to indent at each - // level. If it is a string (such as "\t" or " "), - // it contains the characters used to indent at each level. - // This method produces a JSON text from a JavaScript value. - // When an object value is found, if the object contains a toJSON - // method, its toJSON method will be called and the result will be - // stringified. A toJSON method does not serialize: it returns the - // value represented by the name/value pair that should be serialized, - // or undefined if nothing should be serialized. The toJSON method - // will be passed the key associated with the value, and this will be - // bound to the value. - - // For example, this would serialize Dates as ISO strings. - - // Date.prototype.toJSON = function (key) { - // function f(n) { - // // Format integers to have at least two digits. - // return (n < 10) - // ? "0" + n - // : n; - // } - // return this.getUTCFullYear() + "-" + - // f(this.getUTCMonth() + 1) + "-" + - // f(this.getUTCDate()) + "T" + - // f(this.getUTCHours()) + ":" + - // f(this.getUTCMinutes()) + ":" + - // f(this.getUTCSeconds()) + "Z"; - // }; - - // You can provide an optional replacer method. It will be passed the - // key and value of each member, with this bound to the containing - // object. The value that is returned from your method will be - // serialized. If your method returns undefined, then the member will - // be excluded from the serialization. - - // If the replacer parameter is an array of strings, then it will be - // used to select the members to be serialized. It filters the results - // such that only members with keys listed in the replacer array are - // stringified. - - // Values that do not have JSON representations, such as undefined or - // functions, will not be serialized. Such values in objects will be - // dropped; in arrays they will be replaced with null. You can use - // a replacer function to replace those with JSON values. - - // JSON.stringify(undefined) returns undefined. - - // The optional space parameter produces a stringification of the - // value that is filled with line breaks and indentation to make it - // easier to read. - - // If the space parameter is a non-empty string, then that string will - // be used for indentation. If the space parameter is a number, then - // the indentation will be that many spaces. - - // Example: - - // text = JSON.stringify(["e", {pluribus: "unum"}]); - // // text is '["e",{"pluribus":"unum"}]' - - // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); - // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - // text = JSON.stringify([new Date()], function (key, value) { - // return this[key] instanceof Date - // ? "Date(" + this[key] + ")" - // : value; - // }); - // // text is '["Date(---current time---)"]' - - // JSON.parse(text, reviver) - // This method parses a JSON text to produce an object or array. - // It can throw a SyntaxError exception. - - // The optional reviver parameter is a function that can filter and - // transform the results. It receives each of the keys and values, - // and its return value is used instead of the original value. - // If it returns what it received, then the structure is not modified. - // If it returns undefined then the member is deleted. - - // Example: - - // // Parse the text. Values that look like ISO date strings will - // // be converted to Date objects. - - // myData = JSON.parse(text, function (key, value) { - // var a; - // if (typeof value === "string") { - // a = - // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - // if (a) { - // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - // +a[5], +a[6])); - // } - // } - // return value; - // }); - - // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - // var d; - // if (typeof value === "string" && - // value.slice(0, 5) === "Date(" && - // value.slice(-1) === ")") { - // d = new Date(value.slice(5, -1)); - // if (d) { - // return d; - // } - // } - // return value; - // }); - - // This is a reference implementation. You are free to copy, modify, or - // redistribute. - - /*jslint - eval, for, this - */ - - /*property - JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf - */ - - - // Create a JSON object only if one does not already exist. We create the - // methods in a closure to avoid creating global variables. - - if (typeof JSON !== "object") { - JSON = {}; - } - - (function () { - - var rx_one = /^[\],:{}\s]*$/; - var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; - var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; - var rx_four = /(?:^|:|,)(?:\s*\[)+/g; - var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 - ? "0" + n - : n; - } - - function this_value() { - return this.valueOf(); - } - - if (typeof Date.prototype.toJSON !== "function") { - - Date.prototype.toJSON = function () { - - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + "-" + - f(this.getUTCMonth() + 1) + "-" + - f(this.getUTCDate()) + "T" + - f(this.getUTCHours()) + ":" + - f(this.getUTCMinutes()) + ":" + - f(this.getUTCSeconds()) + "Z" - : null; - }; - - Boolean.prototype.toJSON = this_value; - Number.prototype.toJSON = this_value; - String.prototype.toJSON = this_value; - } - - var gap; - var indent; - var meta; - var rep; - - - function quote(string) { - - // If the string contains no control characters, no quote characters, and no - // backslash characters, then we can safely slap some quotes around it. - // Otherwise we must also replace the offending characters with safe escape - // sequences. - - rx_escapable.lastIndex = 0; - return rx_escapable.test(string) - ? "\"" + string.replace(rx_escapable, function (a) { - var c = meta[a]; - return typeof c === "string" - ? c - : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); - }) + "\"" - : "\"" + string + "\""; - } - - - function str(key, holder) { - - // Produce a string from holder[key]. - - var i; // The loop counter. - var k; // The member key. - var v; // The member value. - var length; - var mind = gap; - var partial; - var value = holder[key]; - - // If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === "object" && - typeof value.toJSON === "function") { - value = value.toJSON(key); - } - - // If we were called with a replacer function, then call the replacer to - // obtain a replacement value. - - if (typeof rep === "function") { - value = rep.call(holder, key, value); - } - - // What happens next depends on the value's type. - - switch (typeof value) { - case "string": - return quote(value); - - case "number": - - // JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) - ? String(value) - : "null"; - - case "boolean": - case "null": - - // If the value is a boolean or null, convert it to a string. Note: - // typeof null does not produce "null". The case is included here in - // the remote chance that this gets fixed someday. - - return String(value); - - // If the type is "object", we might be dealing with an object or an array or - // null. - - case "object": - - // Due to a specification blunder in ECMAScript, typeof null is "object", - // so watch out for that case. - - if (!value) { - return "null"; - } - - // Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - - // Is the value an array? - - if (Object.prototype.toString.apply(value) === "[object Array]") { - - // The value is an array. Stringify every element. Use null as a placeholder - // for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || "null"; - } - - // Join all of the elements together, separated with commas, and wrap them in - // brackets. - - v = partial.length === 0 - ? "[]" - : gap - ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" - : "[" + partial.join(",") + "]"; - gap = mind; - return v; - } - - // If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === "object") { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === "string") { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + ( - gap - ? ": " - : ":" - ) + v); - } - } - } - } else { - - // Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + ( - gap - ? ": " - : ":" - ) + v); - } - } - } - } - - // Join all of the member texts together, separated with commas, - // and wrap them in braces. - - v = partial.length === 0 - ? "{}" - : gap - ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" - : "{" + partial.join(",") + "}"; - gap = mind; - return v; - } - } - - // If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== "function") { - meta = { // table of character substitutions - "\b": "\\b", - "\t": "\\t", - "\n": "\\n", - "\f": "\\f", - "\r": "\\r", - "\"": "\\\"", - "\\": "\\\\" - }; - JSON.stringify = function (value, replacer, space) { - - // The stringify method takes a value and an optional replacer, and an optional - // space parameter, and returns a JSON text. The replacer can be a function - // that can replace values, or an array of strings that will select the keys. - // A default replacer method can be provided. Use of the space parameter can - // produce text that is more easily readable. - - var i; - gap = ""; - indent = ""; - - // If the space parameter is a number, make an indent string containing that - // many spaces. - - if (typeof space === "number") { - for (i = 0; i < space; i += 1) { - indent += " "; - } - - // If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === "string") { - indent = space; - } - - // If there is a replacer, it must be a function or an array. - // Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== "function" && - (typeof replacer !== "object" || - typeof replacer.length !== "number")) { - throw new Error("JSON.stringify"); - } - - // Make a fake root object containing our value under the key of "". - // Return the result of stringifying the value. - - return str("", {"": value}); - }; - } - - - // If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== "function") { - JSON.parse = function (text, reviver) { - - // The parse method takes a text and an optional reviver function, and returns - // a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure so - // that modifications can be made. - - var k; - var v; - var value = holder[key]; - if (value && typeof value === "object") { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace certain - // Unicode characters with escape sequences. JavaScript handles many characters - // incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - rx_dangerous.lastIndex = 0; - if (rx_dangerous.test(text)) { - text = text.replace(rx_dangerous, function (a) { - return "\\u" + - ("0000" + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that look - // for non-JSON patterns. We are especially concerned with "()" and "new" - // because they can cause invocation, and "=" because it can cause mutation. - // But just to be safe, we want to reject all unexpected forms. - - // We split the second stage into 4 regexp operations in order to work around - // crippling inefficiencies in IE's and Safari's regexp engines. First we - // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we - // replace all simple value tokens with "]" characters. Third, we delete all - // open brackets that follow a colon or comma or that begin the text. Finally, - // we look to see that the remaining characters are only whitespace or "]" or - // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. - - if ( - rx_one.test( - text - .replace(rx_two, "@") - .replace(rx_three, "]") - .replace(rx_four, "") - ) - ) { - - // In the third stage we use the eval function to compile the text into a - // JavaScript structure. The "{" operator is subject to a syntactic ambiguity - // in JavaScript: it can begin a block or an object literal. We wrap the text - // in parens to eliminate the ambiguity. - - j = eval("(" + text + ")"); - - // In the optional fourth stage, we recursively walk the new structure, passing - // each name/value pair to a reviver function for possible transformation. - - return (typeof reviver === "function") - ? walk({"": j}, "") - : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError("JSON.parse"); - }; - } - }()); - - var json2 = json2Plugin; - - function json2Plugin() { - - return {} - } - - var plugins = [json2]; - - var store_legacy = storeEngine.createStore(all, plugins); - - // # osm-auth - // - // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo) - // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing), - // does not support custom headers, which this uses everywhere. - var osmAuth = function(o) { - - var oauth = {}; - - // authenticated users will also have a request token secret, but it's - // not used in transactions with the server - oauth.authenticated = function() { - return !!(token('oauth_token') && token('oauth_token_secret')); - }; - - oauth.logout = function() { - token('oauth_token', ''); - token('oauth_token_secret', ''); - token('oauth_request_token_secret', ''); - return oauth; - }; - - // TODO: detect lack of click event - oauth.authenticate = function(callback) { - if (oauth.authenticated()) return callback(); - - oauth.logout(); - - // ## Getting a request token - var params = timenonce(getAuth(o)), - url = o.url + '/oauth/request_token'; - - params.oauth_signature = ohauth_1.signature( - o.oauth_secret, '', - ohauth_1.baseString('POST', url, params)); - - if (!o.singlepage) { - // Create a 600x550 popup window in the center of the screen - var w = 600, h = 550, - settings = [ - ['width', w], ['height', h], - ['left', screen.width / 2 - w / 2], - ['top', screen.height / 2 - h / 2]].map(function(x) { - return x.join('='); - }).join(','), - popup = window.open('about:blank', 'oauth_window', settings); - } - - // Request a request token. When this is complete, the popup - // window is redirected to OSM's authorization page. - ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone); - o.loading(); - - function reqTokenDone(err, xhr) { - o.done(); - if (err) return callback(err); - var resp = ohauth_1.stringQs(xhr.response); - token('oauth_request_token_secret', resp.oauth_token_secret); - var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({ - oauth_token: resp.oauth_token, - oauth_callback: resolveUrl$1(o.landing) - }); - - if (o.singlepage) { - location.href = authorize_url; - } else { - popup.location = authorize_url; - } - } - - // Called by a function in a landing page, in the popup window. The - // window closes itself. - window.authComplete = function(token) { - var oauth_token = ohauth_1.stringQs(token.split('?')[1]); - get_access_token(oauth_token.oauth_token); - delete window.authComplete; - }; - - // ## Getting an request token - // - // At this point we have an `oauth_token`, brought in from a function - // call on a landing page popup. - function get_access_token(oauth_token) { - var url = o.url + '/oauth/access_token', - params = timenonce(getAuth(o)), - request_token_secret = token('oauth_request_token_secret'); - params.oauth_token = oauth_token; - params.oauth_signature = ohauth_1.signature( - o.oauth_secret, - request_token_secret, - ohauth_1.baseString('POST', url, params)); - - // ## Getting an access token - // - // The final token required for authentication. At this point - // we have a `request token secret` - ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone); - o.loading(); - } - - function accessTokenDone(err, xhr) { - o.done(); - if (err) return callback(err); - var access_token = ohauth_1.stringQs(xhr.response); - token('oauth_token', access_token.oauth_token); - token('oauth_token_secret', access_token.oauth_token_secret); - callback(null, oauth); - } - }; - - oauth.bootstrapToken = function(oauth_token, callback) { - // ## Getting an request token - // At this point we have an `oauth_token`, brought in from a function - // call on a landing page popup. - function get_access_token(oauth_token) { - var url = o.url + '/oauth/access_token', - params = timenonce(getAuth(o)), - request_token_secret = token('oauth_request_token_secret'); - params.oauth_token = oauth_token; - params.oauth_signature = ohauth_1.signature( - o.oauth_secret, - request_token_secret, - ohauth_1.baseString('POST', url, params)); - - // ## Getting an access token - // The final token required for authentication. At this point - // we have a `request token secret` - ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone); - o.loading(); - } - - function accessTokenDone(err, xhr) { - o.done(); - if (err) return callback(err); - var access_token = ohauth_1.stringQs(xhr.response); - token('oauth_token', access_token.oauth_token); - token('oauth_token_secret', access_token.oauth_token_secret); - callback(null, oauth); - } - - get_access_token(oauth_token); - }; - - // # xhr - // - // A single XMLHttpRequest wrapper that does authenticated calls if the - // user has logged in. - oauth.xhr = function(options, callback) { - if (!oauth.authenticated()) { - if (o.auto) { - return oauth.authenticate(run); - } else { - callback('not authenticated', null); - return; - } - } else { - return run(); - } - - function run() { - var params = timenonce(getAuth(o)), - oauth_token_secret = token('oauth_token_secret'), - url = (options.prefix !== false) ? o.url + options.path : options.path, - url_parts = url.replace(/#.*$/, '').split('?', 2), - base_url = url_parts[0], - query = (url_parts.length === 2) ? url_parts[1] : ''; - - // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 - if ((!options.options || !options.options.header || - options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && - options.content) { - params = immutable(params, ohauth_1.stringQs(options.content)); - } - - params.oauth_token = token('oauth_token'); - params.oauth_signature = ohauth_1.signature( - o.oauth_secret, - oauth_token_secret, - ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query))) - ); - - return ohauth_1.xhr(options.method, url, params, options.content, options.options, done); - } - - function done(err, xhr) { - if (err) return callback(err); - else if (xhr.responseXML) return callback(err, xhr.responseXML); - else return callback(err, xhr.response); - } - }; - - // pre-authorize this object, if we can just get a token and token_secret - // from the start - oauth.preauth = function(c) { - if (!c) return; - if (c.oauth_token) token('oauth_token', c.oauth_token); - if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret); - return oauth; - }; - - oauth.options = function(_) { - if (!arguments.length) return o; - - o = _; - o.url = o.url || 'https://www.openstreetmap.org'; - o.landing = o.landing || 'land.html'; - o.singlepage = o.singlepage || false; - - // Optional loading and loading-done functions for nice UI feedback. - // by default, no-ops - o.loading = o.loading || function() {}; - o.done = o.done || function() {}; - - return oauth.preauth(o); - }; - - // 'stamp' an authentication object from `getAuth()` - // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce) - // and timestamp - function timenonce(o) { - o.oauth_timestamp = ohauth_1.timestamp(); - o.oauth_nonce = ohauth_1.nonce(); - return o; - } - - // get/set tokens. These are prefixed with the base URL so that `osm-auth` - // can be used with multiple APIs and the keys in `localStorage` - // will not clash - var token; - - if (store_legacy.enabled) { - token = function (x, y) { - if (arguments.length === 1) return store_legacy.get(o.url + x); - else if (arguments.length === 2) return store_legacy.set(o.url + x, y); - }; - } else { - var storage = {}; - token = function (x, y) { - if (arguments.length === 1) return storage[o.url + x]; - else if (arguments.length === 2) return storage[o.url + x] = y; - }; - } - - // Get an authentication object. If you just add and remove properties - // from a single object, you'll need to use `delete` to make sure that - // it doesn't contain undesired properties for authentication - function getAuth(o) { - return { - oauth_consumer_key: o.oauth_consumer_key, - oauth_signature_method: 'HMAC-SHA1' - }; - } - - // potentially pre-authorize - oauth.options(o); - - return oauth; - }; - - var JXON = new (function () { - var - sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */ - aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i; - - function parseText (sValue) { - if (rIsNull.test(sValue)) { return null; } - if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; } - if (isFinite(sValue)) { return parseFloat(sValue); } - if (isFinite(Date.parse(sValue))) { return new Date(sValue); } - return sValue; - } - - function EmptyTree () { } - EmptyTree.prototype.toString = function () { return 'null'; }; - EmptyTree.prototype.valueOf = function () { return null; }; - - function objectify (vValue) { - return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue); - } - - function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) { - var - nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(), - bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2); - - var - sProp, vContent, nLength = 0, sCollectedTxt = '', - vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true; - - if (bChildren) { - for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) { - oNode = oParentNode.childNodes.item(nItem); - if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */ - else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */ - else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */ - } - } - - var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt); - - if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; } - - for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) { - sProp = aCache[nElId].nodeName.toLowerCase(); - vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr); - if (vResult.hasOwnProperty(sProp)) { - if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; } - vResult[sProp].push(vContent); - } else { - vResult[sProp] = vContent; - nLength++; - } - } - - if (bAttributes) { - var - nAttrLen = oParentNode.attributes.length, - sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult; - - for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) { - oAttrib = oParentNode.attributes.item(nAttrib); - oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim()); - } - - if (bNesteAttr) { - if (bFreeze) { Object.freeze(oAttrParent); } - vResult[sAttributesProp] = oAttrParent; - nLength -= nAttrLen - 1; - } - } - - if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) { - vResult[sValueProp] = vBuiltVal; - } else if (!bHighVerb && nLength === 0 && sCollectedTxt) { - vResult = vBuiltVal; - } - - if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); } - - aCache.length = nLevelStart; - - return vResult; - } - - function loadObjTree (oXMLDoc, oParentEl, oParentObj) { - var vValue, oChild; - - if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) { - oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */ - } else if (oParentObj.constructor === Date) { - oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString())); - } - - for (var sName in oParentObj) { - vValue = oParentObj[sName]; - if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */ - if (sName === sValueProp) { - if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); } - } else if (sName === sAttributesProp) { /* verbosity level is 3 */ - for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); } - } else if (sName.charAt(0) === sAttrPref) { - oParentEl.setAttribute(sName.slice(1), vValue); - } else if (vValue.constructor === Array) { - for (var nItem = 0; nItem < vValue.length; nItem++) { - oChild = oXMLDoc.createElement(sName); - loadObjTree(oXMLDoc, oChild, vValue[nItem]); - oParentEl.appendChild(oChild); - } - } else { - oChild = oXMLDoc.createElement(sName); - if (vValue instanceof Object) { - loadObjTree(oXMLDoc, oChild, vValue); - } else if (vValue !== null && vValue !== true) { - oChild.appendChild(oXMLDoc.createTextNode(vValue.toString())); - } - oParentEl.appendChild(oChild); - } - } - } - - this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) { - var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1; - return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3); - }; - - this.unbuild = function (oObjTree) { - var oNewDoc = document.implementation.createDocument('', '', null); - loadObjTree(oNewDoc, oNewDoc, oObjTree); - return oNewDoc; - }; - - this.stringify = function (oObjTree) { - return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree)); - }; - })(); - - // var myObject = JXON.build(doc); - // we got our javascript object! try: alert(JSON.stringify(myObject)); - - // var newDoc = JXON.unbuild(myObject); - // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc)); - - var tiler$5 = utilTiler(); - var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes'); - var urlroot = 'https://www.openstreetmap.org'; - var oauth = osmAuth({ - url: urlroot, - oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT', - oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL', - loading: authLoading, - done: authDone - }); - - var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*']; - var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() }; - var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() }; - var _userCache = { toLoad: {}, user: {} }; - var _cachedApiStatus; - var _changeset = {}; - - var _deferred = new Set(); - var _connectionID = 1; - var _tileZoom$3 = 16; - var _noteZoom = 12; - var _rateLimitError; - var _userChangesets; - var _userDetails; - var _off; - - // set a default but also load this from the API status - var _maxWayNodes = 2000; - - - function authLoading() { - dispatch$6.call('authLoading'); - } - - - function authDone() { - dispatch$6.call('authDone'); - } - - - function abortRequest$5(controllerOrXHR) { - if (controllerOrXHR) { - controllerOrXHR.abort(); - } - } - - - function hasInflightRequests(cache) { - return Object.keys(cache.inflight).length; - } - - - function abortUnwantedRequests$3(cache, visibleTiles) { - Object.keys(cache.inflight).forEach(function(k) { - if (cache.toLoad[k]) return; - if (visibleTiles.find(function(tile) { return k === tile.id; })) return; - - abortRequest$5(cache.inflight[k]); - delete cache.inflight[k]; - }); - } - - - function getLoc(attrs) { - var lon = attrs.lon && attrs.lon.value; - var lat = attrs.lat && attrs.lat.value; - return [parseFloat(lon), parseFloat(lat)]; - } - - - function getNodes(obj) { - var elems = obj.getElementsByTagName('nd'); - var nodes = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - nodes[i] = 'n' + elems[i].attributes.ref.value; - } - return nodes; - } - - function getNodesJSON(obj) { - var elems = obj.nodes; - var nodes = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - nodes[i] = 'n' + elems[i]; - } - return nodes; - } - - function getTags(obj) { - var elems = obj.getElementsByTagName('tag'); - var tags = {}; - for (var i = 0, l = elems.length; i < l; i++) { - var attrs = elems[i].attributes; - tags[attrs.k.value] = attrs.v.value; - } - - return tags; - } - - - function getMembers(obj) { - var elems = obj.getElementsByTagName('member'); - var members = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - var attrs = elems[i].attributes; - members[i] = { - id: attrs.type.value[0] + attrs.ref.value, - type: attrs.type.value, - role: attrs.role.value - }; - } - return members; - } - - function getMembersJSON(obj) { - var elems = obj.members; - var members = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - var attrs = elems[i]; - members[i] = { - id: attrs.type[0] + attrs.ref, - type: attrs.type, - role: attrs.role - }; - } - return members; - } - - function getVisible(attrs) { - return (!attrs.visible || attrs.visible.value !== 'false'); - } - - - function parseComments(comments) { - var parsedComments = []; - - // for each comment - for (var i = 0; i < comments.length; i++) { - var comment = comments[i]; - if (comment.nodeName === 'comment') { - var childNodes = comment.childNodes; - var parsedComment = {}; - - for (var j = 0; j < childNodes.length; j++) { - var node = childNodes[j]; - var nodeName = node.nodeName; - if (nodeName === '#text') continue; - parsedComment[nodeName] = node.textContent; - - if (nodeName === 'uid') { - var uid = node.textContent; - if (uid && !_userCache.user[uid]) { - _userCache.toLoad[uid] = true; - } - } - } - - if (parsedComment) { - parsedComments.push(parsedComment); - } - } - } - return parsedComments; - } - - - function encodeNoteRtree(note) { - return { - minX: note.loc[0], - minY: note.loc[1], - maxX: note.loc[0], - maxY: note.loc[1], - data: note - }; - } - - - var jsonparsers = { - - node: function nodeData(obj, uid) { - return new osmNode({ - id: uid, - visible: typeof obj.visible === 'boolean' ? obj.visible : true, - version: obj.version.toString(), - changeset: obj.changeset.toString(), - timestamp: obj.timestamp, - user: obj.user, - uid: obj.uid.toString(), - loc: [parseFloat(obj.lon), parseFloat(obj.lat)], - tags: obj.tags - }); - }, - - way: function wayData(obj, uid) { - return new osmWay({ - id: uid, - visible: typeof obj.visible === 'boolean' ? obj.visible : true, - version: obj.version.toString(), - changeset: obj.changeset.toString(), - timestamp: obj.timestamp, - user: obj.user, - uid: obj.uid.toString(), - tags: obj.tags, - nodes: getNodesJSON(obj) - }); - }, - - relation: function relationData(obj, uid) { - return new osmRelation({ - id: uid, - visible: typeof obj.visible === 'boolean' ? obj.visible : true, - version: obj.version.toString(), - changeset: obj.changeset.toString(), - timestamp: obj.timestamp, - user: obj.user, - uid: obj.uid.toString(), - tags: obj.tags, - members: getMembersJSON(obj) - }); - } - }; - - function parseJSON(payload, callback, options) { - options = Object.assign({ skipSeen: true }, options); - if (!payload) { - return callback({ message: 'No JSON', status: -1 }); - } - - var json = payload; - if (typeof json !== 'object') - json = JSON.parse(payload); - - if (!json.elements) - return callback({ message: 'No JSON', status: -1 }); - - var children = json.elements; - - var handle = window.requestIdleCallback(function() { - var results = []; - var result; - for (var i = 0; i < children.length; i++) { - result = parseChild(children[i]); - if (result) results.push(result); - } - callback(null, results); - }); - - _deferred.add(handle); - - function parseChild(child) { - var parser = jsonparsers[child.type]; - if (!parser) return null; - - var uid; - - uid = osmEntity.id.fromOSM(child.type, child.id); - if (options.skipSeen) { - if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity - _tileCache.seen[uid] = true; - } - - return parser(child, uid); - } - } - - var parsers = { - node: function nodeData(obj, uid) { - var attrs = obj.attributes; - return new osmNode({ - id: uid, - visible: getVisible(attrs), - version: attrs.version.value, - changeset: attrs.changeset && attrs.changeset.value, - timestamp: attrs.timestamp && attrs.timestamp.value, - user: attrs.user && attrs.user.value, - uid: attrs.uid && attrs.uid.value, - loc: getLoc(attrs), - tags: getTags(obj) - }); - }, - - way: function wayData(obj, uid) { - var attrs = obj.attributes; - return new osmWay({ - id: uid, - visible: getVisible(attrs), - version: attrs.version.value, - changeset: attrs.changeset && attrs.changeset.value, - timestamp: attrs.timestamp && attrs.timestamp.value, - user: attrs.user && attrs.user.value, - uid: attrs.uid && attrs.uid.value, - tags: getTags(obj), - nodes: getNodes(obj), - }); - }, - - relation: function relationData(obj, uid) { - var attrs = obj.attributes; - return new osmRelation({ - id: uid, - visible: getVisible(attrs), - version: attrs.version.value, - changeset: attrs.changeset && attrs.changeset.value, - timestamp: attrs.timestamp && attrs.timestamp.value, - user: attrs.user && attrs.user.value, - uid: attrs.uid && attrs.uid.value, - tags: getTags(obj), - members: getMembers(obj) - }); - }, - - note: function parseNote(obj, uid) { - var attrs = obj.attributes; - var childNodes = obj.childNodes; - var props = {}; - - props.id = uid; - props.loc = getLoc(attrs); - - // if notes are coincident, move them apart slightly - var coincident = false; - var epsilon = 0.00001; - do { - if (coincident) { - props.loc = geoVecAdd(props.loc, [epsilon, epsilon]); - } - var bbox = geoExtent(props.loc).bbox(); - coincident = _noteCache.rtree.search(bbox).length; - } while (coincident); - - // parse note contents - for (var i = 0; i < childNodes.length; i++) { - var node = childNodes[i]; - var nodeName = node.nodeName; - if (nodeName === '#text') continue; - - // if the element is comments, parse the comments - if (nodeName === 'comments') { - props[nodeName] = parseComments(node.childNodes); - } else { - props[nodeName] = node.textContent; - } - } - - var note = new osmNote(props); - var item = encodeNoteRtree(note); - _noteCache.note[note.id] = note; - _noteCache.rtree.insert(item); - - return note; - }, - - user: function parseUser(obj, uid) { - var attrs = obj.attributes; - var user = { - id: uid, - display_name: attrs.display_name && attrs.display_name.value, - account_created: attrs.account_created && attrs.account_created.value, - changesets_count: '0', - active_blocks: '0' - }; - - var img = obj.getElementsByTagName('img'); - if (img && img[0] && img[0].getAttribute('href')) { - user.image_url = img[0].getAttribute('href'); - } - - var changesets = obj.getElementsByTagName('changesets'); - if (changesets && changesets[0] && changesets[0].getAttribute('count')) { - user.changesets_count = changesets[0].getAttribute('count'); - } - - var blocks = obj.getElementsByTagName('blocks'); - if (blocks && blocks[0]) { - var received = blocks[0].getElementsByTagName('received'); - if (received && received[0] && received[0].getAttribute('active')) { - user.active_blocks = received[0].getAttribute('active'); - } - } - - _userCache.user[uid] = user; - delete _userCache.toLoad[uid]; - return user; - } - }; - - - function parseXML(xml, callback, options) { - options = Object.assign({ skipSeen: true }, options); - if (!xml || !xml.childNodes) { - return callback({ message: 'No XML', status: -1 }); - } - - var root = xml.childNodes[0]; - var children = root.childNodes; - - var handle = window.requestIdleCallback(function() { - var results = []; - var result; - for (var i = 0; i < children.length; i++) { - result = parseChild(children[i]); - if (result) results.push(result); - } - callback(null, results); - }); - - _deferred.add(handle); - - - function parseChild(child) { - var parser = parsers[child.nodeName]; - if (!parser) return null; - - var uid; - if (child.nodeName === 'user') { - uid = child.attributes.id.value; - if (options.skipSeen && _userCache.user[uid]) { - delete _userCache.toLoad[uid]; - return null; - } - - } else if (child.nodeName === 'note') { - uid = child.getElementsByTagName('id')[0].textContent; - - } else { - uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value); - if (options.skipSeen) { - if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity - _tileCache.seen[uid] = true; - } - } - - return parser(child, uid); - } - } - - - // replace or remove note from rtree - function updateRtree$3(item, replace) { - _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); - - if (replace) { - _noteCache.rtree.insert(item); - } - } - - - function wrapcb(thisArg, callback, cid) { - return function(err, result) { - if (err) { - // 400 Bad Request, 401 Unauthorized, 403 Forbidden.. - if (err.status === 400 || err.status === 401 || err.status === 403) { - thisArg.logout(); - } - return callback.call(thisArg, err); - - } else if (thisArg.getConnectionId() !== cid) { - return callback.call(thisArg, { message: 'Connection Switched', status: -1 }); - - } else { - return callback.call(thisArg, err, result); - } - }; - } - - - var serviceOsm = { - - init: function() { - utilRebind(this, dispatch$6, 'on'); - }, - - - reset: function() { - Array.from(_deferred).forEach(function(handle) { - window.cancelIdleCallback(handle); - _deferred.delete(handle); - }); - - _connectionID++; - _userChangesets = undefined; - _userDetails = undefined; - _rateLimitError = undefined; - - Object.values(_tileCache.inflight).forEach(abortRequest$5); - Object.values(_noteCache.inflight).forEach(abortRequest$5); - Object.values(_noteCache.inflightPost).forEach(abortRequest$5); - if (_changeset.inflight) abortRequest$5(_changeset.inflight); - - _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() }; - _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() }; - _userCache = { toLoad: {}, user: {} }; - _cachedApiStatus = undefined; - _changeset = {}; - - return this; - }, - - - getConnectionId: function() { - return _connectionID; - }, - - - changesetURL: function(changesetID) { - return urlroot + '/changeset/' + changesetID; - }, - - - changesetsURL: function(center, zoom) { - var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); - return urlroot + '/history#map=' + - Math.floor(zoom) + '/' + - center[1].toFixed(precision) + '/' + - center[0].toFixed(precision); - }, - - - entityURL: function(entity) { - return urlroot + '/' + entity.type + '/' + entity.osmId(); - }, - - - historyURL: function(entity) { - return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history'; - }, - - - userURL: function(username) { - return urlroot + '/user/' + username; - }, - - - noteURL: function(note) { - return urlroot + '/note/' + note.id; - }, - - - noteReportURL: function(note) { - return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id; - }, - - - // Generic method to load data from the OSM API - // Can handle either auth or unauth calls. - loadFromAPI: function(path, callback, options) { - options = Object.assign({ skipSeen: true }, options); - var that = this; - var cid = _connectionID; - - function done(err, payload) { - if (that.getConnectionId() !== cid) { - if (callback) callback({ message: 'Connection Switched', status: -1 }); - return; - } - - var isAuthenticated = that.authenticated(); - - // 400 Bad Request, 401 Unauthorized, 403 Forbidden - // Logout and retry the request.. - if (isAuthenticated && err && err.status && - (err.status === 400 || err.status === 401 || err.status === 403)) { - that.logout(); - that.loadFromAPI(path, callback, options); - - // else, no retry.. - } else { - // 509 Bandwidth Limit Exceeded, 429 Too Many Requests - // Set the rateLimitError flag and trigger a warning.. - if (!isAuthenticated && !_rateLimitError && err && err.status && - (err.status === 509 || err.status === 429)) { - _rateLimitError = err; - dispatch$6.call('change'); - that.reloadApiStatus(); - - } else if ((err && _cachedApiStatus === 'online') || - (!err && _cachedApiStatus !== 'online')) { - // If the response's error state doesn't match the status, - // it's likely we lost or gained the connection so reload the status - that.reloadApiStatus(); - } - - if (callback) { - if (err) { - return callback(err); - } else { - if (path.indexOf('.json') !== -1) { - return parseJSON(payload, callback, options); - } else { - return parseXML(payload, callback, options); - } - } - } - } - } - - if (this.authenticated()) { - return oauth.xhr({ method: 'GET', path: path }, done); - } else { - var url = urlroot + path; - var controller = new AbortController(); - d3_json(url, { signal: controller.signal }) - .then(function(data) { - done(null, data); - }) - .catch(function(err) { - if (err.name === 'AbortError') return; - // d3-fetch includes status in the error message, - // but we can't access the response itself - // https://github.com/d3/d3-fetch/issues/27 - var match = err.message.match(/^\d{3}/); - if (match) { - done({ status: +match[0], statusText: err.message }); - } else { - done(err.message); - } - }); - return controller; - } - }, - - - // Load a single entity by id (ways and relations use the `/full` call) - // GET /api/0.6/node/#id - // GET /api/0.6/[way|relation]/#id/full - loadEntity: function(id, callback) { - var type = osmEntity.id.type(id); - var osmID = osmEntity.id.toOSM(id); - var options = { skipSeen: false }; - - this.loadFromAPI( - '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', - function(err, entities) { - if (callback) callback(err, { data: entities }); - }, - options - ); - }, - - - // Load a single entity with a specific version - // GET /api/0.6/[node|way|relation]/#id/#version - loadEntityVersion: function(id, version, callback) { - var type = osmEntity.id.type(id); - var osmID = osmEntity.id.toOSM(id); - var options = { skipSeen: false }; - - this.loadFromAPI( - '/api/0.6/' + type + '/' + osmID + '/' + version + '.json', - function(err, entities) { - if (callback) callback(err, { data: entities }); - }, - options - ); - }, - - - // Load multiple entities in chunks - // (note: callback may be called multiple times) - // Unlike `loadEntity`, child nodes and members are not fetched - // GET /api/0.6/[nodes|ways|relations]?#parameters - loadMultiple: function(ids, callback) { - var that = this; - var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type); - - Object.keys(groups).forEach(function(k) { - var type = k + 's'; // nodes, ways, relations - var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); }); - var options = { skipSeen: false }; - - utilArrayChunk(osmIDs, 150).forEach(function(arr) { - that.loadFromAPI( - '/api/0.6/' + type + '.json?' + type + '=' + arr.join(), - function(err, entities) { - if (callback) callback(err, { data: entities }); - }, - options - ); - }); - }); - }, - - - // Create, upload, and close a changeset - // PUT /api/0.6/changeset/create - // POST /api/0.6/changeset/#id/upload - // PUT /api/0.6/changeset/#id/close - putChangeset: function(changeset, changes, callback) { - var cid = _connectionID; - - if (_changeset.inflight) { - return callback({ message: 'Changeset already inflight', status: -2 }, changeset); - - } else if (_changeset.open) { // reuse existing open changeset.. - return createdChangeset.call(this, null, _changeset.open); - - } else { // Open a new changeset.. - var options = { - method: 'PUT', - path: '/api/0.6/changeset/create', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(changeset.asJXON()) - }; - _changeset.inflight = oauth.xhr( - options, - wrapcb(this, createdChangeset, cid) - ); - } - - - function createdChangeset(err, changesetID) { - _changeset.inflight = null; - if (err) { return callback(err, changeset); } - - _changeset.open = changesetID; - changeset = changeset.update({ id: changesetID }); - - // Upload the changeset.. - var options = { - method: 'POST', - path: '/api/0.6/changeset/' + changesetID + '/upload', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(changeset.osmChangeJXON(changes)) - }; - _changeset.inflight = oauth.xhr( - options, - wrapcb(this, uploadedChangeset, cid) - ); - } - - - function uploadedChangeset(err) { - _changeset.inflight = null; - if (err) return callback(err, changeset); - - // Upload was successful, safe to call the callback. - // Add delay to allow for postgres replication #1646 #2678 - window.setTimeout(function() { callback(null, changeset); }, 2500); - _changeset.open = null; - - // At this point, we don't really care if the connection was switched.. - // Only try to close the changeset if we're still talking to the same server. - if (this.getConnectionId() === cid) { - // Still attempt to close changeset, but ignore response because #2667 - oauth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changeset.id + '/close', - options: { header: { 'Content-Type': 'text/xml' } } - }, function() { return true; }); - } - } - }, - - - // Load multiple users in chunks - // (note: callback may be called multiple times) - // GET /api/0.6/users?users=#id1,#id2,...,#idn - loadUsers: function(uids, callback) { - var toLoad = []; - var cached = []; - - utilArrayUniq(uids).forEach(function(uid) { - if (_userCache.user[uid]) { - delete _userCache.toLoad[uid]; - cached.push(_userCache.user[uid]); - } else { - toLoad.push(uid); - } - }); - - if (cached.length || !this.authenticated()) { - callback(undefined, cached); - if (!this.authenticated()) return; // require auth - } - - utilArrayChunk(toLoad, 150).forEach(function(arr) { - oauth.xhr( - { method: 'GET', path: '/api/0.6/users?users=' + arr.join() }, - wrapcb(this, done, _connectionID) - ); - }.bind(this)); - - function done(err, xml) { - if (err) { return callback(err); } - - var options = { skipSeen: true }; - return parseXML(xml, function(err, results) { - if (err) { - return callback(err); - } else { - return callback(undefined, results); - } - }, options); - } - }, - - - // Load a given user by id - // GET /api/0.6/user/#id - loadUser: function(uid, callback) { - if (_userCache.user[uid] || !this.authenticated()) { // require auth - delete _userCache.toLoad[uid]; - return callback(undefined, _userCache.user[uid]); - } - - oauth.xhr( - { method: 'GET', path: '/api/0.6/user/' + uid }, - wrapcb(this, done, _connectionID) - ); - - function done(err, xml) { - if (err) { return callback(err); } - - var options = { skipSeen: true }; - return parseXML(xml, function(err, results) { - if (err) { - return callback(err); - } else { - return callback(undefined, results[0]); - } - }, options); - } - }, - - - // Load the details of the logged-in user - // GET /api/0.6/user/details - userDetails: function(callback) { - if (_userDetails) { // retrieve cached - return callback(undefined, _userDetails); - } - - oauth.xhr( - { method: 'GET', path: '/api/0.6/user/details' }, - wrapcb(this, done, _connectionID) - ); - - function done(err, xml) { - if (err) { return callback(err); } - - var options = { skipSeen: false }; - return parseXML(xml, function(err, results) { - if (err) { - return callback(err); - } else { - _userDetails = results[0]; - return callback(undefined, _userDetails); - } - }, options); - } - }, - - - // Load previous changesets for the logged in user - // GET /api/0.6/changesets?user=#id - userChangesets: function(callback) { - if (_userChangesets) { // retrieve cached - return callback(undefined, _userChangesets); - } - - this.userDetails( - wrapcb(this, gotDetails, _connectionID) - ); - - - function gotDetails(err, user) { - if (err) { return callback(err); } - - oauth.xhr( - { method: 'GET', path: '/api/0.6/changesets?user=' + user.id }, - wrapcb(this, done, _connectionID) - ); - } - - function done(err, xml) { - if (err) { return callback(err); } - - _userChangesets = Array.prototype.map.call( - xml.getElementsByTagName('changeset'), - function (changeset) { return { tags: getTags(changeset) }; } - ).filter(function (changeset) { - var comment = changeset.tags.comment; - return comment && comment !== ''; - }); - - return callback(undefined, _userChangesets); - } - }, - - - // Fetch the status of the OSM API - // GET /api/capabilities - status: function(callback) { - var url = urlroot + '/api/capabilities'; - var errback = wrapcb(this, done, _connectionID); - d3_xml(url) - .then(function(data) { errback(null, data); }) - .catch(function(err) { errback(err.message); }); - - function done(err, xml) { - if (err) { - // the status is null if no response could be retrieved - return callback(err, null); - } - - // update blacklists - var elements = xml.getElementsByTagName('blacklist'); - var regexes = []; - for (var i = 0; i < elements.length; i++) { - var regex = elements[i].getAttribute('regex'); // needs unencode? - if (regex) { - regexes.push(regex); - } - } - if (regexes.length) { - _blacklists = regexes; - } - - if (_rateLimitError) { - return callback(_rateLimitError, 'rateLimited'); - } else { - var waynodes = xml.getElementsByTagName('waynodes'); - var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10); - if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes; - - var apiStatus = xml.getElementsByTagName('status'); - var val = apiStatus[0].getAttribute('api'); - return callback(undefined, val); - } - } - }, - - // Calls `status` and dispatches an `apiStatusChange` event if the returned - // status differs from the cached status. - reloadApiStatus: function() { - // throttle to avoid unncessary API calls - if (!this.throttledReloadApiStatus) { - var that = this; - this.throttledReloadApiStatus = throttle(function() { - that.status(function(err, status) { - if (status !== _cachedApiStatus) { - _cachedApiStatus = status; - dispatch$6.call('apiStatusChange', that, err, status); - } - }); - }, 500); - } - this.throttledReloadApiStatus(); - }, - - - // Returns the maximum number of nodes a single way can have - maxWayNodes: function() { - return _maxWayNodes; - }, - - - // Load data (entities) from the API in tiles - // GET /api/0.6/map?bbox= - loadTiles: function(projection, callback) { - if (_off) return; - - // determine the needed tiles to cover the view - var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); - - // abort inflight requests that are no longer needed - var hadRequests = hasInflightRequests(_tileCache); - abortUnwantedRequests$3(_tileCache, tiles); - if (hadRequests && !hasInflightRequests(_tileCache)) { - dispatch$6.call('loaded'); // stop the spinner - } - - // issue new requests.. - tiles.forEach(function(tile) { - this.loadTile(tile, callback); - }, this); - }, - - - // Load a single data tile - // GET /api/0.6/map?bbox= - loadTile: function(tile, callback) { - if (_off) return; - if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return; - - if (!hasInflightRequests(_tileCache)) { - dispatch$6.call('loading'); // start the spinner - } - - var path = '/api/0.6/map.json?bbox='; - var options = { skipSeen: true }; - - _tileCache.inflight[tile.id] = this.loadFromAPI( - path + tile.extent.toParam(), - tileCallback, - options - ); - - function tileCallback(err, parsed) { - delete _tileCache.inflight[tile.id]; - if (!err) { - delete _tileCache.toLoad[tile.id]; - _tileCache.loaded[tile.id] = true; - var bbox = tile.extent.bbox(); - bbox.id = tile.id; - _tileCache.rtree.insert(bbox); - } - if (callback) { - callback(err, Object.assign({ data: parsed }, tile)); - } - if (!hasInflightRequests(_tileCache)) { - dispatch$6.call('loaded'); // stop the spinner - } - } - }, - - - isDataLoaded: function(loc) { - var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }; - return _tileCache.rtree.collides(bbox); - }, - - - // load the tile that covers the given `loc` - loadTileAtLoc: function(loc, callback) { - // Back off if the toLoad queue is filling up.. re #6417 - // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to - // let users safely edit geometries which extend to unloaded tiles. We can drop some.) - if (Object.keys(_tileCache.toLoad).length > 50) return; - - var k = geoZoomToScale(_tileZoom$3 + 1); - var offset = geoRawMercator().scale(k)(loc); - var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] }); - var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); - - tiles.forEach(function(tile) { - if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return; - - _tileCache.toLoad[tile.id] = true; - this.loadTile(tile, callback); - }, this); - }, - - - // Load notes from the API in tiles - // GET /api/0.6/notes?bbox= - loadNotes: function(projection, noteOptions) { - noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions); - if (_off) return; - - var that = this; - var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox='; - var throttleLoadUsers = throttle(function() { - var uids = Object.keys(_userCache.toLoad); - if (!uids.length) return; - that.loadUsers(uids, function() {}); // eagerly load user details - }, 750); - - // determine the needed tiles to cover the view - var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); - - // abort inflight requests that are no longer needed - abortUnwantedRequests$3(_noteCache, tiles); - - // issue new requests.. - tiles.forEach(function(tile) { - if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return; - - var options = { skipSeen: false }; - _noteCache.inflight[tile.id] = that.loadFromAPI( - path + tile.extent.toParam(), - function(err) { - delete _noteCache.inflight[tile.id]; - if (!err) { - _noteCache.loaded[tile.id] = true; - } - throttleLoadUsers(); - dispatch$6.call('loadedNotes'); - }, - options - ); - }); - }, - - - // Create a note - // POST /api/0.6/notes?params - postNoteCreate: function(note, callback) { - if (!this.authenticated()) { - return callback({ message: 'Not Authenticated', status: -3 }, note); - } - if (_noteCache.inflightPost[note.id]) { - return callback({ message: 'Note update already inflight', status: -2 }, note); - } - - if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required - - var comment = note.newComment; - if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; } - - var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment }); - - _noteCache.inflightPost[note.id] = oauth.xhr( - { method: 'POST', path: path }, - wrapcb(this, done, _connectionID) - ); - - - function done(err, xml) { - delete _noteCache.inflightPost[note.id]; - if (err) { return callback(err); } - - // we get the updated note back, remove from caches and reparse.. - this.removeNote(note); - - var options = { skipSeen: false }; - return parseXML(xml, function(err, results) { - if (err) { - return callback(err); - } else { - return callback(undefined, results[0]); - } - }, options); - } - }, - - - // Update a note - // POST /api/0.6/notes/#id/comment?text=comment - // POST /api/0.6/notes/#id/close?text=comment - // POST /api/0.6/notes/#id/reopen?text=comment - postNoteUpdate: function(note, newStatus, callback) { - if (!this.authenticated()) { - return callback({ message: 'Not Authenticated', status: -3 }, note); - } - if (_noteCache.inflightPost[note.id]) { - return callback({ message: 'Note update already inflight', status: -2 }, note); - } - - var action; - if (note.status !== 'closed' && newStatus === 'closed') { - action = 'close'; - } else if (note.status !== 'open' && newStatus === 'open') { - action = 'reopen'; - } else { - action = 'comment'; - if (!note.newComment) return; // when commenting, comment required - } - - var path = '/api/0.6/notes/' + note.id + '/' + action; - if (note.newComment) { - path += '?' + utilQsString({ text: note.newComment }); - } - - _noteCache.inflightPost[note.id] = oauth.xhr( - { method: 'POST', path: path }, - wrapcb(this, done, _connectionID) - ); - - - function done(err, xml) { - delete _noteCache.inflightPost[note.id]; - if (err) { return callback(err); } - - // we get the updated note back, remove from caches and reparse.. - this.removeNote(note); - - // update closed note cache - used to populate `closed:note` changeset tag - if (action === 'close') { - _noteCache.closed[note.id] = true; - } else if (action === 'reopen') { - delete _noteCache.closed[note.id]; - } - - var options = { skipSeen: false }; - return parseXML(xml, function(err, results) { - if (err) { - return callback(err); - } else { - return callback(undefined, results[0]); - } - }, options); - } - }, - - - switch: function(options) { - urlroot = options.urlroot; - - oauth.options(Object.assign({ - url: urlroot, - loading: authLoading, - done: authDone - }, options)); - - this.reset(); - this.userChangesets(function() {}); // eagerly load user details/changesets - dispatch$6.call('change'); - return this; - }, - - - toggle: function(val) { - _off = !val; - return this; - }, - - - isChangesetInflight: function() { - return !!_changeset.inflight; - }, - - - // get/set cached data - // This is used to save/restore the state when entering/exiting the walkthrough - // Also used for testing purposes. - caches: function(obj) { - function cloneCache(source) { - var target = {}; - Object.keys(source).forEach(function(k) { - if (k === 'rtree') { - target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush - } else if (k === 'note') { - target.note = {}; - Object.keys(source.note).forEach(function(id) { - target.note[id] = osmNote(source.note[id]); // copy notes - }); - } else { - target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep - } - }); - return target; - } - - if (!arguments.length) { - return { - tile: cloneCache(_tileCache), - note: cloneCache(_noteCache), - user: cloneCache(_userCache) - }; - } - - // access caches directly for testing (e.g., loading notes rtree) - if (obj === 'get') { - return { - tile: _tileCache, - note: _noteCache, - user: _userCache - }; - } - - if (obj.tile) { - _tileCache = obj.tile; - _tileCache.inflight = {}; - } - if (obj.note) { - _noteCache = obj.note; - _noteCache.inflight = {}; - _noteCache.inflightPost = {}; - } - if (obj.user) { - _userCache = obj.user; - } - - return this; - }, - - - logout: function() { - _userChangesets = undefined; - _userDetails = undefined; - oauth.logout(); - dispatch$6.call('change'); - return this; - }, - - - authenticated: function() { - return oauth.authenticated(); - }, - - - authenticate: function(callback) { - var that = this; - var cid = _connectionID; - _userChangesets = undefined; - _userDetails = undefined; - - function done(err, res) { - if (err) { - if (callback) callback(err); - return; - } - if (that.getConnectionId() !== cid) { - if (callback) callback({ message: 'Connection Switched', status: -1 }); - return; - } - _rateLimitError = undefined; - dispatch$6.call('change'); - if (callback) callback(err, res); - that.userChangesets(function() {}); // eagerly load user details/changesets - } - - return oauth.authenticate(done); - }, - - - imageryBlacklists: function() { - return _blacklists; - }, - - - tileZoom: function(val) { - if (!arguments.length) return _tileZoom$3; - _tileZoom$3 = val; - return this; - }, - - - // get all cached notes covering the viewport - notes: function(projection) { - var viewport = projection.clipExtent(); - var min = [viewport[0][0], viewport[1][1]]; - var max = [viewport[1][0], viewport[0][1]]; - var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - - return _noteCache.rtree.search(bbox) - .map(function(d) { return d.data; }); - }, - - - // get a single note from the cache - getNote: function(id) { - return _noteCache.note[id]; - }, - - - // remove a single note from the cache - removeNote: function(note) { - if (!(note instanceof osmNote) || !note.id) return; - - delete _noteCache.note[note.id]; - updateRtree$3(encodeNoteRtree(note), false); // false = remove - }, - - - // replace a single note in the cache - replaceNote: function(note) { - if (!(note instanceof osmNote) || !note.id) return; - - _noteCache.note[note.id] = note; - updateRtree$3(encodeNoteRtree(note), true); // true = replace - return note; - }, - - - // Get an array of note IDs closed during this session. - // Used to populate `closed:note` changeset tag - getClosedIDs: function() { - return Object.keys(_noteCache.closed).sort(); - } - - }; - - var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php'; - var _inflight$1 = {}; - var _wikibaseCache = {}; - var _localeIDs = { en: false }; - - - var debouncedRequest = debounce(request, 500, { leading: false }); - - function request(url, callback) { - if (_inflight$1[url]) return; - var controller = new AbortController(); - _inflight$1[url] = controller; - - d3_json(url, { signal: controller.signal }) - .then(function(result) { - delete _inflight$1[url]; - if (callback) callback(null, result); - }) - .catch(function(err) { - delete _inflight$1[url]; - if (err.name === 'AbortError') return; - if (callback) callback(err.message); - }); - } - - - /** - * Get the best string value from the descriptions/labels result - * Note that if mediawiki doesn't recognize language code, it will return all values. - * In that case, fallback to use English. - * @param values object - either descriptions or labels - * @param langCode String - * @returns localized string - */ - function localizedToString(values, langCode) { - if (values) { - values = values[langCode] || values.en; - } - return values ? values.value : ''; - } - - - var serviceOsmWikibase = { - - init: function() { - _inflight$1 = {}; - _wikibaseCache = {}; - _localeIDs = {}; - }, - - - reset: function() { - Object.values(_inflight$1).forEach(function(controller) { controller.abort(); }); - _inflight$1 = {}; - }, - - - /** - * Get the best value for the property, or undefined if not found - * @param entity object from wikibase - * @param property string e.g. 'P4' for image - * @param langCode string e.g. 'fr' for French - */ - claimToValue: function(entity, property, langCode) { - if (!entity.claims[property]) return undefined; - var locale = _localeIDs[langCode]; - var preferredPick, localePick; - - entity.claims[property].forEach(function(stmt) { - // If exists, use value limited to the needed language (has a qualifier P26 = locale) - // Or if not found, use the first value with the "preferred" rank - if (!preferredPick && stmt.rank === 'preferred') { - preferredPick = stmt; - } - if (locale && stmt.qualifiers && stmt.qualifiers.P26 && - stmt.qualifiers.P26[0].datavalue.value.id === locale - ) { - localePick = stmt; - } - }); - - var result = localePick || preferredPick; - if (result) { - var datavalue = result.mainsnak.datavalue; - return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value; - } else { - return undefined; - } - }, - - - /** - * Convert monolingual property into a key-value object (language -> value) - * @param entity object from wikibase - * @param property string e.g. 'P31' for monolingual wiki page title - */ - monolingualClaimToValueObj: function(entity, property) { - if (!entity || !entity.claims[property]) return undefined; - - return entity.claims[property].reduce(function(acc, obj) { - var value = obj.mainsnak.datavalue.value; - acc[value.language] = value.text; - return acc; - }, {}); - }, - - - toSitelink: function(key, value) { - var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key; - return result.replace(/_/g, ' ').trim(); - }, - - - // - // Pass params object of the form: - // { - // key: 'string', - // value: 'string', - // rtype: 'string', - // langCode: 'string' - // } - // - getEntity: function(params, callback) { - var doRequest = params.debounce ? debouncedRequest : request; - var that = this; - var titles = []; - var result = {}; - var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false; - var keySitelink = params.key ? this.toSitelink(params.key) : false; - var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false; - var localeSitelink; - - if (params.langCode && _localeIDs[params.langCode] === undefined) { - // If this is the first time we are asking about this locale, - // fetch corresponding entity (if it exists), and cache it. - // If there is no such entry, cache `false` value to avoid re-requesting it. - localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim(); - titles.push(localeSitelink); - } - - if (rtypeSitelink) { - if (_wikibaseCache[rtypeSitelink]) { - result.rtype = _wikibaseCache[rtypeSitelink]; - } else { - titles.push(rtypeSitelink); - } - } - - if (keySitelink) { - if (_wikibaseCache[keySitelink]) { - result.key = _wikibaseCache[keySitelink]; - } else { - titles.push(keySitelink); - } - } - - if (tagSitelink) { - if (_wikibaseCache[tagSitelink]) { - result.tag = _wikibaseCache[tagSitelink]; - } else { - titles.push(tagSitelink); - } - } - - if (!titles.length) { - // Nothing to do, we already had everything in the cache - return callback(null, result); - } - - // Requesting just the user language code - // If backend recognizes the code, it will perform proper fallbacks, - // and the result will contain the requested code. If not, all values are returned: - // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"} - // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}} - var obj = { - action: 'wbgetentities', - sites: 'wiki', - titles: titles.join('|'), - languages: params.langCode, - languagefallback: 1, - origin: '*', - format: 'json', - // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069 - // We shouldn't use v1 until it gets fixed, but should switch to it afterwards - // formatversion: 2, - }; - - var url = apibase$3 + '?' + utilQsString(obj); - doRequest(url, function(err, d) { - if (err) { - callback(err); - } else if (!d.success || d.error) { - callback(d.error.messages.map(function(v) { return v.html['*']; }).join('
    ')); - } else { - var localeID = false; - Object.values(d.entities).forEach(function(res) { - if (res.missing !== '') { - // Simplify access to the localized values - res.description = localizedToString(res.descriptions, params.langCode); - res.label = localizedToString(res.labels, params.langCode); - - var title = res.sitelinks.wiki.title; - if (title === rtypeSitelink) { - _wikibaseCache[rtypeSitelink] = res; - result.rtype = res; - } else if (title === keySitelink) { - _wikibaseCache[keySitelink] = res; - result.key = res; - } else if (title === tagSitelink) { - _wikibaseCache[tagSitelink] = res; - result.tag = res; - } else if (title === localeSitelink) { - localeID = res.id; - } else { - console.log('Unexpected title ' + title); // eslint-disable-line no-console - } - } - }); - - if (localeSitelink) { - // If locale ID is not found, store false to prevent repeated queries - that.addLocale(params.langCode, localeID); - } - - callback(null, result); - } - }); - }, - - - // - // Pass params object of the form: - // { - // key: 'string', // required - // value: 'string' // optional - // } - // -or- - // { - // rtype: 'rtype' // relation type (e.g. 'multipolygon') - // } - // - // Get an result object used to display tag documentation - // { - // title: 'string', - // description: 'string', - // editURL: 'string', - // imageURL: 'string', - // wiki: { title: 'string', text: 'string', url: 'string' } - // } - // - getDocs: function(params, callback) { - var that = this; - var langCode = _mainLocalizer.localeCode().toLowerCase(); - params.langCode = langCode; - - this.getEntity(params, function(err, data) { - if (err) { - callback(err); - return; - } - - var entity = data.rtype || data.tag || data.key; - if (!entity) { - callback('No entity'); - return; - } - - // prepare result - var result = { - title: entity.title, - description: entity.description, - editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title - }; - - // add image - if (entity.claims) { - var imageroot; - var image = that.claimToValue(entity, 'P4', langCode); - if (image) { - imageroot = 'https://commons.wikimedia.org/w/index.php'; - } else { - image = that.claimToValue(entity, 'P28', langCode); - if (image) { - imageroot = 'https://wiki.openstreetmap.org/w/index.php'; - } - } - if (imageroot && image) { - result.imageURL = imageroot + '?' + utilQsString({ - title: 'Special:Redirect/file/' + image, - width: 400 - }); - } - } - - // Try to get a wiki page from tag data item first, followed by the corresponding key data item. - // If neither tag nor key data item contain a wiki page in the needed language nor English, - // get the first found wiki page from either the tag or the key item. - var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31'); - var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31'); - var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31'); - - // If exact language code does not exist, try to find the first part before the '-' - // BUG: in some cases, a more elaborate fallback logic might be needed - var langPrefix = langCode.split('-', 2)[0]; - - // use the first acceptable wiki page - result.wiki = - getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') || - getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') || - getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') || - getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') || - getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') || - getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') || - getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') || - getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') || - getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference'); - - callback(null, result); - - - // Helper method to get wiki info if a given language exists - function getWikiInfo(wiki, langCode, tKey) { - if (wiki && wiki[langCode]) { - return { - title: wiki[langCode], - text: tKey, - url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode] - }; - } - } - }); - }, - - - addLocale: function(langCode, qid) { - // Makes it easier to unit test - _localeIDs[langCode] = qid; - }, - - - apibase: function(val) { - if (!arguments.length) return apibase$3; - apibase$3 = val; - return this; - } - - }; - - var jsonpCache = {}; - window.jsonpCache = jsonpCache; - - function jsonpRequest(url, callback) { - var request = { - abort: function() {} - }; - - if (window.JSONP_FIX) { - if (window.JSONP_DELAY === 0) { - callback(window.JSONP_FIX); - } else { - var t = window.setTimeout(function() { - callback(window.JSONP_FIX); - }, window.JSONP_DELAY || 0); - - request.abort = function() { window.clearTimeout(t); }; - } - - return request; - } - - function rand() { - var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - var c = ''; - var i = -1; - while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52)); - return c; - } - - function create(url) { - var e = url.match(/callback=(\w+)/); - var c = e ? e[1] : rand(); - - jsonpCache[c] = function(data) { - if (jsonpCache[c]) { - callback(data); - } - finalize(); - }; - - function finalize() { - delete jsonpCache[c]; - script.remove(); - } - - request.abort = finalize; - return 'jsonpCache.' + c; - } - - var cb = create(url); - - var script = select('head') - .append('script') - .attr('type', 'text/javascript') - .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb)); - - return request; - } - - const bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?'; - const streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/'; - const bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm'; - const pannellumViewerCSS = 'pannellum-streetside/pannellum.css'; - const pannellumViewerJS = 'pannellum-streetside/pannellum.js'; - const maxResults$2 = 2000; - const tileZoom$2 = 16.5; - const tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true); - const dispatch$7 = dispatch('loadedBubbles', 'viewerChanged'); - const minHfov = 10; // zoom in degrees: 20, 10, 5 - const maxHfov = 90; // zoom out degrees - const defaultHfov = 45; - - let _hires = false; - let _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096 - let _currScene = 0; - let _ssCache; - let _pannellumViewer; - let _sceneOptions; - let _dataUrlArray = []; - - - /** - * abortRequest(). - */ - function abortRequest$6(i) { - i.abort(); - } - - - /** - * localeTimeStamp(). - */ - function localeTimestamp(s) { - if (!s) return null; - const options = { day: 'numeric', month: 'short', year: 'numeric' }; - const d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleString(_mainLocalizer.localeCode(), options); - } - - - /** - * loadTiles() wraps the process of generating tiles and then fetching image points for each tile. - */ - function loadTiles$2(which, url, projection, margin) { - const tiles = tiler$6.margin(margin).getTiles(projection); - - // abort inflight requests that are no longer needed - const cache = _ssCache[which]; - Object.keys(cache.inflight).forEach(k => { - const wanted = tiles.find(tile => k.indexOf(tile.id + ',') === 0); - if (!wanted) { - abortRequest$6(cache.inflight[k]); - delete cache.inflight[k]; - } - }); - - tiles.forEach(tile => loadNextTilePage$2(which, url, tile)); - } - - - /** - * loadNextTilePage() load data for the next tile page in line. - */ - function loadNextTilePage$2(which, url, tile) { - const cache = _ssCache[which]; - const nextPage = cache.nextPage[tile.id] || 0; - const id = tile.id + ',' + String(nextPage); - if (cache.loaded[id] || cache.inflight[id]) return; - - cache.inflight[id] = getBubbles(url, tile, (bubbles) => { - cache.loaded[id] = true; - delete cache.inflight[id]; - if (!bubbles) return; - - // [].shift() removes the first element, some statistics info, not a bubble point - bubbles.shift(); - - const features = bubbles.map(bubble => { - if (cache.points[bubble.id]) return null; // skip duplicates - - const loc = [bubble.lo, bubble.la]; - const d = { - loc: loc, - key: bubble.id, - ca: bubble.he, - captured_at: bubble.cd, - captured_by: 'microsoft', - // nbn: bubble.nbn, - // pbn: bubble.pbn, - // ad: bubble.ad, - // rn: bubble.rn, - pr: bubble.pr, // previous - ne: bubble.ne, // next - pano: true, - sequenceKey: null - }; - - cache.points[bubble.id] = d; - - // a sequence starts here - if (bubble.pr === undefined) { - cache.leaders.push(bubble.id); - } - - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; - - }).filter(Boolean); - - cache.rtree.load(features); - - connectSequences(); - - if (which === 'bubbles') { - dispatch$7.call('loadedBubbles'); - } - }); - } - - - // call this sometimes to connect the bubbles into sequences - function connectSequences() { - let cache = _ssCache.bubbles; - let keepLeaders = []; - - for (let i = 0; i < cache.leaders.length; i++) { - let bubble = cache.points[cache.leaders[i]]; - let seen = {}; - - // try to make a sequence.. use the key of the leader bubble. - let sequence = { key: bubble.key, bubbles: [] }; - let complete = false; - - do { - sequence.bubbles.push(bubble); - seen[bubble.key] = true; - - if (bubble.ne === undefined) { - complete = true; - } else { - bubble = cache.points[bubble.ne]; // advance to next - } - } while (bubble && !seen[bubble.key] && !complete); - - - if (complete) { - _ssCache.sequences[sequence.key] = sequence; - - // assign bubbles to the sequence - for (let j = 0; j < sequence.bubbles.length; j++) { - sequence.bubbles[j].sequenceKey = sequence.key; - } - - // create a GeoJSON LineString - sequence.geojson = { - type: 'LineString', - properties: { key: sequence.key }, - coordinates: sequence.bubbles.map(d => d.loc) - }; - - } else { - keepLeaders.push(cache.leaders[i]); - } - } - - // couldn't complete these, save for later - cache.leaders = keepLeaders; - } - - - /** - * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations). - */ - function getBubbles(url, tile, callback) { - let rect = tile.extent.rectangle(); - let urlForRequest = url + utilQsString({ - n: rect[3], - s: rect[1], - e: rect[2], - w: rect[0], - c: maxResults$2, - appkey: bubbleAppKey, - jsCallback: '{callback}' - }); - - return jsonpRequest(urlForRequest, (data) => { - if (!data || data.error) { - callback(null); - } else { - callback(data); - } - }); - } - - - // partition viewport into higher zoom tiles - function partitionViewport$2(projection) { - let z = geoScaleToZoom(projection.scale()); - let z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 - let tiler = utilTiler().zoomExtent([z2, z2]); - - return tiler.getTiles(projection) - .map(tile => tile.extent); - } - - - // no more than `limit` results per partition. - function searchLimited$2(limit, projection, rtree) { - limit = limit || 5; - - return partitionViewport$2(projection) - .reduce((result, extent) => { - let found = rtree.search(extent.bbox()) - .slice(0, limit) - .map(d => d.data); - - return (found.length ? result.concat(found) : result); - }, []); - } - - - /** - * loadImage() - */ - function loadImage(imgInfo) { - return new Promise(resolve => { - let img = new Image(); - img.onload = () => { - let canvas = document.getElementById('ideditor-canvas' + imgInfo.face); - let ctx = canvas.getContext('2d'); - ctx.drawImage(img, imgInfo.x, imgInfo.y); - resolve({ imgInfo: imgInfo, status: 'ok' }); - }; - img.onerror = () => { - resolve({ data: imgInfo, status: 'error' }); - }; - img.setAttribute('crossorigin', ''); - img.src = imgInfo.url; - }); - } - - - /** - * loadCanvas() - */ - function loadCanvas(imageGroup) { - return Promise.all(imageGroup.map(loadImage)) - .then((data) => { - let canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face); - const which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 }; - let face = data[0].imgInfo.face; - _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0); - return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'}; - }); - } - - - /** - * loadFaces() - */ - function loadFaces(faceGroup) { - return Promise.all(faceGroup.map(loadCanvas)) - .then(() => { return { status: 'loadFaces done' }; }); - } - - - function setupCanvas(selection, reset) { - if (reset) { - selection.selectAll('#ideditor-stitcher-canvases') - .remove(); - } - - // Add the Streetside working canvases. These are used for 'stitching', or combining, - // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls - selection.selectAll('#ideditor-stitcher-canvases') - .data([0]) - .enter() - .append('div') - .attr('id', 'ideditor-stitcher-canvases') - .attr('display', 'none') - .selectAll('canvas') - .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12']) - .enter() - .append('canvas') - .attr('id', d => 'ideditor-' + d) - .attr('width', _resolution) - .attr('height', _resolution); - } - - - function qkToXY(qk) { - let x = 0; - let y = 0; - let scale = 256; - for (let i = qk.length; i > 0; i--) { - const key = qk[i-1]; - x += (+(key === '1' || key === '3')) * scale; - y += (+(key === '2' || key === '3')) * scale; - scale *= 2; - } - return [x, y]; - } - - - function getQuadKeys() { - let dim = _resolution / 256; - let quadKeys; - - if (dim === 16) { - quadKeys = [ - '0000','0001','0010','0011','0100','0101','0110','0111', '1000','1001','1010','1011','1100','1101','1110','1111', - '0002','0003','0012','0013','0102','0103','0112','0113', '1002','1003','1012','1013','1102','1103','1112','1113', - '0020','0021','0030','0031','0120','0121','0130','0131', '1020','1021','1030','1031','1120','1121','1130','1131', - '0022','0023','0032','0033','0122','0123','0132','0133', '1022','1023','1032','1033','1122','1123','1132','1133', - '0200','0201','0210','0211','0300','0301','0310','0311', '1200','1201','1210','1211','1300','1301','1310','1311', - '0202','0203','0212','0213','0302','0303','0312','0313', '1202','1203','1212','1213','1302','1303','1312','1313', - '0220','0221','0230','0231','0320','0321','0330','0331', '1220','1221','1230','1231','1320','1321','1330','1331', - '0222','0223','0232','0233','0322','0323','0332','0333', '1222','1223','1232','1233','1322','1323','1332','1333', - - '2000','2001','2010','2011','2100','2101','2110','2111', '3000','3001','3010','3011','3100','3101','3110','3111', - '2002','2003','2012','2013','2102','2103','2112','2113', '3002','3003','3012','3013','3102','3103','3112','3113', - '2020','2021','2030','2031','2120','2121','2130','2131', '3020','3021','3030','3031','3120','3121','3130','3131', - '2022','2023','2032','2033','2122','2123','2132','2133', '3022','3023','3032','3033','3122','3123','3132','3133', - '2200','2201','2210','2211','2300','2301','2310','2311', '3200','3201','3210','3211','3300','3301','3310','3311', - '2202','2203','2212','2213','2302','2303','2312','2313', '3202','3203','3212','3213','3302','3303','3312','3313', - '2220','2221','2230','2231','2320','2321','2330','2331', '3220','3221','3230','3231','3320','3321','3330','3331', - '2222','2223','2232','2233','2322','2323','2332','2333', '3222','3223','3232','3233','3322','3323','3332','3333' - ]; - - } else if (dim === 8) { - quadKeys = [ - '000','001','010','011', '100','101','110','111', - '002','003','012','013', '102','103','112','113', - '020','021','030','031', '120','121','130','131', - '022','023','032','033', '122','123','132','133', - - '200','201','210','211', '300','301','310','311', - '202','203','212','213', '302','303','312','313', - '220','221','230','231', '320','321','330','331', - '222','223','232','233', '322','323','332','333' - ]; - - } else if (dim === 4) { - quadKeys = [ - '00','01', '10','11', - '02','03', '12','13', - - '20','21', '30','31', - '22','23', '32','33' - ]; - - } else { // dim === 2 - quadKeys = [ - '0', '1', - '2', '3' - ]; - } - - return quadKeys; - } - - - - var serviceStreetside = { - /** - * init() initialize streetside. - */ - init: function() { - if (!_ssCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$7, 'on'); - }, - - /** - * reset() reset the cache. - */ - reset: function() { - if (_ssCache) { - Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6); - } - - _ssCache = { - bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] }, - sequences: {} - }; - }, - - /** - * bubbles() - */ - bubbles: function(projection) { - const limit = 5; - return searchLimited$2(limit, projection, _ssCache.bubbles.rtree); - }, - - - sequences: function(projection) { - const viewport = projection.clipExtent(); - const min = [viewport[0][0], viewport[1][1]]; - const max = [viewport[1][0], viewport[0][1]]; - const bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - let seen = {}; - let results = []; - - // all sequences for bubbles in viewport - _ssCache.bubbles.rtree.search(bbox) - .forEach(d => { - const key = d.data.sequenceKey; - if (key && !seen[key]) { - seen[key] = true; - results.push(_ssCache.sequences[key].geojson); - } - }); - - return results; - }, - - - /** - * loadBubbles() - */ - loadBubbles: function(projection, margin) { - // by default: request 2 nearby tiles so we can connect sequences. - if (margin === undefined) margin = 2; - - loadTiles$2('bubbles', bubbleApi, projection, margin); - }, - - - viewer: function() { - return _pannellumViewer; - }, - - - initViewer: function () { - if (!window.pannellum) return; - if (_pannellumViewer) return; - - const sceneID = ++_currScene + ''; - const options = { - 'default': { firstScene: sceneID }, - scenes: {} - }; - options.scenes[sceneID] = _sceneOptions; - - _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options); - }, - - - /** - * loadViewer() create the streeside viewer. - */ - loadViewer: function(context) { - let that = this; - - let pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - // create ms-wrapper, a photo wrapper class - let wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper') - .data([0]); - - // inject ms-wrapper into the photoviewer div - // (used by all to house each custom photo viewer) - let wrapEnter = wrap.enter() - .append('div') - .attr('class', 'photo-wrapper ms-wrapper') - .classed('hide', true); - - // inject div to support streetside viewer (pannellum) and attribution line - wrapEnter - .append('div') - .attr('id', 'ideditor-viewer-streetside') - .on(pointerPrefix + 'down.streetside', () => { - select(window) - .on(pointerPrefix + 'move.streetside', () => { - dispatch$7.call('viewerChanged'); - }, true); - }) - .on(pointerPrefix + 'up.streetside pointercancel.streetside', () => { - select(window) - .on(pointerPrefix + 'move.streetside', null); - - // continue dispatching events for a few seconds, in case viewer has inertia. - let t = timer(elapsed => { - dispatch$7.call('viewerChanged'); - if (elapsed > 2000) { - t.stop(); - } - }); - }) - .append('div') - .attr('class', 'photo-attribution fillD'); - - let controlsEnter = wrapEnter - .append('div') - .attr('class', 'photo-controls-wrap') - .append('div') - .attr('class', 'photo-controls'); - - controlsEnter - .append('button') - .on('click.back', step(-1)) - .text('◄'); - - controlsEnter - .append('button') - .on('click.forward', step(1)) - .text('►'); - - - // create working canvas for stitching together images - wrap = wrap - .merge(wrapEnter) - .call(setupCanvas, true); - - // load streetside pannellum viewer css - select('head').selectAll('#ideditor-streetside-viewercss') - .data([0]) - .enter() - .append('link') - .attr('id', 'ideditor-streetside-viewercss') - .attr('rel', 'stylesheet') - .attr('href', context.asset(pannellumViewerCSS)); - - // load streetside pannellum viewer js - select('head').selectAll('#ideditor-streetside-viewerjs') - .data([0]) - .enter() - .append('script') - .attr('id', 'ideditor-streetside-viewerjs') - .attr('src', context.asset(pannellumViewerJS)); - - - // Register viewer resize handler - context.ui().photoviewer.on('resize.streetside', () => { - if (_pannellumViewer) { - _pannellumViewer.resize(); - } - }); - - - function step(stepBy) { - return () => { - let viewer = context.container().select('.photoviewer'); - let selected = viewer.empty() ? undefined : viewer.datum(); - if (!selected) return; - - let nextID = (stepBy === 1 ? selected.ne : selected.pr); - let yaw = _pannellumViewer.getYaw(); - let ca = selected.ca + yaw; - let origin = selected.loc; - - // construct a search trapezoid pointing out from current bubble - const meters = 35; - let p1 = [ - origin[0] + geoMetersToLon(meters / 5, origin[1]), - origin[1] - ]; - let p2 = [ - origin[0] + geoMetersToLon(meters / 2, origin[1]), - origin[1] + geoMetersToLat(meters) - ]; - let p3 = [ - origin[0] - geoMetersToLon(meters / 2, origin[1]), - origin[1] + geoMetersToLat(meters) - ]; - let p4 = [ - origin[0] - geoMetersToLon(meters / 5, origin[1]), - origin[1] - ]; - - let poly = [p1, p2, p3, p4, p1]; - - // rotate it to face forward/backward - let angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180); - poly = geoRotate(poly, -angle, origin); - - let extent = poly.reduce((extent, point) => { - return extent.extend(geoExtent(point)); - }, geoExtent()); - - // find nearest other bubble in the search polygon - let minDist = Infinity; - _ssCache.bubbles.rtree.search(extent.bbox()) - .forEach(d => { - if (d.data.key === selected.key) return; - if (!geoPointInPolygon(d.data.loc, poly)) return; - - let dist = geoVecLength(d.data.loc, selected.loc); - let theta = selected.ca - d.data.ca; - let minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta)); - if (minTheta > 20) { - dist += 5; // penalize distance if camera angles don't match - } - - if (dist < minDist) { - nextID = d.data.key; - minDist = dist; - } - }); - - let nextBubble = nextID && _ssCache.bubbles.points[nextID]; - if (!nextBubble) return; - - context.map().centerEase(nextBubble.loc); - - that.selectImage(context, nextBubble) - .then(response => { - if (response.status === 'ok') { - _sceneOptions.yaw = yaw; - that.showViewer(context); - } - }); - }; - } - }, - - - /** - * showViewer() - */ - showViewer: function(context, yaw) { - if (!_sceneOptions) return; - - if (yaw !== undefined) { - _sceneOptions.yaw = yaw; - } - - if (!_pannellumViewer) { - this.initViewer(); - } else { - // make a new scene - let sceneID = ++_currScene + ''; - _pannellumViewer - .addScene(sceneID, _sceneOptions) - .loadScene(sceneID); - - // remove previous scene - if (_currScene > 2) { - sceneID = (_currScene - 1) + ''; - _pannellumViewer - .removeScene(sceneID); - } - } - - let wrap = context.container().select('.photoviewer') - .classed('hide', false); - - let isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size(); - - if (isHidden) { - wrap - .selectAll('.photo-wrapper:not(.ms-wrapper)') - .classed('hide', true); - - wrap - .selectAll('.photo-wrapper.ms-wrapper') - .classed('hide', false); - } - - return this; - }, - - - /** - * hideViewer() - */ - hideViewer: function (context) { - let viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(null); - - viewer - .classed('hide', true) - .selectAll('.photo-wrapper') - .classed('hide', true); - - context.container().selectAll('.viewfield-group, .sequence, .icon-sign') - .classed('currentView', false); - - return this.setStyles(context, null, true); - }, - - - /** - * selectImage(). - */ - selectImage: function (context, d) { - let that = this; - let viewer = context.container().select('.photoviewer'); - if (!viewer.empty()) viewer.datum(d); - - this.setStyles(context, null, true); - - let wrap = context.container().select('.photoviewer .ms-wrapper'); - let attribution = wrap.selectAll('.photo-attribution').html(''); - - wrap.selectAll('.pnlm-load-box') // display "loading.." - .style('display', 'block'); - - if (!d) { - return Promise.resolve({ status: 'ok' }); - } - - let line1 = attribution - .append('div') - .attr('class', 'attribution-row'); - - const hiresDomId = utilUniqueDomId('streetside-hires'); - - // Add hires checkbox - let label = line1 - .append('label') - .attr('for', hiresDomId) - .attr('class', 'streetside-hires'); - - label - .append('input') - .attr('type', 'checkbox') - .attr('id', hiresDomId) - .property('checked', _hires) - .on('click', () => { - event.stopPropagation(); - - _hires = !_hires; - _resolution = _hires ? 1024 : 512; - wrap.call(setupCanvas, true); - - let viewstate = { - yaw: _pannellumViewer.getYaw(), - pitch: _pannellumViewer.getPitch(), - hfov: _pannellumViewer.getHfov() - }; - - that.selectImage(context, d) - .then(response => { - if (response.status === 'ok') { - _sceneOptions = Object.assign(_sceneOptions, viewstate); - that.showViewer(context); - } - }); - }); - - label - .append('span') - .text(_t('streetside.hires')); - - - let captureInfo = line1 - .append('div') - .attr('class', 'attribution-capture-info'); - - // Add capture date - if (d.captured_by) { - const yyyy = (new Date()).getFullYear(); - - captureInfo - .append('a') - .attr('class', 'captured_by') - .attr('target', '_blank') - .attr('href', 'https://www.microsoft.com/en-us/maps/streetside') - .text('©' + yyyy + ' Microsoft'); - - captureInfo - .append('span') - .text('|'); - } - - if (d.captured_at) { - captureInfo - .append('span') - .attr('class', 'captured_at') - .text(localeTimestamp(d.captured_at)); - } - - // Add image links - let line2 = attribution - .append('div') - .attr('class', 'attribution-row'); - - line2 - .append('a') - .attr('class', 'image-view-link') - .attr('target', '_blank') - .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] + - '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1') - .text(_t('streetside.view_on_bing')); - - line2 - .append('a') - .attr('class', 'image-report-link') - .attr('target', '_blank') - .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' + - encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17') - .text(_t('streetside.report')); - - - let bubbleIdQuadKey = d.key.toString(4); - const paddingNeeded = 16 - bubbleIdQuadKey.length; - for (let i = 0; i < paddingNeeded; i++) { - bubbleIdQuadKey = '0' + bubbleIdQuadKey; - } - const imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey; - const imgUrlSuffix = '.jpg?g=6338&n=z'; - - // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12 - const faceKeys = ['01','02','03','10','11','12']; - - // Map images to cube faces - let quadKeys = getQuadKeys(); - let faces = faceKeys.map((faceKey) => { - return quadKeys.map((quadKey) =>{ - const xy = qkToXY(quadKey); - return { - face: faceKey, - url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix, - x: xy[0], - y: xy[1] - }; - }); - }); - - return loadFaces(faces) - .then(() => { - _sceneOptions = { - showFullscreenCtrl: false, - autoLoad: true, - compass: true, - northOffset: d.ca, - yaw: 0, - minHfov: minHfov, - maxHfov: maxHfov, - hfov: defaultHfov, - type: 'cubemap', - cubeMap: [ - _dataUrlArray[0], - _dataUrlArray[1], - _dataUrlArray[2], - _dataUrlArray[3], - _dataUrlArray[4], - _dataUrlArray[5] - ] - }; - return { status: 'ok' }; - }); - }, - - - getSequenceKeyForBubble: function(d) { - return d && d.sequenceKey; - }, - - - // Updates the currently highlighted sequence and selected bubble. - // Reset is only necessary when interacting with the viewport because - // this implicitly changes the currently selected bubble/sequence - setStyles: function (context, hovered, reset) { - if (reset) { // reset all layers - context.container().selectAll('.viewfield-group') - .classed('highlighted', false) - .classed('hovered', false) - .classed('currentView', false); - - context.container().selectAll('.sequence') - .classed('highlighted', false) - .classed('currentView', false); - } - - let hoveredBubbleKey = hovered && hovered.key; - let hoveredSequenceKey = this.getSequenceKeyForBubble(hovered); - let hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey]; - let hoveredBubbleKeys = (hoveredSequence && hoveredSequence.bubbles.map(d => d.key)) || []; - - let viewer = context.container().select('.photoviewer'); - let selected = viewer.empty() ? undefined : viewer.datum(); - let selectedBubbleKey = selected && selected.key; - let selectedSequenceKey = this.getSequenceKeyForBubble(selected); - let selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey]; - let selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(d => d.key)) || []; - - // highlight sibling viewfields on either the selected or the hovered sequences - let highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys); - - context.container().selectAll('.layer-streetside-images .viewfield-group') - .classed('highlighted', d => highlightedBubbleKeys.indexOf(d.key) !== -1) - .classed('hovered', d => d.key === hoveredBubbleKey) - .classed('currentView', d => d.key === selectedBubbleKey); - - context.container().selectAll('.layer-streetside-images .sequence') - .classed('highlighted', d => d.properties.key === hoveredSequenceKey) - .classed('currentView', d => d.properties.key === selectedSequenceKey); - - // update viewfields if needed - context.container().selectAll('.viewfield-group .viewfield') - .attr('d', viewfieldPath); - - function viewfieldPath() { - let d = this.parentNode.__data__; - if (d.pano && d.key !== selectedBubbleKey) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - return this; - }, - - - /** - * cache(). - */ - cache: function () { - return _ssCache; - } - }; - - var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/'; - var _inflight$2 = {}; - var _popularKeys = {}; - var _taginfoCache = {}; - - var tag_sorts = { - point: 'count_nodes', - vertex: 'count_nodes', - area: 'count_ways', - line: 'count_ways' - }; - var tag_sort_members = { - point: 'count_node_members', - vertex: 'count_node_members', - area: 'count_way_members', - line: 'count_way_members', - relation: 'count_relation_members' - }; - var tag_filters = { - point: 'nodes', - vertex: 'nodes', - area: 'ways', - line: 'ways' - }; - var tag_members_fractions = { - point: 'count_node_members_fraction', - vertex: 'count_node_members_fraction', - area: 'count_way_members_fraction', - line: 'count_way_members_fraction', - relation: 'count_relation_members_fraction' - }; - - - function sets(params, n, o) { - if (params.geometry && o[params.geometry]) { - params[n] = o[params.geometry]; - } - return params; - } - - - function setFilter(params) { - return sets(params, 'filter', tag_filters); - } - - - function setSort(params) { - return sets(params, 'sortname', tag_sorts); - } - - - function setSortMembers(params) { - return sets(params, 'sortname', tag_sort_members); - } - - - function clean(params) { - return utilObjectOmit(params, ['geometry', 'debounce']); - } - - - function filterKeys(type) { - var count_type = type ? 'count_' + type : 'count_all'; - return function(d) { - return parseFloat(d[count_type]) > 2500 || d.in_wiki; - }; - } - - - function filterMultikeys(prefix) { - return function(d) { - // d.key begins with prefix, and d.key contains no additional ':'s - var re = new RegExp('^' + prefix + '(.*)$'); - var matches = d.key.match(re) || []; - return (matches.length === 2 && matches[1].indexOf(':') === -1); - }; - } - - - function filterValues(allowUpperCase) { - return function(d) { - if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation - if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters - return parseFloat(d.fraction) > 0.0; - }; - } - - - function filterRoles(geometry) { - return function(d) { - if (d.role === '') return false; // exclude empty role - if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation - return parseFloat(d[tag_members_fractions[geometry]]) > 0.0; - }; - } - - - function valKey(d) { - return { - value: d.key, - title: d.key - }; - } - - - function valKeyDescription(d) { - var obj = { - value: d.value, - title: d.description || d.value - }; - if (d.count) { - obj.count = d.count; - } - return obj; - } - - - function roleKey(d) { - return { - value: d.role, - title: d.role - }; - } - - - // sort keys with ':' lower than keys without ':' - function sortKeys(a, b) { - return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1 - : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1 - : 0; - } - - - var debouncedRequest$1 = debounce(request$1, 300, { leading: false }); - - function request$1(url, params, exactMatch, callback, loaded) { - if (_inflight$2[url]) return; - - if (checkCache(url, params, exactMatch, callback)) return; - - var controller = new AbortController(); - _inflight$2[url] = controller; - - d3_json(url, { signal: controller.signal }) - .then(function(result) { - delete _inflight$2[url]; - if (loaded) loaded(null, result); - }) - .catch(function(err) { - delete _inflight$2[url]; - if (err.name === 'AbortError') return; - if (loaded) loaded(err.message); - }); - } - - - function checkCache(url, params, exactMatch, callback) { - var rp = params.rp || 25; - var testQuery = params.query || ''; - var testUrl = url; - - do { - var hit = _taginfoCache[testUrl]; - - // exact match, or shorter match yielding fewer than max results (rp) - if (hit && (url === testUrl || hit.length < rp)) { - callback(null, hit); - return true; - } - - // don't try to shorten the query - if (exactMatch || !testQuery.length) return false; - - // do shorten the query to see if we already have a cached result - // that has returned fewer than max results (rp) - testQuery = testQuery.slice(0, -1); - testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&'); - } while (testQuery.length >= 0); - - return false; - } - - - var serviceTaginfo = { - - init: function() { - _inflight$2 = {}; - _taginfoCache = {}; - _popularKeys = { - // manually exclude some keys – #5377, #7485 - postal_code: true, - full_name: true, - loc_name: true, - reg_name: true, - short_name: true, - sorting_name: true, - artist_name: true, - nat_name: true, - long_name: true, - 'bridge:name': true - }; - - // Fetch popular keys. We'll exclude these from `values` - // lookups because they stress taginfo, and they aren't likely - // to yield meaningful autocomplete results.. see #3955 - var params = { - rp: 100, - sortname: 'values_all', - sortorder: 'desc', - page: 1, - debounce: false, - lang: _mainLocalizer.languageCode() - }; - this.keys(params, function(err, data) { - if (err) return; - data.forEach(function(d) { - if (d.value === 'opening_hours') return; // exception - _popularKeys[d.value] = true; - }); - }); - }, - - - reset: function() { - Object.values(_inflight$2).forEach(function(controller) { controller.abort(); }); - _inflight$2 = {}; - }, - - - keys: function(params, callback) { - var doRequest = params.debounce ? debouncedRequest$1 : request$1; - params = clean(setSort(params)); - params = Object.assign({ - rp: 10, - sortname: 'count_all', - sortorder: 'desc', - page: 1, - lang: _mainLocalizer.languageCode() - }, params); - - var url = apibase$4 + 'keys/all?' + utilQsString(params); - doRequest(url, params, false, callback, function(err, d) { - if (err) { - callback(err); - } else { - var f = filterKeys(params.filter); - var result = d.data.filter(f).sort(sortKeys).map(valKey); - _taginfoCache[url] = result; - callback(null, result); - } - }); - }, - - - multikeys: function(params, callback) { - var doRequest = params.debounce ? debouncedRequest$1 : request$1; - params = clean(setSort(params)); - params = Object.assign({ - rp: 25, - sortname: 'count_all', - sortorder: 'desc', - page: 1, - lang: _mainLocalizer.languageCode() - }, params); - - var prefix = params.query; - var url = apibase$4 + 'keys/all?' + utilQsString(params); - doRequest(url, params, true, callback, function(err, d) { - if (err) { - callback(err); - } else { - var f = filterMultikeys(prefix); - var result = d.data.filter(f).map(valKey); - _taginfoCache[url] = result; - callback(null, result); - } - }); - }, - - - values: function(params, callback) { - // Exclude popular keys from values lookups.. see #3955 - var key = params.key; - if (key && _popularKeys[key]) { - callback(null, []); - return; - } - - var doRequest = params.debounce ? debouncedRequest$1 : request$1; - params = clean(setSort(setFilter(params))); - params = Object.assign({ - rp: 25, - sortname: 'count_all', - sortorder: 'desc', - page: 1, - lang: _mainLocalizer.languageCode() - }, params); - - var url = apibase$4 + 'key/values?' + utilQsString(params); - doRequest(url, params, false, callback, function(err, d) { - if (err) { - callback(err); - } else { - // In most cases we prefer taginfo value results with lowercase letters. - // A few OSM keys expect values to contain uppercase values (see #3377). - // This is not an exhaustive list (e.g. `name` also has uppercase values) - // but these are the fields where taginfo value lookup is most useful. - var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/; - var allowUpperCase = re.test(params.key); - var f = filterValues(allowUpperCase); - - var result = d.data.filter(f).map(valKeyDescription); - _taginfoCache[url] = result; - callback(null, result); - } - }); - }, - - - roles: function(params, callback) { - var doRequest = params.debounce ? debouncedRequest$1 : request$1; - var geometry = params.geometry; - params = clean(setSortMembers(params)); - params = Object.assign({ - rp: 25, - sortname: 'count_all_members', - sortorder: 'desc', - page: 1, - lang: _mainLocalizer.languageCode() - }, params); - - var url = apibase$4 + 'relation/roles?' + utilQsString(params); - doRequest(url, params, true, callback, function(err, d) { - if (err) { - callback(err); - } else { - var f = filterRoles(geometry); - var result = d.data.filter(f).map(roleKey); - _taginfoCache[url] = result; - callback(null, result); - } - }); - }, - - - docs: function(params, callback) { - var doRequest = params.debounce ? debouncedRequest$1 : request$1; - params = clean(setSort(params)); - - var path = 'key/wiki_pages?'; - if (params.value) { - path = 'tag/wiki_pages?'; - } else if (params.rtype) { - path = 'relation/wiki_pages?'; - } - - var url = apibase$4 + path + utilQsString(params); - doRequest(url, params, true, callback, function(err, d) { - if (err) { - callback(err); - } else { - _taginfoCache[url] = d.data; - callback(null, d.data); - } - }); - }, - - - apibase: function(_) { - if (!arguments.length) return apibase$4; - apibase$4 = _; - return this; - } - - }; - - var helpers$1 = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - /** - * @module helpers - */ - /** - * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth. - * - * @memberof helpers - * @type {number} - */ - exports.earthRadius = 6371008.8; - /** - * Unit of measurement factors using a spherical (non-ellipsoid) earth radius. - * - * @memberof helpers - * @type {Object} - */ - exports.factors = { - centimeters: exports.earthRadius * 100, - centimetres: exports.earthRadius * 100, - degrees: exports.earthRadius / 111325, - feet: exports.earthRadius * 3.28084, - inches: exports.earthRadius * 39.370, - kilometers: exports.earthRadius / 1000, - kilometres: exports.earthRadius / 1000, - meters: exports.earthRadius, - metres: exports.earthRadius, - miles: exports.earthRadius / 1609.344, - millimeters: exports.earthRadius * 1000, - millimetres: exports.earthRadius * 1000, - nauticalmiles: exports.earthRadius / 1852, - radians: 1, - yards: exports.earthRadius / 1.0936, - }; - /** - * Units of measurement factors based on 1 meter. - * - * @memberof helpers - * @type {Object} - */ - exports.unitsFactors = { - centimeters: 100, - centimetres: 100, - degrees: 1 / 111325, - feet: 3.28084, - inches: 39.370, - kilometers: 1 / 1000, - kilometres: 1 / 1000, - meters: 1, - metres: 1, - miles: 1 / 1609.344, - millimeters: 1000, - millimetres: 1000, - nauticalmiles: 1 / 1852, - radians: 1 / exports.earthRadius, - yards: 1 / 1.0936, - }; - /** - * Area of measurement factors based on 1 square meter. - * - * @memberof helpers - * @type {Object} - */ - exports.areaFactors = { - acres: 0.000247105, - centimeters: 10000, - centimetres: 10000, - feet: 10.763910417, - inches: 1550.003100006, - kilometers: 0.000001, - kilometres: 0.000001, - meters: 1, - metres: 1, - miles: 3.86e-7, - millimeters: 1000000, - millimetres: 1000000, - yards: 1.195990046, - }; - /** - * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}. - * - * @name feature - * @param {Geometry} geometry input geometry - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a GeoJSON Feature - * @example - * var geometry = { - * "type": "Point", - * "coordinates": [110, 50] - * }; - * - * var feature = turf.feature(geometry); - * - * //=feature - */ - function feature(geom, properties, options) { - if (options === void 0) { options = {}; } - var feat = { type: "Feature" }; - if (options.id === 0 || options.id) { - feat.id = options.id; - } - if (options.bbox) { - feat.bbox = options.bbox; - } - feat.properties = properties || {}; - feat.geometry = geom; - return feat; - } - exports.feature = feature; - /** - * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates. - * For GeometryCollection type use `helpers.geometryCollection` - * - * @name geometry - * @param {string} type Geometry Type - * @param {Array} coordinates Coordinates - * @param {Object} [options={}] Optional Parameters - * @returns {Geometry} a GeoJSON Geometry - * @example - * var type = "Point"; - * var coordinates = [110, 50]; - * var geometry = turf.geometry(type, coordinates); - * // => geometry - */ - function geometry(type, coordinates, options) { - switch (type) { - case "Point": return point(coordinates).geometry; - case "LineString": return lineString(coordinates).geometry; - case "Polygon": return polygon(coordinates).geometry; - case "MultiPoint": return multiPoint(coordinates).geometry; - case "MultiLineString": return multiLineString(coordinates).geometry; - case "MultiPolygon": return multiPolygon(coordinates).geometry; - default: throw new Error(type + " is invalid"); - } - } - exports.geometry = geometry; - /** - * Creates a {@link Point} {@link Feature} from a Position. - * - * @name point - * @param {Array} coordinates longitude, latitude position (each in decimal degrees) - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a Point feature - * @example - * var point = turf.point([-75.343, 39.984]); - * - * //=point - */ - function point(coordinates, properties, options) { - if (options === void 0) { options = {}; } - var geom = { - type: "Point", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.point = point; - /** - * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates. - * - * @name points - * @param {Array>} coordinates an array of Points - * @param {Object} [properties={}] Translate these properties to each Feature - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] - * associated with the FeatureCollection - * @param {string|number} [options.id] Identifier associated with the FeatureCollection - * @returns {FeatureCollection} Point Feature - * @example - * var points = turf.points([ - * [-75, 39], - * [-80, 45], - * [-78, 50] - * ]); - * - * //=points - */ - function points(coordinates, properties, options) { - if (options === void 0) { options = {}; } - return featureCollection(coordinates.map(function (coords) { - return point(coords, properties); - }), options); - } - exports.points = points; - /** - * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings. - * - * @name polygon - * @param {Array>>} coordinates an array of LinearRings - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} Polygon Feature - * @example - * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' }); - * - * //=polygon - */ - function polygon(coordinates, properties, options) { - if (options === void 0) { options = {}; } - for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) { - var ring = coordinates_1[_i]; - if (ring.length < 4) { - throw new Error("Each LinearRing of a Polygon must have 4 or more Positions."); - } - for (var j = 0; j < ring[ring.length - 1].length; j++) { - // Check if first point of Polygon contains two numbers - if (ring[ring.length - 1][j] !== ring[0][j]) { - throw new Error("First and last Position are not equivalent."); - } - } - } - var geom = { - type: "Polygon", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.polygon = polygon; - /** - * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates. - * - * @name polygons - * @param {Array>>>} coordinates an array of Polygon coordinates - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the FeatureCollection - * @returns {FeatureCollection} Polygon FeatureCollection - * @example - * var polygons = turf.polygons([ - * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], - * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]], - * ]); - * - * //=polygons - */ - function polygons(coordinates, properties, options) { - if (options === void 0) { options = {}; } - return featureCollection(coordinates.map(function (coords) { - return polygon(coords, properties); - }), options); - } - exports.polygons = polygons; - /** - * Creates a {@link LineString} {@link Feature} from an Array of Positions. - * - * @name lineString - * @param {Array>} coordinates an array of Positions - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} LineString Feature - * @example - * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'}); - * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'}); - * - * //=linestring1 - * //=linestring2 - */ - function lineString(coordinates, properties, options) { - if (options === void 0) { options = {}; } - if (coordinates.length < 2) { - throw new Error("coordinates must be an array of two or more positions"); - } - var geom = { - type: "LineString", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.lineString = lineString; - /** - * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates. - * - * @name lineStrings - * @param {Array>>} coordinates an array of LinearRings - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] - * associated with the FeatureCollection - * @param {string|number} [options.id] Identifier associated with the FeatureCollection - * @returns {FeatureCollection} LineString FeatureCollection - * @example - * var linestrings = turf.lineStrings([ - * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]], - * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]] - * ]); - * - * //=linestrings - */ - function lineStrings(coordinates, properties, options) { - if (options === void 0) { options = {}; } - return featureCollection(coordinates.map(function (coords) { - return lineString(coords, properties); - }), options); - } - exports.lineStrings = lineStrings; - /** - * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}. - * - * @name featureCollection - * @param {Feature[]} features input features - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {FeatureCollection} FeatureCollection of Features - * @example - * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'}); - * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'}); - * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'}); - * - * var collection = turf.featureCollection([ - * locationA, - * locationB, - * locationC - * ]); - * - * //=collection - */ - function featureCollection(features, options) { - if (options === void 0) { options = {}; } - var fc = { type: "FeatureCollection" }; - if (options.id) { - fc.id = options.id; - } - if (options.bbox) { - fc.bbox = options.bbox; - } - fc.features = features; - return fc; - } - exports.featureCollection = featureCollection; - /** - * Creates a {@link Feature} based on a - * coordinate array. Properties can be added optionally. - * - * @name multiLineString - * @param {Array>>} coordinates an array of LineStrings - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a MultiLineString feature - * @throws {Error} if no coordinates are passed - * @example - * var multiLine = turf.multiLineString([[[0,0],[10,10]]]); - * - * //=multiLine - */ - function multiLineString(coordinates, properties, options) { - if (options === void 0) { options = {}; } - var geom = { - type: "MultiLineString", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.multiLineString = multiLineString; - /** - * Creates a {@link Feature} based on a - * coordinate array. Properties can be added optionally. - * - * @name multiPoint - * @param {Array>} coordinates an array of Positions - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a MultiPoint feature - * @throws {Error} if no coordinates are passed - * @example - * var multiPt = turf.multiPoint([[0,0],[10,10]]); - * - * //=multiPt - */ - function multiPoint(coordinates, properties, options) { - if (options === void 0) { options = {}; } - var geom = { - type: "MultiPoint", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.multiPoint = multiPoint; - /** - * Creates a {@link Feature} based on a - * coordinate array. Properties can be added optionally. - * - * @name multiPolygon - * @param {Array>>>} coordinates an array of Polygons - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a multipolygon feature - * @throws {Error} if no coordinates are passed - * @example - * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]); - * - * //=multiPoly - * - */ - function multiPolygon(coordinates, properties, options) { - if (options === void 0) { options = {}; } - var geom = { - type: "MultiPolygon", - coordinates: coordinates, - }; - return feature(geom, properties, options); - } - exports.multiPolygon = multiPolygon; - /** - * Creates a {@link Feature} based on a - * coordinate array. Properties can be added optionally. - * - * @name geometryCollection - * @param {Array} geometries an array of GeoJSON Geometries - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a GeoJSON GeometryCollection Feature - * @example - * var pt = turf.geometry("Point", [100, 0]); - * var line = turf.geometry("LineString", [[101, 0], [102, 1]]); - * var collection = turf.geometryCollection([pt, line]); - * - * // => collection - */ - function geometryCollection(geometries, properties, options) { - if (options === void 0) { options = {}; } - var geom = { - type: "GeometryCollection", - geometries: geometries, - }; - return feature(geom, properties, options); - } - exports.geometryCollection = geometryCollection; - /** - * Round number to precision - * - * @param {number} num Number - * @param {number} [precision=0] Precision - * @returns {number} rounded number - * @example - * turf.round(120.4321) - * //=120 - * - * turf.round(120.4321, 2) - * //=120.43 - */ - function round(num, precision) { - if (precision === void 0) { precision = 0; } - if (precision && !(precision >= 0)) { - throw new Error("precision must be a positive number"); - } - var multiplier = Math.pow(10, precision || 0); - return Math.round(num * multiplier) / multiplier; - } - exports.round = round; - /** - * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit. - * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet - * - * @name radiansToLength - * @param {number} radians in radians across the sphere - * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, - * meters, kilometres, kilometers. - * @returns {number} distance - */ - function radiansToLength(radians, units) { - if (units === void 0) { units = "kilometers"; } - var factor = exports.factors[units]; - if (!factor) { - throw new Error(units + " units is invalid"); - } - return radians * factor; - } - exports.radiansToLength = radiansToLength; - /** - * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians - * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet - * - * @name lengthToRadians - * @param {number} distance in real units - * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, - * meters, kilometres, kilometers. - * @returns {number} radians - */ - function lengthToRadians(distance, units) { - if (units === void 0) { units = "kilometers"; } - var factor = exports.factors[units]; - if (!factor) { - throw new Error(units + " units is invalid"); - } - return distance / factor; - } - exports.lengthToRadians = lengthToRadians; - /** - * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees - * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet - * - * @name lengthToDegrees - * @param {number} distance in real units - * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres, - * meters, kilometres, kilometers. - * @returns {number} degrees - */ - function lengthToDegrees(distance, units) { - return radiansToDegrees(lengthToRadians(distance, units)); - } - exports.lengthToDegrees = lengthToDegrees; - /** - * Converts any bearing angle from the north line direction (positive clockwise) - * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line - * - * @name bearingToAzimuth - * @param {number} bearing angle, between -180 and +180 degrees - * @returns {number} angle between 0 and 360 degrees - */ - function bearingToAzimuth(bearing) { - var angle = bearing % 360; - if (angle < 0) { - angle += 360; - } - return angle; - } - exports.bearingToAzimuth = bearingToAzimuth; - /** - * Converts an angle in radians to degrees - * - * @name radiansToDegrees - * @param {number} radians angle in radians - * @returns {number} degrees between 0 and 360 degrees - */ - function radiansToDegrees(radians) { - var degrees = radians % (2 * Math.PI); - return degrees * 180 / Math.PI; - } - exports.radiansToDegrees = radiansToDegrees; - /** - * Converts an angle in degrees to radians - * - * @name degreesToRadians - * @param {number} degrees angle between 0 and 360 degrees - * @returns {number} angle in radians - */ - function degreesToRadians(degrees) { - var radians = degrees % 360; - return radians * Math.PI / 180; - } - exports.degreesToRadians = degreesToRadians; - /** - * Converts a length to the requested unit. - * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet - * - * @param {number} length to be converted - * @param {Units} [originalUnit="kilometers"] of the length - * @param {Units} [finalUnit="kilometers"] returned unit - * @returns {number} the converted length - */ - function convertLength(length, originalUnit, finalUnit) { - if (originalUnit === void 0) { originalUnit = "kilometers"; } - if (finalUnit === void 0) { finalUnit = "kilometers"; } - if (!(length >= 0)) { - throw new Error("length must be a positive number"); - } - return radiansToLength(lengthToRadians(length, originalUnit), finalUnit); - } - exports.convertLength = convertLength; - /** - * Converts a area to the requested unit. - * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches - * @param {number} area to be converted - * @param {Units} [originalUnit="meters"] of the distance - * @param {Units} [finalUnit="kilometers"] returned unit - * @returns {number} the converted distance - */ - function convertArea(area, originalUnit, finalUnit) { - if (originalUnit === void 0) { originalUnit = "meters"; } - if (finalUnit === void 0) { finalUnit = "kilometers"; } - if (!(area >= 0)) { - throw new Error("area must be a positive number"); - } - var startFactor = exports.areaFactors[originalUnit]; - if (!startFactor) { - throw new Error("invalid original units"); - } - var finalFactor = exports.areaFactors[finalUnit]; - if (!finalFactor) { - throw new Error("invalid final units"); - } - return (area / startFactor) * finalFactor; - } - exports.convertArea = convertArea; - /** - * isNumber - * - * @param {*} num Number to validate - * @returns {boolean} true/false - * @example - * turf.isNumber(123) - * //=true - * turf.isNumber('foo') - * //=false - */ - function isNumber(num) { - return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num); - } - exports.isNumber = isNumber; - /** - * isObject - * - * @param {*} input variable to validate - * @returns {boolean} true/false - * @example - * turf.isObject({elevation: 10}) - * //=true - * turf.isObject('foo') - * //=false - */ - function isObject(input) { - return (!!input) && (input.constructor === Object); - } - exports.isObject = isObject; - /** - * Validate BBox - * - * @private - * @param {Array} bbox BBox to validate - * @returns {void} - * @throws Error if BBox is not valid - * @example - * validateBBox([-180, -40, 110, 50]) - * //=OK - * validateBBox([-180, -40]) - * //=Error - * validateBBox('Foo') - * //=Error - * validateBBox(5) - * //=Error - * validateBBox(null) - * //=Error - * validateBBox(undefined) - * //=Error - */ - function validateBBox(bbox) { - if (!bbox) { - throw new Error("bbox is required"); - } - if (!Array.isArray(bbox)) { - throw new Error("bbox must be an Array"); - } - if (bbox.length !== 4 && bbox.length !== 6) { - throw new Error("bbox must be an Array of 4 or 6 numbers"); - } - bbox.forEach(function (num) { - if (!isNumber(num)) { - throw new Error("bbox must only contain numbers"); - } - }); - } - exports.validateBBox = validateBBox; - /** - * Validate Id - * - * @private - * @param {string|number} id Id to validate - * @returns {void} - * @throws Error if Id is not valid - * @example - * validateId([-180, -40, 110, 50]) - * //=Error - * validateId([-180, -40]) - * //=Error - * validateId('Foo') - * //=OK - * validateId(5) - * //=OK - * validateId(null) - * //=Error - * validateId(undefined) - * //=Error - */ - function validateId(id) { - if (!id) { - throw new Error("id is required"); - } - if (["string", "number"].indexOf(typeof id) === -1) { - throw new Error("id must be a number or a string"); - } - } - exports.validateId = validateId; - // Deprecated methods - function radians2degrees() { - throw new Error("method has been renamed to `radiansToDegrees`"); - } - exports.radians2degrees = radians2degrees; - function degrees2radians() { - throw new Error("method has been renamed to `degreesToRadians`"); - } - exports.degrees2radians = degrees2radians; - function distanceToDegrees() { - throw new Error("method has been renamed to `lengthToDegrees`"); - } - exports.distanceToDegrees = distanceToDegrees; - function distanceToRadians() { - throw new Error("method has been renamed to `lengthToRadians`"); - } - exports.distanceToRadians = distanceToRadians; - function radiansToDistance() { - throw new Error("method has been renamed to `radiansToLength`"); - } - exports.radiansToDistance = radiansToDistance; - function bearingToAngle() { - throw new Error("method has been renamed to `bearingToAzimuth`"); - } - exports.bearingToAngle = bearingToAngle; - function convertDistance() { - throw new Error("method has been renamed to `convertLength`"); - } - exports.convertDistance = convertDistance; - }); - - var invariant = createCommonjsModule(function (module, exports) { - Object.defineProperty(exports, "__esModule", { value: true }); - - /** - * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate. - * - * @name getCoord - * @param {Array|Geometry|Feature} coord GeoJSON Point or an Array of numbers - * @returns {Array} coordinates - * @example - * var pt = turf.point([10, 10]); - * - * var coord = turf.getCoord(pt); - * //= [10, 10] - */ - function getCoord(coord) { - if (!coord) { - throw new Error("coord is required"); - } - if (!Array.isArray(coord)) { - if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") { - return coord.geometry.coordinates; - } - if (coord.type === "Point") { - return coord.coordinates; - } - } - if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) { - return coord; - } - throw new Error("coord must be GeoJSON Point or an Array of numbers"); - } - exports.getCoord = getCoord; - /** - * Unwrap coordinates from a Feature, Geometry Object or an Array - * - * @name getCoords - * @param {Array|Geometry|Feature} coords Feature, Geometry Object or an Array - * @returns {Array} coordinates - * @example - * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]); - * - * var coords = turf.getCoords(poly); - * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]] - */ - function getCoords(coords) { - if (Array.isArray(coords)) { - return coords; - } - // Feature - if (coords.type === "Feature") { - if (coords.geometry !== null) { - return coords.geometry.coordinates; - } - } - else { - // Geometry - if (coords.coordinates) { - return coords.coordinates; - } - } - throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array"); - } - exports.getCoords = getCoords; - /** - * Checks if coordinates contains a number - * - * @name containsNumber - * @param {Array} coordinates GeoJSON Coordinates - * @returns {boolean} true if Array contains a number - */ - function containsNumber(coordinates) { - if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) { - return true; - } - if (Array.isArray(coordinates[0]) && coordinates[0].length) { - return containsNumber(coordinates[0]); - } - throw new Error("coordinates must only contain numbers"); - } - exports.containsNumber = containsNumber; - /** - * Enforce expectations about types of GeoJSON objects for Turf. - * - * @name geojsonType - * @param {GeoJSON} value any GeoJSON object - * @param {string} type expected GeoJSON type - * @param {string} name name of calling function - * @throws {Error} if value is not the expected type. - */ - function geojsonType(value, type, name) { - if (!type || !name) { - throw new Error("type and name required"); - } - if (!value || value.type !== type) { - throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type); - } - } - exports.geojsonType = geojsonType; - /** - * Enforce expectations about types of {@link Feature} inputs for Turf. - * Internally this uses {@link geojsonType} to judge geometry types. - * - * @name featureOf - * @param {Feature} feature a feature with an expected geometry type - * @param {string} type expected GeoJSON type - * @param {string} name name of calling function - * @throws {Error} error if value is not the expected type. - */ - function featureOf(feature, type, name) { - if (!feature) { - throw new Error("No feature passed"); - } - if (!name) { - throw new Error(".featureOf() requires a name"); - } - if (!feature || feature.type !== "Feature" || !feature.geometry) { - throw new Error("Invalid input to " + name + ", Feature with geometry required"); - } - if (!feature.geometry || feature.geometry.type !== type) { - throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type); - } - } - exports.featureOf = featureOf; - /** - * Enforce expectations about types of {@link FeatureCollection} inputs for Turf. - * Internally this uses {@link geojsonType} to judge geometry types. - * - * @name collectionOf - * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged - * @param {string} type expected GeoJSON type - * @param {string} name name of calling function - * @throws {Error} if value is not the expected type. - */ - function collectionOf(featureCollection, type, name) { - if (!featureCollection) { - throw new Error("No featureCollection passed"); - } - if (!name) { - throw new Error(".collectionOf() requires a name"); - } - if (!featureCollection || featureCollection.type !== "FeatureCollection") { - throw new Error("Invalid input to " + name + ", FeatureCollection required"); - } - for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) { - var feature = _a[_i]; - if (!feature || feature.type !== "Feature" || !feature.geometry) { - throw new Error("Invalid input to " + name + ", Feature with geometry required"); - } - if (!feature.geometry || feature.geometry.type !== type) { - throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type); - } - } - } - exports.collectionOf = collectionOf; - /** - * Get Geometry from Feature or Geometry Object - * - * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object - * @returns {Geometry|null} GeoJSON Geometry Object - * @throws {Error} if geojson is not a Feature or Geometry Object - * @example - * var point = { - * "type": "Feature", - * "properties": {}, - * "geometry": { - * "type": "Point", - * "coordinates": [110, 40] - * } - * } - * var geom = turf.getGeom(point) - * //={"type": "Point", "coordinates": [110, 40]} - */ - function getGeom(geojson) { - if (geojson.type === "Feature") { - return geojson.geometry; - } - return geojson; - } - exports.getGeom = getGeom; - /** - * Get GeoJSON object's type, Geometry type is prioritize. - * - * @param {GeoJSON} geojson GeoJSON object - * @param {string} [name="geojson"] name of the variable to display in error message - * @returns {string} GeoJSON type - * @example - * var point = { - * "type": "Feature", - * "properties": {}, - * "geometry": { - * "type": "Point", - * "coordinates": [110, 40] - * } - * } - * var geom = turf.getType(point) - * //="Point" - */ - function getType(geojson, name) { - if (geojson.type === "FeatureCollection") { - return "FeatureCollection"; - } - if (geojson.type === "GeometryCollection") { - return "GeometryCollection"; - } - if (geojson.type === "Feature" && geojson.geometry !== null) { - return geojson.geometry.type; - } - return geojson.type; - } - exports.getType = getType; - }); - - var lineclip_1 = lineclip; - var _default = lineclip; - - lineclip.polyline = lineclip; - lineclip.polygon = polygonclip; - - - // Cohen-Sutherland line clippign algorithm, adapted to efficiently - // handle polylines rather than just segments - - function lineclip(points, bbox, result) { - - var len = points.length, - codeA = bitCode(points[0], bbox), - part = [], - i, a, b, codeB, lastCode; - - if (!result) result = []; - - for (i = 1; i < len; i++) { - a = points[i - 1]; - b = points[i]; - codeB = lastCode = bitCode(b, bbox); - - while (true) { - - if (!(codeA | codeB)) { // accept - part.push(a); - - if (codeB !== lastCode) { // segment went outside - part.push(b); - - if (i < len - 1) { // start a new line - result.push(part); - part = []; - } - } else if (i === len - 1) { - part.push(b); - } - break; - - } else if (codeA & codeB) { // trivial reject - break; - - } else if (codeA) { // a outside, intersect with clip edge - a = intersect(a, b, codeA, bbox); - codeA = bitCode(a, bbox); - - } else { // b outside - b = intersect(a, b, codeB, bbox); - codeB = bitCode(b, bbox); - } - } - - codeA = lastCode; - } - - if (part.length) result.push(part); - - return result; - } - - // Sutherland-Hodgeman polygon clipping algorithm - - function polygonclip(points, bbox) { - - var result, edge, prev, prevInside, i, p, inside; - - // clip against each side of the clip rectangle - for (edge = 1; edge <= 8; edge *= 2) { - result = []; - prev = points[points.length - 1]; - prevInside = !(bitCode(prev, bbox) & edge); - - for (i = 0; i < points.length; i++) { - p = points[i]; - inside = !(bitCode(p, bbox) & edge); - - // if segment goes through the clip window, add an intersection - if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox)); - - if (inside) result.push(p); // add a point if it's inside - - prev = p; - prevInside = inside; - } - - points = result; - - if (!points.length) break; - } - - return result; - } - - // intersect a segment against one of the 4 lines that make up the bbox - - function intersect(a, b, edge, bbox) { - return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top - edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom - edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right - edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left - null; - } - - // bit code reflects the point position relative to the bbox: - - // left mid right - // top 1001 1000 1010 - // mid 0001 0000 0010 - // bottom 0101 0100 0110 - - function bitCode(p, bbox) { - var code = 0; - - if (p[0] < bbox[0]) code |= 1; // left - else if (p[0] > bbox[2]) code |= 2; // right - - if (p[1] < bbox[1]) code |= 4; // bottom - else if (p[1] > bbox[3]) code |= 8; // top - - return code; - } - lineclip_1.default = _default; - - var bboxClip_1 = createCommonjsModule(function (module, exports) { - var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; - }; - Object.defineProperty(exports, "__esModule", { value: true }); - - - var lineclip = __importStar(lineclip_1); - /** - * Takes a {@link Feature} and a bbox and clips the feature to the bbox using - * [lineclip](https://github.com/mapbox/lineclip). - * May result in degenerate edges when clipping Polygons. - * - * @name bboxClip - * @param {Feature} feature feature to clip to the bbox - * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order - * @returns {Feature} clipped Feature - * @example - * var bbox = [0, 0, 10, 10]; - * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]); - * - * var clipped = turf.bboxClip(poly, bbox); - * - * //addToMap - * var addToMap = [bbox, poly, clipped] - */ - function bboxClip(feature, bbox) { - var geom = invariant.getGeom(feature); - var type = geom.type; - var properties = feature.type === "Feature" ? feature.properties : {}; - var coords = geom.coordinates; - switch (type) { - case "LineString": - case "MultiLineString": - var lines_1 = []; - if (type === "LineString") { - coords = [coords]; - } - coords.forEach(function (line) { - lineclip.polyline(line, bbox, lines_1); - }); - if (lines_1.length === 1) { - return helpers$1.lineString(lines_1[0], properties); - } - return helpers$1.multiLineString(lines_1, properties); - case "Polygon": - return helpers$1.polygon(clipPolygon(coords, bbox), properties); - case "MultiPolygon": - return helpers$1.multiPolygon(coords.map(function (poly) { - return clipPolygon(poly, bbox); - }), properties); - default: - throw new Error("geometry " + type + " not supported"); - } - } - exports.default = bboxClip; - function clipPolygon(rings, bbox) { - var outRings = []; - for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) { - var ring = rings_1[_i]; - var clipped = lineclip.polygon(ring, bbox); - if (clipped.length > 0) { - if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) { - clipped.push(clipped[0]); - } - if (clipped.length >= 4) { - outRings.push(clipped); - } - } - } - return outRings; - } - }); - - var fastJsonStableStringify = function (data, opts) { - if (!opts) opts = {}; - if (typeof opts === 'function') opts = { cmp: opts }; - var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false; - - var cmp = opts.cmp && (function (f) { - return function (node) { - return function (a, b) { - var aobj = { key: a, value: node[a] }; - var bobj = { key: b, value: node[b] }; - return f(aobj, bobj); - }; - }; - })(opts.cmp); - - var seen = []; - return (function stringify (node) { - if (node && node.toJSON && typeof node.toJSON === 'function') { - node = node.toJSON(); - } - - if (node === undefined) return; - if (typeof node == 'number') return isFinite(node) ? '' + node : 'null'; - if (typeof node !== 'object') return JSON.stringify(node); - - var i, out; - if (Array.isArray(node)) { - out = '['; - for (i = 0; i < node.length; i++) { - if (i) out += ','; - out += stringify(node[i]) || 'null'; - } - return out + ']'; - } - - if (node === null) return 'null'; - - if (seen.indexOf(node) !== -1) { - if (cycles) return JSON.stringify('__cycle__'); - throw new TypeError('Converting circular structure to JSON'); - } - - var seenIndex = seen.push(node) - 1; - var keys = Object.keys(node).sort(cmp && cmp(node)); - out = ''; - for (i = 0; i < keys.length; i++) { - var key = keys[i]; - var value = stringify(node[key]); - - if (!value) continue; - if (out) out += ','; - out += JSON.stringify(key) + ':' + value; - } - seen.splice(seenIndex, 1); - return '{' + out + '}'; - })(data); - }; - - function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } - - class SplayTree { - - constructor(compare = DEFAULT_COMPARE, noDuplicates = false) { - this._compare = compare; - this._root = null; - this._size = 0; - this._noDuplicates = !!noDuplicates; - } - - - rotateLeft(x) { - var y = x.right; - if (y) { - x.right = y.left; - if (y.left) y.left.parent = x; - y.parent = x.parent; - } - - if (!x.parent) this._root = y; - else if (x === x.parent.left) x.parent.left = y; - else x.parent.right = y; - if (y) y.left = x; - x.parent = y; - } - - - rotateRight(x) { - var y = x.left; - if (y) { - x.left = y.right; - if (y.right) y.right.parent = x; - y.parent = x.parent; - } - - if (!x.parent) this._root = y; - else if(x === x.parent.left) x.parent.left = y; - else x.parent.right = y; - if (y) y.right = x; - x.parent = y; - } - - - _splay(x) { - while (x.parent) { - var p = x.parent; - if (!p.parent) { - if (p.left === x) this.rotateRight(p); - else this.rotateLeft(p); - } else if (p.left === x && p.parent.left === p) { - this.rotateRight(p.parent); - this.rotateRight(p); - } else if (p.right === x && p.parent.right === p) { - this.rotateLeft(p.parent); - this.rotateLeft(p); - } else if (p.left === x && p.parent.right === p) { - this.rotateRight(p); - this.rotateLeft(p); - } else { - this.rotateLeft(p); - this.rotateRight(p); - } - } - } - - - splay(x) { - var p, gp, ggp, l, r; - - while (x.parent) { - p = x.parent; - gp = p.parent; - - if (gp && gp.parent) { - ggp = gp.parent; - if (ggp.left === gp) ggp.left = x; - else ggp.right = x; - x.parent = ggp; - } else { - x.parent = null; - this._root = x; - } - - l = x.left; r = x.right; - - if (x === p.left) { // left - if (gp) { - if (gp.left === p) { - /* zig-zig */ - if (p.right) { - gp.left = p.right; - gp.left.parent = gp; - } else gp.left = null; - - p.right = gp; - gp.parent = p; - } else { - /* zig-zag */ - if (l) { - gp.right = l; - l.parent = gp; - } else gp.right = null; - - x.left = gp; - gp.parent = x; - } - } - if (r) { - p.left = r; - r.parent = p; - } else p.left = null; - - x.right = p; - p.parent = x; - } else { // right - if (gp) { - if (gp.right === p) { - /* zig-zig */ - if (p.left) { - gp.right = p.left; - gp.right.parent = gp; - } else gp.right = null; - - p.left = gp; - gp.parent = p; - } else { - /* zig-zag */ - if (r) { - gp.left = r; - r.parent = gp; - } else gp.left = null; - - x.right = gp; - gp.parent = x; - } - } - if (l) { - p.right = l; - l.parent = p; - } else p.right = null; - - x.left = p; - p.parent = x; - } - } - } - - - replace(u, v) { - if (!u.parent) this._root = v; - else if (u === u.parent.left) u.parent.left = v; - else u.parent.right = v; - if (v) v.parent = u.parent; - } - - - minNode(u = this._root) { - if (u) while (u.left) u = u.left; - return u; - } - - - maxNode(u = this._root) { - if (u) while (u.right) u = u.right; - return u; - } - - - insert(key, data) { - var z = this._root; - var p = null; - var comp = this._compare; - var cmp; - - if (this._noDuplicates) { - while (z) { - p = z; - cmp = comp(z.key, key); - if (cmp === 0) return; - else if (comp(z.key, key) < 0) z = z.right; - else z = z.left; - } - } else { - while (z) { - p = z; - if (comp(z.key, key) < 0) z = z.right; - else z = z.left; - } - } - - z = { key, data, left: null, right: null, parent: p }; - - if (!p) this._root = z; - else if (comp(p.key, z.key) < 0) p.right = z; - else p.left = z; - - this.splay(z); - this._size++; - return z; - } - - - find (key) { - var z = this._root; - var comp = this._compare; - while (z) { - var cmp = comp(z.key, key); - if (cmp < 0) z = z.right; - else if (cmp > 0) z = z.left; - else return z; - } - return null; - } - - /** - * Whether the tree contains a node with the given key - * @param {Key} key - * @return {boolean} true/false - */ - contains (key) { - var node = this._root; - var comparator = this._compare; - while (node) { - var cmp = comparator(key, node.key); - if (cmp === 0) return true; - else if (cmp < 0) node = node.left; - else node = node.right; - } - - return false; - } - - - remove (key) { - var z = this.find(key); - - if (!z) return false; - - this.splay(z); - - if (!z.left) this.replace(z, z.right); - else if (!z.right) this.replace(z, z.left); - else { - var y = this.minNode(z.right); - if (y.parent !== z) { - this.replace(y, y.right); - y.right = z.right; - y.right.parent = y; - } - this.replace(z, y); - y.left = z.left; - y.left.parent = y; - } - - this._size--; - return true; - } - - - removeNode(z) { - if (!z) return false; - - this.splay(z); - - if (!z.left) this.replace(z, z.right); - else if (!z.right) this.replace(z, z.left); - else { - var y = this.minNode(z.right); - if (y.parent !== z) { - this.replace(y, y.right); - y.right = z.right; - y.right.parent = y; - } - this.replace(z, y); - y.left = z.left; - y.left.parent = y; - } - - this._size--; - return true; - } - - - erase (key) { - var z = this.find(key); - if (!z) return; - - this.splay(z); - - var s = z.left; - var t = z.right; - - var sMax = null; - if (s) { - s.parent = null; - sMax = this.maxNode(s); - this.splay(sMax); - this._root = sMax; - } - if (t) { - if (s) sMax.right = t; - else this._root = t; - t.parent = sMax; - } - - this._size--; - } - - /** - * Removes and returns the node with smallest key - * @return {?Node} - */ - pop () { - var node = this._root, returnValue = null; - if (node) { - while (node.left) node = node.left; - returnValue = { key: node.key, data: node.data }; - this.remove(node.key); - } - return returnValue; - } - - - /* eslint-disable class-methods-use-this */ - - /** - * Successor node - * @param {Node} node - * @return {?Node} - */ - next (node) { - var successor = node; - if (successor) { - if (successor.right) { - successor = successor.right; - while (successor && successor.left) successor = successor.left; - } else { - successor = node.parent; - while (successor && successor.right === node) { - node = successor; successor = successor.parent; - } - } - } - return successor; - } - - - /** - * Predecessor node - * @param {Node} node - * @return {?Node} - */ - prev (node) { - var predecessor = node; - if (predecessor) { - if (predecessor.left) { - predecessor = predecessor.left; - while (predecessor && predecessor.right) predecessor = predecessor.right; - } else { - predecessor = node.parent; - while (predecessor && predecessor.left === node) { - node = predecessor; - predecessor = predecessor.parent; - } - } - } - return predecessor; - } - /* eslint-enable class-methods-use-this */ - - - /** - * @param {forEachCallback} callback - * @return {SplayTree} - */ - forEach(callback) { - var current = this._root; - var s = [], done = false, i = 0; - - while (!done) { - // Reach the left most Node of the current Node - if (current) { - // Place pointer to a tree node on the stack - // before traversing the node's left subtree - s.push(current); - current = current.left; - } else { - // BackTrack from the empty subtree and visit the Node - // at the top of the stack; however, if the stack is - // empty you are done - if (s.length > 0) { - current = s.pop(); - callback(current, i++); - - // We have visited the node and its left - // subtree. Now, it's right subtree's turn - current = current.right; - } else done = true; - } - } - return this; - } - - - /** - * Walk key range from `low` to `high`. Stops if `fn` returns a value. - * @param {Key} low - * @param {Key} high - * @param {Function} fn - * @param {*?} ctx - * @return {SplayTree} - */ - range(low, high, fn, ctx) { - const Q = []; - const compare = this._compare; - let node = this._root, cmp; - - while (Q.length !== 0 || node) { - if (node) { - Q.push(node); - node = node.left; - } else { - node = Q.pop(); - cmp = compare(node.key, high); - if (cmp > 0) { - break; - } else if (compare(node.key, low) >= 0) { - if (fn.call(ctx, node)) return this; // stop if smth is returned - } - node = node.right; - } - } - return this; - } - - /** - * Returns all keys in order - * @return {Array} - */ - keys () { - var current = this._root; - var s = [], r = [], done = false; - - while (!done) { - if (current) { - s.push(current); - current = current.left; - } else { - if (s.length > 0) { - current = s.pop(); - r.push(current.key); - current = current.right; - } else done = true; - } - } - return r; - } - - - /** - * Returns `data` fields of all nodes in order. - * @return {Array} - */ - values () { - var current = this._root; - var s = [], r = [], done = false; - - while (!done) { - if (current) { - s.push(current); - current = current.left; - } else { - if (s.length > 0) { - current = s.pop(); - r.push(current.data); - current = current.right; - } else done = true; - } - } - return r; - } - - - /** - * Returns node at given index - * @param {number} index - * @return {?Node} - */ - at (index) { - // removed after a consideration, more misleading than useful - // index = index % this.size; - // if (index < 0) index = this.size - index; - - var current = this._root; - var s = [], done = false, i = 0; - - while (!done) { - if (current) { - s.push(current); - current = current.left; - } else { - if (s.length > 0) { - current = s.pop(); - if (i === index) return current; - i++; - current = current.right; - } else done = true; - } - } - return null; - } - - /** - * Bulk-load items. Both array have to be same size - * @param {Array} keys - * @param {Array} [values] - * @param {Boolean} [presort=false] Pre-sort keys and values, using - * tree's comparator. Sorting is done - * in-place - * @return {AVLTree} - */ - load(keys = [], values = [], presort = false) { - if (this._size !== 0) throw new Error('bulk-load: tree is not empty'); - const size = keys.length; - if (presort) sort(keys, values, 0, size - 1, this._compare); - this._root = loadRecursive(null, keys, values, 0, size); - this._size = size; - return this; - } - - - min() { - var node = this.minNode(this._root); - if (node) return node.key; - else return null; - } - - - max() { - var node = this.maxNode(this._root); - if (node) return node.key; - else return null; - } - - isEmpty() { return this._root === null; } - get size() { return this._size; } - - - /** - * Create a tree and load it with items - * @param {Array} keys - * @param {Array?} [values] - - * @param {Function?} [comparator] - * @param {Boolean?} [presort=false] Pre-sort keys and values, using - * tree's comparator. Sorting is done - * in-place - * @param {Boolean?} [noDuplicates=false] Allow duplicates - * @return {SplayTree} - */ - static createTree(keys, values, comparator, presort, noDuplicates) { - return new SplayTree(comparator, noDuplicates).load(keys, values, presort); - } - } - - - function loadRecursive (parent, keys, values, start, end) { - const size = end - start; - if (size > 0) { - const middle = start + Math.floor(size / 2); - const key = keys[middle]; - const data = values[middle]; - const node = { key, data, parent }; - node.left = loadRecursive(node, keys, values, start, middle); - node.right = loadRecursive(node, keys, values, middle + 1, end); - return node; - } - return null; - } - - - function sort(keys, values, left, right, compare) { - if (left >= right) return; - - const pivot = keys[(left + right) >> 1]; - let i = left - 1; - let j = right + 1; - - while (true) { - do i++; while (compare(keys[i], pivot) < 0); - do j--; while (compare(keys[j], pivot) > 0); - if (i >= j) break; - - let tmp = keys[i]; - keys[i] = keys[j]; - keys[j] = tmp; - - tmp = values[i]; - values[i] = values[j]; - values[j] = tmp; - } - - sort(keys, values, left, j, compare); - sort(keys, values, j + 1, right, compare); - } - - const NORMAL = 0; - const NON_CONTRIBUTING = 1; - const SAME_TRANSITION = 2; - const DIFFERENT_TRANSITION = 3; - - const INTERSECTION = 0; - const UNION = 1; - const DIFFERENCE = 2; - const XOR = 3; - - /** - * @param {SweepEvent} event - * @param {SweepEvent} prev - * @param {Operation} operation - */ - function computeFields (event, prev, operation) { - // compute inOut and otherInOut fields - if (prev === null) { - event.inOut = false; - event.otherInOut = true; - - // previous line segment in sweepline belongs to the same polygon - } else { - if (event.isSubject === prev.isSubject) { - event.inOut = !prev.inOut; - event.otherInOut = prev.otherInOut; - - // previous line segment in sweepline belongs to the clipping polygon - } else { - event.inOut = !prev.otherInOut; - event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut; - } - - // compute prevInResult field - if (prev) { - event.prevInResult = (!inResult(prev, operation) || prev.isVertical()) - ? prev.prevInResult : prev; - } - } - - // check if the line segment belongs to the Boolean operation - let isInResult = inResult(event, operation); - if (isInResult) { - event.resultTransition = determineResultTransition(event, operation); - } else { - event.resultTransition = 0; - } - } - - - /* eslint-disable indent */ - function inResult(event, operation) { - switch (event.type) { - case NORMAL: - switch (operation) { - case INTERSECTION: - return !event.otherInOut; - case UNION: - return event.otherInOut; - case DIFFERENCE: - // return (event.isSubject && !event.otherInOut) || - // (!event.isSubject && event.otherInOut); - return (event.isSubject && event.otherInOut) || - (!event.isSubject && !event.otherInOut); - case XOR: - return true; - } - break; - case SAME_TRANSITION: - return operation === INTERSECTION || operation === UNION; - case DIFFERENT_TRANSITION: - return operation === DIFFERENCE; - case NON_CONTRIBUTING: - return false; - } - return false; - } - /* eslint-enable indent */ - - - function determineResultTransition(event, operation) { - let thisIn = !event.inOut; - let thatIn = !event.otherInOut; - - let isIn; - switch (operation) { - case INTERSECTION: - isIn = thisIn && thatIn; break; - case UNION: - isIn = thisIn || thatIn; break; - case XOR: - isIn = thisIn ^ thatIn; break; - case DIFFERENCE: - if (event.isSubject) { - isIn = thisIn && !thatIn; - } else { - isIn = thatIn && !thisIn; - } - break; - } - return isIn ? +1 : -1; - } - - class SweepEvent { - - - /** - * Sweepline event - * - * @class {SweepEvent} - * @param {Array.} point - * @param {Boolean} left - * @param {SweepEvent=} otherEvent - * @param {Boolean} isSubject - * @param {Number} edgeType - */ - constructor (point, left, otherEvent, isSubject, edgeType) { - - /** - * Is left endpoint? - * @type {Boolean} - */ - this.left = left; - - /** - * @type {Array.} - */ - this.point = point; - - /** - * Other edge reference - * @type {SweepEvent} - */ - this.otherEvent = otherEvent; - - /** - * Belongs to source or clipping polygon - * @type {Boolean} - */ - this.isSubject = isSubject; - - /** - * Edge contribution type - * @type {Number} - */ - this.type = edgeType || NORMAL; - - - /** - * In-out transition for the sweepline crossing polygon - * @type {Boolean} - */ - this.inOut = false; - - - /** - * @type {Boolean} - */ - this.otherInOut = false; - - /** - * Previous event in result? - * @type {SweepEvent} - */ - this.prevInResult = null; - - /** - * Type of result transition (0 = not in result, +1 = out-in, -1, in-out) - * @type {Number} - */ - this.resultTransition = 0; - - // connection step - - /** - * @type {Number} - */ - this.otherPos = -1; - - /** - * @type {Number} - */ - this.outputContourId = -1; - - this.isExteriorRing = true; // TODO: Looks unused, remove? - } - - - /** - * @param {Array.} p - * @return {Boolean} - */ - isBelow (p) { - const p0 = this.point, p1 = this.otherEvent.point; - return this.left - ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0 - // signedArea(this.point, this.otherEvent.point, p) > 0 : - : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0; - //signedArea(this.otherEvent.point, this.point, p) > 0; - } - - - /** - * @param {Array.} p - * @return {Boolean} - */ - isAbove (p) { - return !this.isBelow(p); - } - - - /** - * @return {Boolean} - */ - isVertical () { - return this.point[0] === this.otherEvent.point[0]; - } - - - /** - * Does event belong to result? - * @return {Boolean} - */ - get inResult() { - return this.resultTransition !== 0; - } - - - clone () { - const copy = new SweepEvent( - this.point, this.left, this.otherEvent, this.isSubject, this.type); - - copy.contourId = this.contourId; - copy.resultTransition = this.resultTransition; - copy.prevInResult = this.prevInResult; - copy.isExteriorRing = this.isExteriorRing; - copy.inOut = this.inOut; - copy.otherInOut = this.otherInOut; - - return copy; - } - } - - function equals(p1, p2) { - if (p1[0] === p2[0]) { - if (p1[1] === p2[1]) { - return true; - } else { - return false; - } - } - return false; - } - - // const EPSILON = 1e-9; - // const abs = Math.abs; - // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164 - // Precision problem. - // - // module.exports = function equals(p1, p2) { - // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON; - // }; - - const epsilon$1 = 1.1102230246251565e-16; - const splitter = 134217729; - const resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; - - // fast_expansion_sum_zeroelim routine from oritinal code - function sum$1(elen, e, flen, f, h) { - let Q, Qnew, hh, bvirt; - let enow = e[0]; - let fnow = f[0]; - let eindex = 0; - let findex = 0; - if ((fnow > enow) === (fnow > -enow)) { - Q = enow; - enow = e[++eindex]; - } else { - Q = fnow; - fnow = f[++findex]; - } - let hindex = 0; - if (eindex < elen && findex < flen) { - if ((fnow > enow) === (fnow > -enow)) { - Qnew = enow + Q; - hh = Q - (Qnew - enow); - enow = e[++eindex]; - } else { - Qnew = fnow + Q; - hh = Q - (Qnew - fnow); - fnow = f[++findex]; - } - Q = Qnew; - if (hh !== 0) { - h[hindex++] = hh; - } - while (eindex < elen && findex < flen) { - if ((fnow > enow) === (fnow > -enow)) { - Qnew = Q + enow; - bvirt = Qnew - Q; - hh = Q - (Qnew - bvirt) + (enow - bvirt); - enow = e[++eindex]; - } else { - Qnew = Q + fnow; - bvirt = Qnew - Q; - hh = Q - (Qnew - bvirt) + (fnow - bvirt); - fnow = f[++findex]; - } - Q = Qnew; - if (hh !== 0) { - h[hindex++] = hh; - } - } - } - while (eindex < elen) { - Qnew = Q + enow; - bvirt = Qnew - Q; - hh = Q - (Qnew - bvirt) + (enow - bvirt); - enow = e[++eindex]; - Q = Qnew; - if (hh !== 0) { - h[hindex++] = hh; - } - } - while (findex < flen) { - Qnew = Q + fnow; - bvirt = Qnew - Q; - hh = Q - (Qnew - bvirt) + (fnow - bvirt); - fnow = f[++findex]; - Q = Qnew; - if (hh !== 0) { - h[hindex++] = hh; - } - } - if (Q !== 0 || hindex === 0) { - h[hindex++] = Q; - } - return hindex; - } - - function estimate(elen, e) { - let Q = e[0]; - for (let i = 1; i < elen; i++) Q += e[i]; - return Q; - } - - function vec(n) { - return new Float64Array(n); - } - - const ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1; - const ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1; - const ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1; - - const B = vec(4); - const C1 = vec(8); - const C2 = vec(12); - const D = vec(16); - const u = vec(4); - - function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) { - let acxtail, acytail, bcxtail, bcytail; - let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3; - - const acx = ax - cx; - const bcx = bx - cx; - const acy = ay - cy; - const bcy = by - cy; - - s1 = acx * bcy; - c = splitter * acx; - ahi = c - (c - acx); - alo = acx - ahi; - c = splitter * bcy; - bhi = c - (c - bcy); - blo = bcy - bhi; - s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); - t1 = acy * bcx; - c = splitter * acy; - ahi = c - (c - acy); - alo = acy - ahi; - c = splitter * bcx; - bhi = c - (c - bcx); - blo = bcx - bhi; - t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); - _i = s0 - t0; - bvirt = s0 - _i; - B[0] = s0 - (_i + bvirt) + (bvirt - t0); - _j = s1 + _i; - bvirt = _j - s1; - _0 = s1 - (_j - bvirt) + (_i - bvirt); - _i = _0 - t1; - bvirt = _0 - _i; - B[1] = _0 - (_i + bvirt) + (bvirt - t1); - u3 = _j + _i; - bvirt = u3 - _j; - B[2] = _j - (u3 - bvirt) + (_i - bvirt); - B[3] = u3; - - let det = estimate(4, B); - let errbound = ccwerrboundB * detsum; - if (det >= errbound || -det >= errbound) { - return det; - } - - bvirt = ax - acx; - acxtail = ax - (acx + bvirt) + (bvirt - cx); - bvirt = bx - bcx; - bcxtail = bx - (bcx + bvirt) + (bvirt - cx); - bvirt = ay - acy; - acytail = ay - (acy + bvirt) + (bvirt - cy); - bvirt = by - bcy; - bcytail = by - (bcy + bvirt) + (bvirt - cy); - - if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) { - return det; - } - - errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det); - det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); - if (det >= errbound || -det >= errbound) return det; - - s1 = acxtail * bcy; - c = splitter * acxtail; - ahi = c - (c - acxtail); - alo = acxtail - ahi; - c = splitter * bcy; - bhi = c - (c - bcy); - blo = bcy - bhi; - s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); - t1 = acytail * bcx; - c = splitter * acytail; - ahi = c - (c - acytail); - alo = acytail - ahi; - c = splitter * bcx; - bhi = c - (c - bcx); - blo = bcx - bhi; - t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); - _i = s0 - t0; - bvirt = s0 - _i; - u[0] = s0 - (_i + bvirt) + (bvirt - t0); - _j = s1 + _i; - bvirt = _j - s1; - _0 = s1 - (_j - bvirt) + (_i - bvirt); - _i = _0 - t1; - bvirt = _0 - _i; - u[1] = _0 - (_i + bvirt) + (bvirt - t1); - u3 = _j + _i; - bvirt = u3 - _j; - u[2] = _j - (u3 - bvirt) + (_i - bvirt); - u[3] = u3; - const C1len = sum$1(4, B, 4, u, C1); - - s1 = acx * bcytail; - c = splitter * acx; - ahi = c - (c - acx); - alo = acx - ahi; - c = splitter * bcytail; - bhi = c - (c - bcytail); - blo = bcytail - bhi; - s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); - t1 = acy * bcxtail; - c = splitter * acy; - ahi = c - (c - acy); - alo = acy - ahi; - c = splitter * bcxtail; - bhi = c - (c - bcxtail); - blo = bcxtail - bhi; - t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); - _i = s0 - t0; - bvirt = s0 - _i; - u[0] = s0 - (_i + bvirt) + (bvirt - t0); - _j = s1 + _i; - bvirt = _j - s1; - _0 = s1 - (_j - bvirt) + (_i - bvirt); - _i = _0 - t1; - bvirt = _0 - _i; - u[1] = _0 - (_i + bvirt) + (bvirt - t1); - u3 = _j + _i; - bvirt = u3 - _j; - u[2] = _j - (u3 - bvirt) + (_i - bvirt); - u[3] = u3; - const C2len = sum$1(C1len, C1, 4, u, C2); - - s1 = acxtail * bcytail; - c = splitter * acxtail; - ahi = c - (c - acxtail); - alo = acxtail - ahi; - c = splitter * bcytail; - bhi = c - (c - bcytail); - blo = bcytail - bhi; - s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo); - t1 = acytail * bcxtail; - c = splitter * acytail; - ahi = c - (c - acytail); - alo = acytail - ahi; - c = splitter * bcxtail; - bhi = c - (c - bcxtail); - blo = bcxtail - bhi; - t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo); - _i = s0 - t0; - bvirt = s0 - _i; - u[0] = s0 - (_i + bvirt) + (bvirt - t0); - _j = s1 + _i; - bvirt = _j - s1; - _0 = s1 - (_j - bvirt) + (_i - bvirt); - _i = _0 - t1; - bvirt = _0 - _i; - u[1] = _0 - (_i + bvirt) + (bvirt - t1); - u3 = _j + _i; - bvirt = u3 - _j; - u[2] = _j - (u3 - bvirt) + (_i - bvirt); - u[3] = u3; - const Dlen = sum$1(C2len, C2, 4, u, D); - - return D[Dlen - 1]; - } - - function orient2d(ax, ay, bx, by, cx, cy) { - const detleft = (ay - cy) * (bx - cx); - const detright = (ax - cx) * (by - cy); - const det = detleft - detright; - - if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) return det; - - const detsum = Math.abs(detleft + detright); - if (Math.abs(det) >= ccwerrboundA * detsum) return det; - - return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum); - } - - /** - * Signed area of the triangle (p0, p1, p2) - * @param {Array.} p0 - * @param {Array.} p1 - * @param {Array.} p2 - * @return {Number} - */ - function signedArea(p0, p1, p2) { - const res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]); - if (res > 0) return -1; - if (res < 0) return 1; - return 0; - } - - /** - * @param {SweepEvent} e1 - * @param {SweepEvent} e2 - * @return {Number} - */ - function compareEvents(e1, e2) { - const p1 = e1.point; - const p2 = e2.point; - - // Different x-coordinate - if (p1[0] > p2[0]) return 1; - if (p1[0] < p2[0]) return -1; - - // Different points, but same x-coordinate - // Event with lower y-coordinate is processed first - if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1; - - return specialCases(e1, e2, p1); - } - - - /* eslint-disable no-unused-vars */ - function specialCases(e1, e2, p1, p2) { - // Same coordinates, but one is a left endpoint and the other is - // a right endpoint. The right endpoint is processed first - if (e1.left !== e2.left) - return e1.left ? 1 : -1; - - // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point; - // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]) - // Same coordinates, both events - // are left endpoints or right endpoints. - // not collinear - if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) { - // the event associate to the bottom segment is processed first - return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1; - } - - return (!e1.isSubject && e2.isSubject) ? 1 : -1; - } - /* eslint-enable no-unused-vars */ - - /** - * @param {SweepEvent} se - * @param {Array.} p - * @param {Queue} queue - * @return {Queue} - */ - function divideSegment(se, p, queue) { - const r = new SweepEvent(p, false, se, se.isSubject); - const l = new SweepEvent(p, true, se.otherEvent, se.isSubject); - - /* eslint-disable no-console */ - if (equals(se.point, se.otherEvent.point)) { - console.warn('what is that, a collapsed segment?', se); - } - /* eslint-enable no-console */ - - r.contourId = l.contourId = se.contourId; - - // avoid a rounding error. The left event would be processed after the right event - if (compareEvents(l, se.otherEvent) > 0) { - se.otherEvent.left = true; - l.left = false; - } - - // avoid a rounding error. The left event would be processed after the right event - // if (compareEvents(se, r) > 0) {} - - se.otherEvent.otherEvent = l; - se.otherEvent = r; - - queue.push(l); - queue.push(r); - - return queue; - } - - //const EPS = 1e-9; - - /** - * Finds the magnitude of the cross product of two vectors (if we pretend - * they're in three dimensions) - * - * @param {Object} a First vector - * @param {Object} b Second vector - * @private - * @returns {Number} The magnitude of the cross product - */ - function crossProduct(a, b) { - return (a[0] * b[1]) - (a[1] * b[0]); - } - - /** - * Finds the dot product of two vectors. - * - * @param {Object} a First vector - * @param {Object} b Second vector - * @private - * @returns {Number} The dot product - */ - function dotProduct(a, b) { - return (a[0] * b[0]) + (a[1] * b[1]); - } - - /** - * Finds the intersection (if any) between two line segments a and b, given the - * line segments' end points a1, a2 and b1, b2. - * - * This algorithm is based on Schneider and Eberly. - * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - * Page 244. - * - * @param {Array.} a1 point of first line - * @param {Array.} a2 point of first line - * @param {Array.} b1 point of second line - * @param {Array.} b2 point of second line - * @param {Boolean=} noEndpointTouch whether to skip single touchpoints - * (meaning connected segments) as - * intersections - * @returns {Array.>|Null} If the lines intersect, the point of - * intersection. If they overlap, the two end points of the overlapping segment. - * Otherwise, null. - */ - function intersection (a1, a2, b1, b2, noEndpointTouch) { - // The algorithm expects our lines in the form P + sd, where P is a point, - // s is on the interval [0, 1], and d is a vector. - // We are passed two points. P can be the first point of each pair. The - // vector, then, could be thought of as the distance (in x and y components) - // from the first point to the second point. - // So first, let's make our vectors: - const va = [a2[0] - a1[0], a2[1] - a1[1]]; - const vb = [b2[0] - b1[0], b2[1] - b1[1]]; - // We also define a function to convert back to regular point form: - - /* eslint-disable arrow-body-style */ - - function toPoint(p, s, d) { - return [ - p[0] + s * d[0], - p[1] + s * d[1] - ]; - } - - /* eslint-enable arrow-body-style */ - - // The rest is pretty much a straight port of the algorithm. - const e = [b1[0] - a1[0], b1[1] - a1[1]]; - let kross = crossProduct(va, vb); - let sqrKross = kross * kross; - const sqrLenA = dotProduct(va, va); - //const sqrLenB = dotProduct(vb, vb); - - // Check for line intersection. This works because of the properties of the - // cross product -- specifically, two vectors are parallel if and only if the - // cross product is the 0 vector. The full calculation involves relative error - // to account for possible very small line segments. See Schneider & Eberly - // for details. - if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) { - // If they're not parallel, then (because these are line segments) they - // still might not actually intersect. This code checks that the - // intersection point of the lines is actually on both line segments. - const s = crossProduct(e, vb) / kross; - if (s < 0 || s > 1) { - // not on line segment a - return null; - } - const t = crossProduct(e, va) / kross; - if (t < 0 || t > 1) { - // not on line segment b - return null; - } - if (s === 0 || s === 1) { - // on an endpoint of line segment a - return noEndpointTouch ? null : [toPoint(a1, s, va)]; - } - if (t === 0 || t === 1) { - // on an endpoint of line segment b - return noEndpointTouch ? null : [toPoint(b1, t, vb)]; - } - return [toPoint(a1, s, va)]; - } - - // If we've reached this point, then the lines are either parallel or the - // same, but the segments could overlap partially or fully, or not at all. - // So we need to find the overlap, if any. To do that, we can use e, which is - // the (vector) difference between the two initial points. If this is parallel - // with the line itself, then the two lines are the same line, and there will - // be overlap. - //const sqrLenE = dotProduct(e, e); - kross = crossProduct(e, va); - sqrKross = kross * kross; - - if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) { - // Lines are just parallel, not the same. No overlap. - return null; - } - - const sa = dotProduct(va, e) / sqrLenA; - const sb = sa + dotProduct(va, vb) / sqrLenA; - const smin = Math.min(sa, sb); - const smax = Math.max(sa, sb); - - // this is, essentially, the FindIntersection acting on floats from - // Schneider & Eberly, just inlined into this function. - if (smin <= 1 && smax >= 0) { - - // overlap on an end point - if (smin === 1) { - return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)]; - } - - if (smax === 0) { - return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)]; - } - - if (noEndpointTouch && smin === 0 && smax === 1) return null; - - // There's overlap on a segment -- two points of intersection. Return both. - return [ - toPoint(a1, smin > 0 ? smin : 0, va), - toPoint(a1, smax < 1 ? smax : 1, va) - ]; - } - - return null; - } - - /** - * @param {SweepEvent} se1 - * @param {SweepEvent} se2 - * @param {Queue} queue - * @return {Number} - */ - function possibleIntersection (se1, se2, queue) { - // that disallows self-intersecting polygons, - // did cost us half a day, so I'll leave it - // out of respect - // if (se1.isSubject === se2.isSubject) return; - const inter = intersection( - se1.point, se1.otherEvent.point, - se2.point, se2.otherEvent.point - ); - - const nintersections = inter ? inter.length : 0; - if (nintersections === 0) return 0; // no intersection - - // the line segments intersect at an endpoint of both line segments - if ((nintersections === 1) && - (equals(se1.point, se2.point) || - equals(se1.otherEvent.point, se2.otherEvent.point))) { - return 0; - } - - if (nintersections === 2 && se1.isSubject === se2.isSubject) { - // if(se1.contourId === se2.contourId){ - // console.warn('Edges of the same polygon overlap', - // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point); - // } - //throw new Error('Edges of the same polygon overlap'); - return 0; - } - - // The line segments associated to se1 and se2 intersect - if (nintersections === 1) { - - // if the intersection point is not an endpoint of se1 - if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) { - divideSegment(se1, inter[0], queue); - } - - // if the intersection point is not an endpoint of se2 - if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) { - divideSegment(se2, inter[0], queue); - } - return 1; - } - - // The line segments associated to se1 and se2 overlap - const events = []; - let leftCoincide = false; - let rightCoincide = false; - - if (equals(se1.point, se2.point)) { - leftCoincide = true; // linked - } else if (compareEvents(se1, se2) === 1) { - events.push(se2, se1); - } else { - events.push(se1, se2); - } - - if (equals(se1.otherEvent.point, se2.otherEvent.point)) { - rightCoincide = true; - } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) { - events.push(se2.otherEvent, se1.otherEvent); - } else { - events.push(se1.otherEvent, se2.otherEvent); - } - - if ((leftCoincide && rightCoincide) || leftCoincide) { - // both line segments are equal or share the left endpoint - se2.type = NON_CONTRIBUTING; - se1.type = (se2.inOut === se1.inOut) - ? SAME_TRANSITION : DIFFERENT_TRANSITION; - - if (leftCoincide && !rightCoincide) { - // honestly no idea, but changing events selection from [2, 1] - // to [0, 1] fixes the overlapping self-intersecting polygons issue - divideSegment(events[1].otherEvent, events[0].point, queue); - } - return 2; - } - - // the line segments share the right endpoint - if (rightCoincide) { - divideSegment(events[0], events[1].point, queue); - return 3; - } - - // no line segment includes totally the other one - if (events[0] !== events[3].otherEvent) { - divideSegment(events[0], events[1].point, queue); - divideSegment(events[1], events[2].point, queue); - return 3; - } - - // one line segment includes the other one - divideSegment(events[0], events[1].point, queue); - divideSegment(events[3].otherEvent, events[2].point, queue); - - return 3; - } - - /** - * @param {SweepEvent} le1 - * @param {SweepEvent} le2 - * @return {Number} - */ - function compareSegments(le1, le2) { - if (le1 === le2) return 0; - - // Segments are not collinear - if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || - signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) { - - // If they share their left endpoint use the right endpoint to sort - if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; - - // Different left endpoint: use the left endpoint to sort - if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1; - - // has the line segment associated to e1 been inserted - // into S after the line segment associated to e2 ? - if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; - - // The line segment associated to e2 has been inserted - // into S after the line segment associated to e1 - return le1.isBelow(le2.point) ? -1 : 1; - } - - if (le1.isSubject === le2.isSubject) { // same polygon - let p1 = le1.point, p2 = le2.point; - if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) { - p1 = le1.otherEvent.point; p2 = le2.otherEvent.point; - if (p1[0] === p2[0] && p1[1] === p2[1]) return 0; - else return le1.contourId > le2.contourId ? 1 : -1; - } - } else { // Segments are collinear, but belong to separate polygons - return le1.isSubject ? -1 : 1; - } - - return compareEvents(le1, le2) === 1 ? 1 : -1; - } - - function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) { - const sweepLine = new SplayTree(compareSegments); - const sortedEvents = []; - - const rightbound = Math.min(sbbox[2], cbbox[2]); - - let prev, next, begin; - - while (eventQueue.length !== 0) { - let event = eventQueue.pop(); - sortedEvents.push(event); - - // optimization by bboxes for intersection and difference goes here - if ((operation === INTERSECTION && event.point[0] > rightbound) || - (operation === DIFFERENCE && event.point[0] > sbbox[2])) { - break; - } - - if (event.left) { - next = prev = sweepLine.insert(event); - begin = sweepLine.minNode(); - - if (prev !== begin) prev = sweepLine.prev(prev); - else prev = null; - - next = sweepLine.next(next); - - const prevEvent = prev ? prev.key : null; - let prevprevEvent; - computeFields(event, prevEvent, operation); - if (next) { - if (possibleIntersection(event, next.key, eventQueue) === 2) { - computeFields(event, prevEvent, operation); - computeFields(event, next.key, operation); - } - } - - if (prev) { - if (possibleIntersection(prev.key, event, eventQueue) === 2) { - let prevprev = prev; - if (prevprev !== begin) prevprev = sweepLine.prev(prevprev); - else prevprev = null; - - prevprevEvent = prevprev ? prevprev.key : null; - computeFields(prevEvent, prevprevEvent, operation); - computeFields(event, prevEvent, operation); - } - } - } else { - event = event.otherEvent; - next = prev = sweepLine.find(event); - - if (prev && next) { - - if (prev !== begin) prev = sweepLine.prev(prev); - else prev = null; - - next = sweepLine.next(next); - sweepLine.remove(event); - - if (next && prev) { - possibleIntersection(prev.key, next.key, eventQueue); - } - } - } - } - return sortedEvents; - } - - class Contour { - - /** - * Contour - * - * @class {Contour} - */ - constructor() { - this.points = []; - this.holeIds = []; - this.holeOf = null; - this.depth = null; - } - - isExterior() { - return this.holeOf == null; - } - - } - - /** - * @param {Array.} sortedEvents - * @return {Array.} - */ - function orderEvents(sortedEvents) { - let event, i, len, tmp; - const resultEvents = []; - for (i = 0, len = sortedEvents.length; i < len; i++) { - event = sortedEvents[i]; - if ((event.left && event.inResult) || - (!event.left && event.otherEvent.inResult)) { - resultEvents.push(event); - } - } - // Due to overlapping edges the resultEvents array can be not wholly sorted - let sorted = false; - while (!sorted) { - sorted = true; - for (i = 0, len = resultEvents.length; i < len; i++) { - if ((i + 1) < len && - compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) { - tmp = resultEvents[i]; - resultEvents[i] = resultEvents[i + 1]; - resultEvents[i + 1] = tmp; - sorted = false; - } - } - } - - - for (i = 0, len = resultEvents.length; i < len; i++) { - event = resultEvents[i]; - event.otherPos = i; - } - - // imagine, the right event is found in the beginning of the queue, - // when his left counterpart is not marked yet - for (i = 0, len = resultEvents.length; i < len; i++) { - event = resultEvents[i]; - if (!event.left) { - tmp = event.otherPos; - event.otherPos = event.otherEvent.otherPos; - event.otherEvent.otherPos = tmp; - } - } - - return resultEvents; - } - - - /** - * @param {Number} pos - * @param {Array.} resultEvents - * @param {Object>} processed - * @return {Number} - */ - function nextPos(pos, resultEvents, processed, origPos) { - let newPos = pos + 1, - p = resultEvents[pos].point, - p1; - const length = resultEvents.length; - - if (newPos < length) - p1 = resultEvents[newPos].point; - - while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) { - if (!processed[newPos]) { - return newPos; - } else { - newPos++; - } - p1 = resultEvents[newPos].point; - } - - newPos = pos - 1; - - while (processed[newPos] && newPos > origPos) { - newPos--; - } - - return newPos; - } - - - function initializeContourFromContext(event, contours, contourId) { - const contour = new Contour(); - if (event.prevInResult != null) { - const prevInResult = event.prevInResult; - // Note that it is valid to query the "previous in result" for its output contour id, - // because we must have already processed it (i.e., assigned an output contour id) - // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in - // result". - const lowerContourId = prevInResult.outputContourId; - const lowerResultTransition = prevInResult.resultTransition; - if (lowerResultTransition > 0) { - // We are inside. Now we have to check if the thing below us is another hole or - // an exterior contour. - const lowerContour = contours[lowerContourId]; - if (lowerContour.holeOf != null) { - // The lower contour is a hole => Connect the new contour as a hole to its parent, - // and use same depth. - const parentContourId = lowerContour.holeOf; - contours[parentContourId].holeIds.push(contourId); - contour.holeOf = parentContourId; - contour.depth = contours[lowerContourId].depth; - } else { - // The lower contour is an exterior contour => Connect the new contour as a hole, - // and increment depth. - contours[lowerContourId].holeIds.push(contourId); - contour.holeOf = lowerContourId; - contour.depth = contours[lowerContourId].depth + 1; - } - } else { - // We are outside => this contour is an exterior contour of same depth. - contour.holeOf = null; - contour.depth = contours[lowerContourId].depth; - } - } else { - // There is no lower/previous contour => this contour is an exterior contour of depth 0. - contour.holeOf = null; - contour.depth = 0; - } - return contour; - } - - /** - * @param {Array.} sortedEvents - * @return {Array.<*>} polygons - */ - function connectEdges(sortedEvents) { - let i, len; - const resultEvents = orderEvents(sortedEvents); - - // "false"-filled array - const processed = {}; - const contours = []; - - for (i = 0, len = resultEvents.length; i < len; i++) { - - if (processed[i]) { - continue; - } - - const contourId = contours.length; - const contour = initializeContourFromContext(resultEvents[i], contours, contourId); - - // Helper function that combines marking an event as processed with assigning its output contour ID - const markAsProcessed = (pos) => { - processed[pos] = true; - resultEvents[pos].outputContourId = contourId; - }; - - let pos = i; - let origPos = i; - - const initial = resultEvents[i].point; - contour.points.push(initial); - - /* eslint no-constant-condition: "off" */ - while (true) { - markAsProcessed(pos); - - pos = resultEvents[pos].otherPos; - - markAsProcessed(pos); - contour.points.push(resultEvents[pos].point); - - pos = nextPos(pos, resultEvents, processed, origPos); - - if (pos == origPos) { - break; - } - } - - contours.push(contour); - } - - return contours; - } - - var tinyqueue = TinyQueue; - var _default$1 = TinyQueue; - - function TinyQueue(data, compare) { - if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare); - - this.data = data || []; - this.length = this.data.length; - this.compare = compare || defaultCompare$1; - - if (this.length > 0) { - for (var i = (this.length >> 1) - 1; i >= 0; i--) this._down(i); - } - } - - function defaultCompare$1(a, b) { - return a < b ? -1 : a > b ? 1 : 0; - } - - TinyQueue.prototype = { - - push: function (item) { - this.data.push(item); - this.length++; - this._up(this.length - 1); - }, - - pop: function () { - if (this.length === 0) return undefined; - - var top = this.data[0]; - this.length--; - - if (this.length > 0) { - this.data[0] = this.data[this.length]; - this._down(0); - } - this.data.pop(); - - return top; - }, - - peek: function () { - return this.data[0]; - }, - - _up: function (pos) { - var data = this.data; - var compare = this.compare; - var item = data[pos]; - - while (pos > 0) { - var parent = (pos - 1) >> 1; - var current = data[parent]; - if (compare(item, current) >= 0) break; - data[pos] = current; - pos = parent; - } - - data[pos] = item; - }, - - _down: function (pos) { - var data = this.data; - var compare = this.compare; - var halfLength = this.length >> 1; - var item = data[pos]; - - while (pos < halfLength) { - var left = (pos << 1) + 1; - var right = left + 1; - var best = data[left]; - - if (right < this.length && compare(data[right], best) < 0) { - left = right; - best = data[right]; - } - if (compare(best, item) >= 0) break; - - data[pos] = best; - pos = left; - } - - data[pos] = item; - } - }; - tinyqueue.default = _default$1; - - const max$2 = Math.max; - const min = Math.min; - - let contourId = 0; - - - function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) { - let i, len, s1, s2, e1, e2; - for (i = 0, len = contourOrHole.length - 1; i < len; i++) { - s1 = contourOrHole[i]; - s2 = contourOrHole[i + 1]; - e1 = new SweepEvent(s1, false, undefined, isSubject); - e2 = new SweepEvent(s2, false, e1, isSubject); - e1.otherEvent = e2; - - if (s1[0] === s2[0] && s1[1] === s2[1]) { - continue; // skip collapsed edges, or it breaks - } - - e1.contourId = e2.contourId = depth; - if (!isExteriorRing) { - e1.isExteriorRing = false; - e2.isExteriorRing = false; - } - if (compareEvents(e1, e2) > 0) { - e2.left = true; - } else { - e1.left = true; - } - - const x = s1[0], y = s1[1]; - bbox[0] = min(bbox[0], x); - bbox[1] = min(bbox[1], y); - bbox[2] = max$2(bbox[2], x); - bbox[3] = max$2(bbox[3], y); - - // Pushing it so the queue is sorted from left to right, - // with object on the left having the highest priority. - Q.push(e1); - Q.push(e2); - } - } - - - function fillQueue(subject, clipping, sbbox, cbbox, operation) { - const eventQueue = new tinyqueue(null, compareEvents); - let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk; - - for (i = 0, ii = subject.length; i < ii; i++) { - polygonSet = subject[i]; - for (j = 0, jj = polygonSet.length; j < jj; j++) { - isExteriorRing = j === 0; - if (isExteriorRing) contourId++; - processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing); - } - } - - for (i = 0, ii = clipping.length; i < ii; i++) { - polygonSet = clipping[i]; - for (j = 0, jj = polygonSet.length; j < jj; j++) { - isExteriorRing = j === 0; - if (operation === DIFFERENCE) isExteriorRing = false; - if (isExteriorRing) contourId++; - processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing); - } - } - - return eventQueue; - } - - const EMPTY = []; - - - function trivialOperation(subject, clipping, operation) { - let result = null; - if (subject.length * clipping.length === 0) { - if (operation === INTERSECTION) { - result = EMPTY; - } else if (operation === DIFFERENCE) { - result = subject; - } else if (operation === UNION || - operation === XOR) { - result = (subject.length === 0) ? clipping : subject; - } - } - return result; - } - - - function compareBBoxes(subject, clipping, sbbox, cbbox, operation) { - let result = null; - if (sbbox[0] > cbbox[2] || - cbbox[0] > sbbox[2] || - sbbox[1] > cbbox[3] || - cbbox[1] > sbbox[3]) { - if (operation === INTERSECTION) { - result = EMPTY; - } else if (operation === DIFFERENCE) { - result = subject; - } else if (operation === UNION || - operation === XOR) { - result = subject.concat(clipping); - } - } - return result; - } - - - function boolean(subject, clipping, operation) { - if (typeof subject[0][0][0] === 'number') { - subject = [subject]; - } - if (typeof clipping[0][0][0] === 'number') { - clipping = [clipping]; - } - let trivial = trivialOperation(subject, clipping, operation); - if (trivial) { - return trivial === EMPTY ? null : trivial; - } - const sbbox = [Infinity, Infinity, -Infinity, -Infinity]; - const cbbox = [Infinity, Infinity, -Infinity, -Infinity]; - - // console.time('fill queue'); - const eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); - //console.timeEnd('fill queue'); - - trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation); - if (trivial) { - return trivial === EMPTY ? null : trivial; - } - // console.time('subdivide edges'); - const sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); - //console.timeEnd('subdivide edges'); - - // console.time('connect vertices'); - const contours = connectEdges(sortedEvents); - //console.timeEnd('connect vertices'); - - // Convert contours to polygons - const polygons = []; - for (let i = 0; i < contours.length; i++) { - let contour = contours[i]; - if (contour.isExterior()) { - // The exterior ring goes first - let rings = [contour.points]; - // Followed by holes if any - for (let j = 0; j < contour.holeIds.length; j++) { - let holeId = contour.holeIds[j]; - rings.push(contours[holeId].points); - } - polygons.push(rings); - } - } - - return polygons; - } - - function union (subject, clipping) { - return boolean(subject, clipping, UNION); - } - - var read$6 = function (buffer, offset, isLE, mLen, nBytes) { - var e, m; - var eLen = (nBytes * 8) - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var nBits = -7; - var i = isLE ? (nBytes - 1) : 0; - var d = isLE ? -1 : 1; - var s = buffer[offset + i]; - - i += d; - - e = s & ((1 << (-nBits)) - 1); - s >>= (-nBits); - nBits += eLen; - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1); - e >>= (-nBits); - nBits += mLen; - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias; - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen); - e = e - eBias; - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) - }; - - var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c; - var eLen = (nBytes * 8) - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0); - var i = isLE ? 0 : (nBytes - 1); - var d = isLE ? 1 : -1; - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; - - value = Math.abs(value); - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0; - e = eMax; - } else { - e = Math.floor(Math.log(value) / Math.LN2); - if (value * (c = Math.pow(2, -e)) < 1) { - e--; - c *= 2; - } - if (e + eBias >= 1) { - value += rt / c; - } else { - value += rt * Math.pow(2, 1 - eBias); - } - if (value * c >= 2) { - e++; - c /= 2; - } - - if (e + eBias >= eMax) { - m = 0; - e = eMax; - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen); - e = e + eBias; - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); - e = 0; - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m; - eLen += mLen; - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128; - }; - - var ieee754 = { - read: read$6, - write: write$6 - }; - - var pbf = Pbf; - - - - function Pbf(buf) { - this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); - this.pos = 0; - this.type = 0; - this.length = this.buf.length; - } - - Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum - Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64 - Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields - Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32 - - var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), - SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; - - // Threshold chosen based on both benchmarking and knowledge about browser string - // data structures (which currently switch structure types at 12 bytes or more) - var TEXT_DECODER_MIN_LENGTH = 12; - var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8'); - - Pbf.prototype = { - - destroy: function() { - this.buf = null; - }, - - // === READING ================================================================= - - readFields: function(readField, result, end) { - end = end || this.length; - - while (this.pos < end) { - var val = this.readVarint(), - tag = val >> 3, - startPos = this.pos; - - this.type = val & 0x7; - readField(tag, result, this); - - if (this.pos === startPos) this.skip(val); - } - return result; - }, - - readMessage: function(readField, result) { - return this.readFields(readField, result, this.readVarint() + this.pos); - }, - - readFixed32: function() { - var val = readUInt32(this.buf, this.pos); - this.pos += 4; - return val; - }, - - readSFixed32: function() { - var val = readInt32(this.buf, this.pos); - this.pos += 4; - return val; - }, - - // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed) - - readFixed64: function() { - var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; - this.pos += 8; - return val; - }, - - readSFixed64: function() { - var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32; - this.pos += 8; - return val; - }, - - readFloat: function() { - var val = ieee754.read(this.buf, this.pos, true, 23, 4); - this.pos += 4; - return val; - }, - - readDouble: function() { - var val = ieee754.read(this.buf, this.pos, true, 52, 8); - this.pos += 8; - return val; - }, - - readVarint: function(isSigned) { - var buf = this.buf, - val, b; - - b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val; - b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val; - b = buf[this.pos]; val |= (b & 0x0f) << 28; - - return readVarintRemainder(val, isSigned, this); - }, - - readVarint64: function() { // for compatibility with v2.0.1 - return this.readVarint(true); - }, - - readSVarint: function() { - var num = this.readVarint(); - return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding - }, - - readBoolean: function() { - return Boolean(this.readVarint()); - }, - - readString: function() { - var end = this.readVarint() + this.pos; - var pos = this.pos; - this.pos = end; - - if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) { - // longer strings are fast with the built-in browser TextDecoder API - return readUtf8TextDecoder(this.buf, pos, end); - } - // short strings are fast with our custom implementation - return readUtf8(this.buf, pos, end); - }, - - readBytes: function() { - var end = this.readVarint() + this.pos, - buffer = this.buf.subarray(this.pos, end); - this.pos = end; - return buffer; - }, - - // verbose for performance reasons; doesn't affect gzipped size - - readPackedVarint: function(arr, isSigned) { - if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned)); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readVarint(isSigned)); - return arr; - }, - readPackedSVarint: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSVarint()); - return arr; - }, - readPackedBoolean: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readBoolean()); - return arr; - }, - readPackedFloat: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readFloat()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFloat()); - return arr; - }, - readPackedDouble: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readDouble()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readDouble()); - return arr; - }, - readPackedFixed32: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFixed32()); - return arr; - }, - readPackedSFixed32: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSFixed32()); - return arr; - }, - readPackedFixed64: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readFixed64()); - return arr; - }, - readPackedSFixed64: function(arr) { - if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64()); - var end = readPackedEnd(this); - arr = arr || []; - while (this.pos < end) arr.push(this.readSFixed64()); - return arr; - }, - - skip: function(val) { - var type = val & 0x7; - if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} - else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; - else if (type === Pbf.Fixed32) this.pos += 4; - else if (type === Pbf.Fixed64) this.pos += 8; - else throw new Error('Unimplemented type: ' + type); - }, - - // === WRITING ================================================================= - - writeTag: function(tag, type) { - this.writeVarint((tag << 3) | type); - }, - - realloc: function(min) { - var length = this.length || 16; - - while (length < this.pos + min) length *= 2; - - if (length !== this.length) { - var buf = new Uint8Array(length); - buf.set(this.buf); - this.buf = buf; - this.length = length; - } - }, - - finish: function() { - this.length = this.pos; - this.pos = 0; - return this.buf.subarray(0, this.length); - }, - - writeFixed32: function(val) { - this.realloc(4); - writeInt32(this.buf, val, this.pos); - this.pos += 4; - }, - - writeSFixed32: function(val) { - this.realloc(4); - writeInt32(this.buf, val, this.pos); - this.pos += 4; - }, - - writeFixed64: function(val) { - this.realloc(8); - writeInt32(this.buf, val & -1, this.pos); - writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); - this.pos += 8; - }, - - writeSFixed64: function(val) { - this.realloc(8); - writeInt32(this.buf, val & -1, this.pos); - writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); - this.pos += 8; - }, - - writeVarint: function(val) { - val = +val || 0; - - if (val > 0xfffffff || val < 0) { - writeBigVarint(val, this); - return; - } - - this.realloc(4); - - this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; - this.buf[this.pos++] = (val >>> 7) & 0x7f; - }, - - writeSVarint: function(val) { - this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); - }, - - writeBoolean: function(val) { - this.writeVarint(Boolean(val)); - }, - - writeString: function(str) { - str = String(str); - this.realloc(str.length * 4); - - this.pos++; // reserve 1 byte for short string length - - var startPos = this.pos; - // write the string directly to the buffer and see how much was written - this.pos = writeUtf8(this.buf, str, this.pos); - var len = this.pos - startPos; - - if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); - - // finally, write the message length in the reserved place and restore the position - this.pos = startPos - 1; - this.writeVarint(len); - this.pos += len; - }, - - writeFloat: function(val) { - this.realloc(4); - ieee754.write(this.buf, val, this.pos, true, 23, 4); - this.pos += 4; - }, - - writeDouble: function(val) { - this.realloc(8); - ieee754.write(this.buf, val, this.pos, true, 52, 8); - this.pos += 8; - }, - - writeBytes: function(buffer) { - var len = buffer.length; - this.writeVarint(len); - this.realloc(len); - for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i]; - }, - - writeRawMessage: function(fn, obj) { - this.pos++; // reserve 1 byte for short message length - - // write the message directly to the buffer and see how much was written - var startPos = this.pos; - fn(obj, this); - var len = this.pos - startPos; - - if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); - - // finally, write the message length in the reserved place and restore the position - this.pos = startPos - 1; - this.writeVarint(len); - this.pos += len; - }, - - writeMessage: function(tag, fn, obj) { - this.writeTag(tag, Pbf.Bytes); - this.writeRawMessage(fn, obj); - }, - - writePackedVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedVarint, arr); }, - writePackedSVarint: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSVarint, arr); }, - writePackedBoolean: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedBoolean, arr); }, - writePackedFloat: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFloat, arr); }, - writePackedDouble: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedDouble, arr); }, - writePackedFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed32, arr); }, - writePackedSFixed32: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr); }, - writePackedFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedFixed64, arr); }, - writePackedSFixed64: function(tag, arr) { if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr); }, - - writeBytesField: function(tag, buffer) { - this.writeTag(tag, Pbf.Bytes); - this.writeBytes(buffer); - }, - writeFixed32Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeFixed32(val); - }, - writeSFixed32Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeSFixed32(val); - }, - writeFixed64Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeFixed64(val); - }, - writeSFixed64Field: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeSFixed64(val); - }, - writeVarintField: function(tag, val) { - this.writeTag(tag, Pbf.Varint); - this.writeVarint(val); - }, - writeSVarintField: function(tag, val) { - this.writeTag(tag, Pbf.Varint); - this.writeSVarint(val); - }, - writeStringField: function(tag, str) { - this.writeTag(tag, Pbf.Bytes); - this.writeString(str); - }, - writeFloatField: function(tag, val) { - this.writeTag(tag, Pbf.Fixed32); - this.writeFloat(val); - }, - writeDoubleField: function(tag, val) { - this.writeTag(tag, Pbf.Fixed64); - this.writeDouble(val); - }, - writeBooleanField: function(tag, val) { - this.writeVarintField(tag, Boolean(val)); - } - }; - - function readVarintRemainder(l, s, p) { - var buf = p.buf, - h, b; - - b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s); - b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s); - - throw new Error('Expected varint not more than 10 bytes'); - } - - function readPackedEnd(pbf) { - return pbf.type === Pbf.Bytes ? - pbf.readVarint() + pbf.pos : pbf.pos + 1; - } - - function toNum(low, high, isSigned) { - if (isSigned) { - return high * 0x100000000 + (low >>> 0); - } - - return ((high >>> 0) * 0x100000000) + (low >>> 0); - } - - function writeBigVarint(val, pbf) { - var low, high; - - if (val >= 0) { - low = (val % 0x100000000) | 0; - high = (val / 0x100000000) | 0; - } else { - low = ~(-val % 0x100000000); - high = ~(-val / 0x100000000); - - if (low ^ 0xffffffff) { - low = (low + 1) | 0; - } else { - low = 0; - high = (high + 1) | 0; - } - } - - if (val >= 0x10000000000000000 || val < -0x10000000000000000) { - throw new Error('Given varint doesn\'t fit into 10 bytes'); - } - - pbf.realloc(10); - - writeBigVarintLow(low, high, pbf); - writeBigVarintHigh(high, pbf); - } - - function writeBigVarintLow(low, high, pbf) { - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7; - pbf.buf[pbf.pos] = low & 0x7f; - } - - function writeBigVarintHigh(high, pbf) { - var lsb = (high & 0x07) << 4; - - pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return; - pbf.buf[pbf.pos++] = high & 0x7f; - } - - function makeRoomForExtraLength(startPos, len, pbf) { - var extraLen = - len <= 0x3fff ? 1 : - len <= 0x1fffff ? 2 : - len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7)); - - // if 1 byte isn't enough for encoding message length, shift the data to the right - pbf.realloc(extraLen); - for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i]; - } - - function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); } - function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); } - function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); } - function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); } - function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); } - function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); } - function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); } - function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); } - function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); } - - // Buffer code below from https://github.com/feross/buffer, MIT-licensed - - function readUInt32(buf, pos) { - return ((buf[pos]) | - (buf[pos + 1] << 8) | - (buf[pos + 2] << 16)) + - (buf[pos + 3] * 0x1000000); - } - - function writeInt32(buf, val, pos) { - buf[pos] = val; - buf[pos + 1] = (val >>> 8); - buf[pos + 2] = (val >>> 16); - buf[pos + 3] = (val >>> 24); - } - - function readInt32(buf, pos) { - return ((buf[pos]) | - (buf[pos + 1] << 8) | - (buf[pos + 2] << 16)) + - (buf[pos + 3] << 24); - } - - function readUtf8(buf, pos, end) { - var str = ''; - var i = pos; - - while (i < end) { - var b0 = buf[i]; - var c = null; // codepoint - var bytesPerSequence = - b0 > 0xEF ? 4 : - b0 > 0xDF ? 3 : - b0 > 0xBF ? 2 : 1; - - if (i + bytesPerSequence > end) break; - - var b1, b2, b3; - - if (bytesPerSequence === 1) { - if (b0 < 0x80) { - c = b0; - } - } else if (bytesPerSequence === 2) { - b1 = buf[i + 1]; - if ((b1 & 0xC0) === 0x80) { - c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F); - if (c <= 0x7F) { - c = null; - } - } - } else if (bytesPerSequence === 3) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) { - c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F); - if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) { - c = null; - } - } - } else if (bytesPerSequence === 4) { - b1 = buf[i + 1]; - b2 = buf[i + 2]; - b3 = buf[i + 3]; - if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { - c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F); - if (c <= 0xFFFF || c >= 0x110000) { - c = null; - } - } - } - - if (c === null) { - c = 0xFFFD; - bytesPerSequence = 1; - - } else if (c > 0xFFFF) { - c -= 0x10000; - str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800); - c = 0xDC00 | c & 0x3FF; - } - - str += String.fromCharCode(c); - i += bytesPerSequence; - } - - return str; - } - - function readUtf8TextDecoder(buf, pos, end) { - return utf8TextDecoder.decode(buf.subarray(pos, end)); - } - - function writeUtf8(buf, str, pos) { - for (var i = 0, c, lead; i < str.length; i++) { - c = str.charCodeAt(i); // code point - - if (c > 0xD7FF && c < 0xE000) { - if (lead) { - if (c < 0xDC00) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - lead = c; - continue; - } else { - c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; - lead = null; - } - } else { - if (c > 0xDBFF || (i + 1 === str.length)) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - } else { - lead = c; - } - continue; - } - } else if (lead) { - buf[pos++] = 0xEF; - buf[pos++] = 0xBF; - buf[pos++] = 0xBD; - lead = null; - } - - if (c < 0x80) { - buf[pos++] = c; - } else { - if (c < 0x800) { - buf[pos++] = c >> 0x6 | 0xC0; - } else { - if (c < 0x10000) { - buf[pos++] = c >> 0xC | 0xE0; - } else { - buf[pos++] = c >> 0x12 | 0xF0; - buf[pos++] = c >> 0xC & 0x3F | 0x80; - } - buf[pos++] = c >> 0x6 & 0x3F | 0x80; - } - buf[pos++] = c & 0x3F | 0x80; - } - } - return pos; - } - - var pointGeometry = Point; - - /** - * A standalone point geometry with useful accessor, comparison, and - * modification methods. - * - * @class Point - * @param {Number} x the x-coordinate. this could be longitude or screen - * pixels, or any other sort of unit. - * @param {Number} y the y-coordinate. this could be latitude or screen - * pixels, or any other sort of unit. - * @example - * var point = new Point(-77, 38); - */ - function Point(x, y) { - this.x = x; - this.y = y; - } - - Point.prototype = { - - /** - * Clone this point, returning a new point that can be modified - * without affecting the old one. - * @return {Point} the clone - */ - clone: function() { return new Point(this.x, this.y); }, - - /** - * Add this point's x & y coordinates to another point, - * yielding a new point. - * @param {Point} p the other point - * @return {Point} output point - */ - add: function(p) { return this.clone()._add(p); }, - - /** - * Subtract this point's x & y coordinates to from point, - * yielding a new point. - * @param {Point} p the other point - * @return {Point} output point - */ - sub: function(p) { return this.clone()._sub(p); }, - - /** - * Multiply this point's x & y coordinates by point, - * yielding a new point. - * @param {Point} p the other point - * @return {Point} output point - */ - multByPoint: function(p) { return this.clone()._multByPoint(p); }, - - /** - * Divide this point's x & y coordinates by point, - * yielding a new point. - * @param {Point} p the other point - * @return {Point} output point - */ - divByPoint: function(p) { return this.clone()._divByPoint(p); }, - - /** - * Multiply this point's x & y coordinates by a factor, - * yielding a new point. - * @param {Point} k factor - * @return {Point} output point - */ - mult: function(k) { return this.clone()._mult(k); }, - - /** - * Divide this point's x & y coordinates by a factor, - * yielding a new point. - * @param {Point} k factor - * @return {Point} output point - */ - div: function(k) { return this.clone()._div(k); }, - - /** - * Rotate this point around the 0, 0 origin by an angle a, - * given in radians - * @param {Number} a angle to rotate around, in radians - * @return {Point} output point - */ - rotate: function(a) { return this.clone()._rotate(a); }, - - /** - * Rotate this point around p point by an angle a, - * given in radians - * @param {Number} a angle to rotate around, in radians - * @param {Point} p Point to rotate around - * @return {Point} output point - */ - rotateAround: function(a,p) { return this.clone()._rotateAround(a,p); }, - - /** - * Multiply this point by a 4x1 transformation matrix - * @param {Array} m transformation matrix - * @return {Point} output point - */ - matMult: function(m) { return this.clone()._matMult(m); }, - - /** - * Calculate this point but as a unit vector from 0, 0, meaning - * that the distance from the resulting point to the 0, 0 - * coordinate will be equal to 1 and the angle from the resulting - * point to the 0, 0 coordinate will be the same as before. - * @return {Point} unit vector point - */ - unit: function() { return this.clone()._unit(); }, - - /** - * Compute a perpendicular point, where the new y coordinate - * is the old x coordinate and the new x coordinate is the old y - * coordinate multiplied by -1 - * @return {Point} perpendicular point - */ - perp: function() { return this.clone()._perp(); }, - - /** - * Return a version of this point with the x & y coordinates - * rounded to integers. - * @return {Point} rounded point - */ - round: function() { return this.clone()._round(); }, - - /** - * Return the magitude of this point: this is the Euclidean - * distance from the 0, 0 coordinate to this point's x and y - * coordinates. - * @return {Number} magnitude - */ - mag: function() { - return Math.sqrt(this.x * this.x + this.y * this.y); - }, - - /** - * Judge whether this point is equal to another point, returning - * true or false. - * @param {Point} other the other point - * @return {boolean} whether the points are equal - */ - equals: function(other) { - return this.x === other.x && - this.y === other.y; - }, - - /** - * Calculate the distance from this point to another point - * @param {Point} p the other point - * @return {Number} distance - */ - dist: function(p) { - return Math.sqrt(this.distSqr(p)); - }, - - /** - * Calculate the distance from this point to another point, - * without the square root step. Useful if you're comparing - * relative distances. - * @param {Point} p the other point - * @return {Number} distance - */ - distSqr: function(p) { - var dx = p.x - this.x, - dy = p.y - this.y; - return dx * dx + dy * dy; - }, - - /** - * Get the angle from the 0, 0 coordinate to this point, in radians - * coordinates. - * @return {Number} angle - */ - angle: function() { - return Math.atan2(this.y, this.x); - }, - - /** - * Get the angle from this point to another point, in radians - * @param {Point} b the other point - * @return {Number} angle - */ - angleTo: function(b) { - return Math.atan2(this.y - b.y, this.x - b.x); - }, - - /** - * Get the angle between this point and another point, in radians - * @param {Point} b the other point - * @return {Number} angle - */ - angleWith: function(b) { - return this.angleWithSep(b.x, b.y); - }, - - /* - * Find the angle of the two vectors, solving the formula for - * the cross product a x b = |a||b|sin(θ) for θ. - * @param {Number} x the x-coordinate - * @param {Number} y the y-coordinate - * @return {Number} the angle in radians - */ - angleWithSep: function(x, y) { - return Math.atan2( - this.x * y - this.y * x, - this.x * x + this.y * y); - }, - - _matMult: function(m) { - var x = m[0] * this.x + m[1] * this.y, - y = m[2] * this.x + m[3] * this.y; - this.x = x; - this.y = y; - return this; - }, - - _add: function(p) { - this.x += p.x; - this.y += p.y; - return this; - }, - - _sub: function(p) { - this.x -= p.x; - this.y -= p.y; - return this; - }, - - _mult: function(k) { - this.x *= k; - this.y *= k; - return this; - }, - - _div: function(k) { - this.x /= k; - this.y /= k; - return this; - }, - - _multByPoint: function(p) { - this.x *= p.x; - this.y *= p.y; - return this; - }, - - _divByPoint: function(p) { - this.x /= p.x; - this.y /= p.y; - return this; - }, - - _unit: function() { - this._div(this.mag()); - return this; - }, - - _perp: function() { - var y = this.y; - this.y = this.x; - this.x = -y; - return this; - }, - - _rotate: function(angle) { - var cos = Math.cos(angle), - sin = Math.sin(angle), - x = cos * this.x - sin * this.y, - y = sin * this.x + cos * this.y; - this.x = x; - this.y = y; - return this; - }, - - _rotateAround: function(angle, p) { - var cos = Math.cos(angle), - sin = Math.sin(angle), - x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), - y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y); - this.x = x; - this.y = y; - return this; - }, - - _round: function() { - this.x = Math.round(this.x); - this.y = Math.round(this.y); - return this; - } - }; - - /** - * Construct a point from an array if necessary, otherwise if the input - * is already a Point, or an unknown type, return it unchanged - * @param {Array|Point|*} a any kind of input value - * @return {Point} constructed point, or passed-through value. - * @example - * // this - * var point = Point.convert([0, 1]); - * // is equivalent to - * var point = new Point(0, 1); - */ - Point.convert = function (a) { - if (a instanceof Point) { - return a; - } - if (Array.isArray(a)) { - return new Point(a[0], a[1]); - } - return a; - }; - - var vectortilefeature = VectorTileFeature; - - function VectorTileFeature(pbf, end, extent, keys, values) { - // Public - this.properties = {}; - this.extent = extent; - this.type = 0; - - // Private - this._pbf = pbf; - this._geometry = -1; - this._keys = keys; - this._values = values; - - pbf.readFields(readFeature, this, end); - } - - function readFeature(tag, feature, pbf) { - if (tag == 1) feature.id = pbf.readVarint(); - else if (tag == 2) readTag(pbf, feature); - else if (tag == 3) feature.type = pbf.readVarint(); - else if (tag == 4) feature._geometry = pbf.pos; - } - - function readTag(pbf, feature) { - var end = pbf.readVarint() + pbf.pos; - - while (pbf.pos < end) { - var key = feature._keys[pbf.readVarint()], - value = feature._values[pbf.readVarint()]; - feature.properties[key] = value; - } - } - - VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon']; - - VectorTileFeature.prototype.loadGeometry = function() { - var pbf = this._pbf; - pbf.pos = this._geometry; - - var end = pbf.readVarint() + pbf.pos, - cmd = 1, - length = 0, - x = 0, - y = 0, - lines = [], - line; - - while (pbf.pos < end) { - if (length <= 0) { - var cmdLen = pbf.readVarint(); - cmd = cmdLen & 0x7; - length = cmdLen >> 3; - } - - length--; - - if (cmd === 1 || cmd === 2) { - x += pbf.readSVarint(); - y += pbf.readSVarint(); - - if (cmd === 1) { // moveTo - if (line) lines.push(line); - line = []; - } - - line.push(new pointGeometry(x, y)); - - } else if (cmd === 7) { - - // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90 - if (line) { - line.push(line[0].clone()); // closePolygon - } - - } else { - throw new Error('unknown command ' + cmd); - } - } - - if (line) lines.push(line); - - return lines; - }; - - VectorTileFeature.prototype.bbox = function() { - var pbf = this._pbf; - pbf.pos = this._geometry; - - var end = pbf.readVarint() + pbf.pos, - cmd = 1, - length = 0, - x = 0, - y = 0, - x1 = Infinity, - x2 = -Infinity, - y1 = Infinity, - y2 = -Infinity; - - while (pbf.pos < end) { - if (length <= 0) { - var cmdLen = pbf.readVarint(); - cmd = cmdLen & 0x7; - length = cmdLen >> 3; - } - - length--; - - if (cmd === 1 || cmd === 2) { - x += pbf.readSVarint(); - y += pbf.readSVarint(); - if (x < x1) x1 = x; - if (x > x2) x2 = x; - if (y < y1) y1 = y; - if (y > y2) y2 = y; - - } else if (cmd !== 7) { - throw new Error('unknown command ' + cmd); - } - } - - return [x1, y1, x2, y2]; - }; - - VectorTileFeature.prototype.toGeoJSON = function(x, y, z) { - var size = this.extent * Math.pow(2, z), - x0 = this.extent * x, - y0 = this.extent * y, - coords = this.loadGeometry(), - type = VectorTileFeature.types[this.type], - i, j; - - function project(line) { - for (var j = 0; j < line.length; j++) { - var p = line[j], y2 = 180 - (p.y + y0) * 360 / size; - line[j] = [ - (p.x + x0) * 360 / size - 180, - 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90 - ]; - } - } - - switch (this.type) { - case 1: - var points = []; - for (i = 0; i < coords.length; i++) { - points[i] = coords[i][0]; - } - coords = points; - project(coords); - break; - - case 2: - for (i = 0; i < coords.length; i++) { - project(coords[i]); - } - break; - - case 3: - coords = classifyRings(coords); - for (i = 0; i < coords.length; i++) { - for (j = 0; j < coords[i].length; j++) { - project(coords[i][j]); - } - } - break; - } - - if (coords.length === 1) { - coords = coords[0]; - } else { - type = 'Multi' + type; - } - - var result = { - type: "Feature", - geometry: { - type: type, - coordinates: coords - }, - properties: this.properties - }; - - if ('id' in this) { - result.id = this.id; - } - - return result; - }; - - // classifies an array of rings into polygons with outer rings and holes - - function classifyRings(rings) { - var len = rings.length; - - if (len <= 1) return [rings]; - - var polygons = [], - polygon, - ccw; - - for (var i = 0; i < len; i++) { - var area = signedArea$1(rings[i]); - if (area === 0) continue; - - if (ccw === undefined) ccw = area < 0; - - if (ccw === area < 0) { - if (polygon) polygons.push(polygon); - polygon = [rings[i]]; - - } else { - polygon.push(rings[i]); - } - } - if (polygon) polygons.push(polygon); - - return polygons; - } - - function signedArea$1(ring) { - var sum = 0; - for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { - p1 = ring[i]; - p2 = ring[j]; - sum += (p2.x - p1.x) * (p1.y + p2.y); - } - return sum; - } - - var vectortilelayer = VectorTileLayer; - - function VectorTileLayer(pbf, end) { - // Public - this.version = 1; - this.name = null; - this.extent = 4096; - this.length = 0; - - // Private - this._pbf = pbf; - this._keys = []; - this._values = []; - this._features = []; - - pbf.readFields(readLayer, this, end); - - this.length = this._features.length; - } - - function readLayer(tag, layer, pbf) { - if (tag === 15) layer.version = pbf.readVarint(); - else if (tag === 1) layer.name = pbf.readString(); - else if (tag === 5) layer.extent = pbf.readVarint(); - else if (tag === 2) layer._features.push(pbf.pos); - else if (tag === 3) layer._keys.push(pbf.readString()); - else if (tag === 4) layer._values.push(readValueMessage(pbf)); - } - - function readValueMessage(pbf) { - var value = null, - end = pbf.readVarint() + pbf.pos; - - while (pbf.pos < end) { - var tag = pbf.readVarint() >> 3; - - value = tag === 1 ? pbf.readString() : - tag === 2 ? pbf.readFloat() : - tag === 3 ? pbf.readDouble() : - tag === 4 ? pbf.readVarint64() : - tag === 5 ? pbf.readVarint() : - tag === 6 ? pbf.readSVarint() : - tag === 7 ? pbf.readBoolean() : null; - } - - return value; - } - - // return feature `i` from this layer as a `VectorTileFeature` - VectorTileLayer.prototype.feature = function(i) { - if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); - - this._pbf.pos = this._features[i]; - - var end = this._pbf.readVarint() + this._pbf.pos; - return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values); - }; - - var vectortile = VectorTile; - - function VectorTile(pbf, end) { - this.layers = pbf.readFields(readTile, {}, end); - } - - function readTile(tag, layers, pbf) { - if (tag === 3) { - var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos); - if (layer.length) layers[layer.name] = layer; - } - } - - var VectorTile$1 = vectortile; - var VectorTileFeature$1 = vectortilefeature; - var VectorTileLayer$1 = vectortilelayer; - - var vectorTile = { - VectorTile: VectorTile$1, - VectorTileFeature: VectorTileFeature$1, - VectorTileLayer: VectorTileLayer$1 - }; - - var tiler$7 = utilTiler().tileSize(512).margin(1); - var dispatch$8 = dispatch('loadedData'); - var _vtCache; - - - function abortRequest$7(controller) { - controller.abort(); - } - - - function vtToGeoJSON(data, tile, mergeCache) { - var vectorTile$1 = new vectorTile.VectorTile(new pbf(data)); - var layers = Object.keys(vectorTile$1.layers); - if (!Array.isArray(layers)) { layers = [layers]; } - - var features = []; - layers.forEach(function(layerID) { - var layer = vectorTile$1.layers[layerID]; - if (layer) { - for (var i = 0; i < layer.length; i++) { - var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]); - var geometry = feature.geometry; - - // Treat all Polygons as MultiPolygons - if (geometry.type === 'Polygon') { - geometry.type = 'MultiPolygon'; - geometry.coordinates = [geometry.coordinates]; - } - - // Clip to tile bounds - if (geometry.type === 'MultiPolygon') { - var isClipped = false; - var featureClip = bboxClip_1(feature, tile.extent.rectangle()); - if (!fastDeepEqual(feature.geometry, featureClip.geometry)) { - // feature = featureClip; - isClipped = true; - } - if (!feature.geometry.coordinates.length) continue; // not actually on this tile - if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile - } - - // Generate some unique IDs and add some metadata - var featurehash = utilHashcode(fastJsonStableStringify(feature)); - var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {})); - feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_'); - feature.__featurehash__ = featurehash; - feature.__propertyhash__ = propertyhash; - features.push(feature); - - // Clipped Polygons at same zoom with identical properties can get merged - if (isClipped && geometry.type === 'MultiPolygon') { - var merged = mergeCache[propertyhash]; - if (merged && merged.length) { - var other = merged[0]; - var coords = union( - feature.geometry.coordinates, - other.geometry.coordinates - ); - - if (!coords || !coords.length) { - continue; // something failed in martinez union - } - - merged.push(feature); - for (var j = 0; j < merged.length; j++) { // all these features get... - merged[j].geometry.coordinates = coords; // same coords - merged[j].__featurehash__ = featurehash; // same hash, so deduplication works - } - } else { - mergeCache[propertyhash] = [feature]; - } - } - } - } - }); - - return features; - } - - - function loadTile(source, tile) { - if (source.loaded[tile.id] || source.inflight[tile.id]) return; - - var url = source.template - .replace('{x}', tile.xyz[0]) - .replace('{y}', tile.xyz[1]) - // TMS-flipped y coordinate - .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1) - .replace(/\{z(oom)?\}/, tile.xyz[2]) - .replace(/\{switch:([^}]+)\}/, function(s, r) { - var subdomains = r.split(','); - return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length]; - }); - - - var controller = new AbortController(); - source.inflight[tile.id] = controller; - - fetch(url, { signal: controller.signal }) - .then(function(response) { - if (!response.ok) { - throw new Error(response.status + ' ' + response.statusText); - } - source.loaded[tile.id] = []; - delete source.inflight[tile.id]; - return response.arrayBuffer(); - }) - .then(function(data) { - if (!data) { - throw new Error('No Data'); - } - - var z = tile.xyz[2]; - if (!source.canMerge[z]) { - source.canMerge[z] = {}; // initialize mergeCache - } - - source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]); - dispatch$8.call('loadedData'); - }) - .catch(function() { - source.loaded[tile.id] = []; - delete source.inflight[tile.id]; - }); - } - - - var serviceVectorTile = { - - init: function() { - if (!_vtCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch$8, 'on'); - }, - - - reset: function() { - for (var sourceID in _vtCache) { - var source = _vtCache[sourceID]; - if (source && source.inflight) { - Object.values(source.inflight).forEach(abortRequest$7); - } - } - - _vtCache = {}; - }, - - - addSource: function(sourceID, template) { - _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} }; - return _vtCache[sourceID]; - }, - - - data: function(sourceID, projection) { - var source = _vtCache[sourceID]; - if (!source) return []; - - var tiles = tiler$7.getTiles(projection); - var seen = {}; - var results = []; - - for (var i = 0; i < tiles.length; i++) { - var features = source.loaded[tiles[i].id]; - if (!features || !features.length) continue; - - for (var j = 0; j < features.length; j++) { - var feature = features[j]; - var hash = feature.__featurehash__; - if (seen[hash]) continue; - seen[hash] = true; - - // return a shallow copy, because the hash may change - // later if this feature gets merged with another - results.push(Object.assign({}, feature)); // shallow copy - } - } - - return results; - }, - - - loadTiles: function(sourceID, template, projection) { - var source = _vtCache[sourceID]; - if (!source) { - source = this.addSource(sourceID, template); - } - - var tiles = tiler$7.getTiles(projection); - - // abort inflight requests that are no longer needed - Object.keys(source.inflight).forEach(function(k) { - var wanted = tiles.find(function(tile) { return k === tile.id; }); - if (!wanted) { - abortRequest$7(source.inflight[k]); - delete source.inflight[k]; - } - }); - - tiles.forEach(function(tile) { - loadTile(source, tile); - }); - }, - - - cache: function() { - return _vtCache; - } - - }; - - var apibase$5 = 'https://www.wikidata.org/w/api.php?'; - var _wikidataCache = {}; - - - var serviceWikidata = { - - init: function() {}, - - reset: function() { - _wikidataCache = {}; - }, - - - // Search for Wikidata items matching the query - itemsForSearchQuery: function(query, callback) { - if (!query) { - if (callback) callback('No query', {}); - return; - } - - var lang = this.languagesToQuery()[0]; - - var url = apibase$5 + utilQsString({ - action: 'wbsearchentities', - format: 'json', - formatversion: 2, - search: query, - type: 'item', - // the language to search - language: lang, - // the langauge for the label and description in the result - uselang: lang, - limit: 10, - origin: '*' - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } - if (callback) callback(null, result.search || {}); - }) - .catch(function(err) { - if (callback) callback(err.message, {}); - }); - }, - - - // Given a Wikipedia language and article title, - // return an array of corresponding Wikidata entities. - itemsByTitle: function(lang, title, callback) { - if (!title) { - if (callback) callback('No title', {}); - return; - } - - lang = lang || 'en'; - var url = apibase$5 + utilQsString({ - action: 'wbgetentities', - format: 'json', - formatversion: 2, - sites: lang.replace(/-/g, '_') + 'wiki', - titles: title, - languages: 'en', // shrink response by filtering to one language - origin: '*' - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } - if (callback) callback(null, result.entities || {}); - }) - .catch(function(err) { - if (callback) callback(err.message, {}); - }); - }, - - - languagesToQuery: function() { - var localeCode = _mainLocalizer.localeCode().toLowerCase(); - // HACK: en-us isn't a wikidata language. We should really be filtering by - // the languages known to be supported by wikidata. - if (localeCode === 'en-us') localeCode = 'en'; - return utilArrayUniq([ - localeCode, - _mainLocalizer.languageCode().toLowerCase(), - 'en' - ]); - }, - - - entityByQID: function(qid, callback) { - if (!qid) { - callback('No qid', {}); - return; - } - if (_wikidataCache[qid]) { - if (callback) callback(null, _wikidataCache[qid]); - return; - } - - var langs = this.languagesToQuery(); - var url = apibase$5 + utilQsString({ - action: 'wbgetentities', - format: 'json', - formatversion: 2, - ids: qid, - props: 'labels|descriptions|claims|sitelinks', - sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'), - languages: langs.join('|'), - languagefallback: 1, - origin: '*' - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } - if (callback) callback(null, result.entities[qid] || {}); - }) - .catch(function(err) { - if (callback) callback(err.message, {}); - }); - }, - - - // Pass `params` object of the form: - // { - // qid: 'string' // brand wikidata (e.g. 'Q37158') - // } - // - // Get an result object used to display tag documentation - // { - // title: 'string', - // description: 'string', - // editURL: 'string', - // imageURL: 'string', - // wiki: { title: 'string', text: 'string', url: 'string' } - // } - // - getDocs: function(params, callback) { - var langs = this.languagesToQuery(); - this.entityByQID(params.qid, function(err, entity) { - if (err || !entity) { - callback(err || 'No entity'); - return; - } - - var i; - var description; - if (entity.descriptions && Object.keys(entity.descriptions).length > 0) { - description = entity.descriptions[Object.keys(entity.descriptions)[0]].value; - } - - // prepare result - var result = { - title: entity.id, - description: description, - editURL: 'https://www.wikidata.org/wiki/' + entity.id - }; - - // add image - if (entity.claims) { - var imageroot = 'https://commons.wikimedia.org/w/index.php'; - var props = ['P154','P18']; // logo image, image - var prop, image; - for (i = 0; i < props.length; i++) { - prop = entity.claims[props[i]]; - if (prop && Object.keys(prop).length > 0) { - image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value; - if (image) { - result.imageURL = imageroot + '?' + utilQsString({ - title: 'Special:Redirect/file/' + image, - width: 400 - }); - break; - } - } - } - } - - if (entity.sitelinks) { - var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; - - // must be one of these that we requested.. - for (i = 0; i < langs.length; i++) { // check each, in order of preference - var w = langs[i] + 'wiki'; - if (entity.sitelinks[w]) { - var title = entity.sitelinks[w].title; - var tKey = 'inspector.wiki_reference'; - if (!englishLocale && langs[i] === 'en') { // user's locale isn't English but - tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway.. - } - - result.wiki = { - title: title, - text: tKey, - url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_') - }; - break; - } - } - } - - callback(null, result); - }); - } - - }; - - var endpoint = 'https://en.wikipedia.org/w/api.php?'; - - var serviceWikipedia = { - - init: function() {}, - reset: function() {}, - - - search: function(lang, query, callback) { - if (!query) { - if (callback) callback('No Query', []); - return; - } - - lang = lang || 'en'; - var url = endpoint.replace('en', lang) + - utilQsString({ - action: 'query', - list: 'search', - srlimit: '10', - srinfo: 'suggestion', - format: 'json', - origin: '*', - srsearch: query - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } else if (!result || !result.query || !result.query.search) { - throw new Error('No Results'); - } - if (callback) { - var titles = result.query.search.map(function(d) { return d.title; }); - callback(null, titles); - } - }) - .catch(function(err) { - if (callback) callback(err, []); - }); - }, - - - suggestions: function(lang, query, callback) { - if (!query) { - if (callback) callback('', []); - return; - } - - lang = lang || 'en'; - var url = endpoint.replace('en', lang) + - utilQsString({ - action: 'opensearch', - namespace: 0, - suggest: '', - format: 'json', - origin: '*', - search: query - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } else if (!result || result.length < 2) { - throw new Error('No Results'); - } - if (callback) callback(null, result[1] || []); - }) - .catch(function(err) { - if (callback) callback(err.message, []); - }); - }, - - - translations: function(lang, title, callback) { - if (!title) { - if (callback) callback('No Title'); - return; - } - - var url = endpoint.replace('en', lang) + - utilQsString({ - action: 'query', - prop: 'langlinks', - format: 'json', - origin: '*', - lllimit: 500, - titles: title - }); - - d3_json(url) - .then(function(result) { - if (result && result.error) { - throw new Error(result.error); - } else if (!result || !result.query || !result.query.pages) { - throw new Error('No Results'); - } - if (callback) { - var list = result.query.pages[Object.keys(result.query.pages)[0]]; - var translations = {}; - if (list && list.langlinks) { - list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; }); - } - callback(null, translations); - } - }) - .catch(function(err) { - if (callback) callback(err.message); - }); - } - - }; - - var services = { - geocoder: serviceNominatim, - keepRight: serviceKeepRight, - improveOSM: serviceImproveOSM, - osmose: serviceOsmose, - mapillary: serviceMapillary, - openstreetcam: serviceOpenstreetcam, - osm: serviceOsm, - osmWikibase: serviceOsmWikibase, - maprules: serviceMapRules, - streetside: serviceStreetside, - taginfo: serviceTaginfo, - vectorTile: serviceVectorTile, - wikidata: serviceWikidata, - wikipedia: serviceWikipedia - }; - - function svgIcon(name, svgklass, useklass) { - return function drawIcon(selection) { - selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : '')) - .data([0]) - .enter() - .append('svg') - .attr('class', 'icon ' + (svgklass || '')) - .append('use') - .attr('xlink:href', name) - .attr('class', useklass); - }; - } - - function uiNoteComments() { - var _note; - - - function noteComments(selection) { - if (_note.isNew()) return; // don't draw .comments-container - - var comments = selection.selectAll('.comments-container') - .data([0]); - - comments = comments.enter() - .append('div') - .attr('class', 'comments-container') - .merge(comments); - - var commentEnter = comments.selectAll('.comment') - .data(_note.comments) - .enter() - .append('div') - .attr('class', 'comment'); - - commentEnter - .append('div') - .attr('class', function(d) { return 'comment-avatar user-' + d.uid; }) - .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); - - var mainEnter = commentEnter - .append('div') - .attr('class', 'comment-main'); - - var metadataEnter = mainEnter - .append('div') - .attr('class', 'comment-metadata'); - - metadataEnter - .append('div') - .attr('class', 'comment-author') - .each(function(d) { - var selection = select(this); - var osm = services.osm; - if (osm && d.user) { - selection = selection - .append('a') - .attr('class', 'comment-author-link') - .attr('href', osm.userURL(d.user)) - .attr('tabindex', -1) - .attr('target', '_blank'); - } - selection - .text(function(d) { return d.user || _t('note.anonymous'); }); - }); - - metadataEnter - .append('div') - .attr('class', 'comment-date') - .text(function(d) { - return _t('note.status.' + d.action, { when: localeDateString(d.date) }); - }); - - mainEnter - .append('div') - .attr('class', 'comment-text') - .html(function(d) { return d.html; }); - - comments - .call(replaceAvatars); - } - - - function replaceAvatars(selection) { - var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; - var osm = services.osm; - if (showThirdPartyIcons !== 'true' || !osm) return; - - var uids = {}; // gather uids in the comment thread - _note.comments.forEach(function(d) { - if (d.uid) uids[d.uid] = true; - }); - - Object.keys(uids).forEach(function(uid) { - osm.loadUser(uid, function(err, user) { - if (!user || !user.image_url) return; - - selection.selectAll('.comment-avatar.user-' + uid) - .html('') - .append('img') - .attr('class', 'icon comment-avatar-icon') - .attr('src', user.image_url) - .attr('alt', user.display_name); - }); - }); - } - - - function localeDateString(s) { - if (!s) return null; - var options = { day: 'numeric', month: 'short', year: 'numeric' }; - s = s.replace(/-/g, '/'); // fix browser-specific Date() issues - var d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(_mainLocalizer.localeCode(), options); - } - - - noteComments.note = function(val) { - if (!arguments.length) return _note; - _note = val; - return noteComments; - }; - - - return noteComments; - } - - function uiNoteHeader() { - var _note; - - - function noteHeader(selection) { - var header = selection.selectAll('.note-header') - .data( - (_note ? [_note] : []), - function(d) { return d.status + d.id; } - ); - - header.exit() - .remove(); - - var headerEnter = header.enter() - .append('div') - .attr('class', 'note-header'); - - var iconEnter = headerEnter - .append('div') - .attr('class', function(d) { return 'note-header-icon ' + d.status; }) - .classed('new', function(d) { return d.id < 0; }); - - iconEnter - .append('div') - .attr('class', 'preset-icon-28') - .call(svgIcon('#iD-icon-note', 'note-fill')); - - iconEnter.each(function(d) { - var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply')); - iconEnter - .append('div') - .attr('class', 'note-icon-annotation') - .call(svgIcon(statusIcon, 'icon-annotation')); - }); - - headerEnter - .append('div') - .attr('class', 'note-header-label') - .text(function(d) { - if (_note.isNew()) { return _t('note.new'); } - return _t('note.note') + ' ' + d.id + ' ' + - (d.status === 'closed' ? _t('note.closed') : ''); - }); - } - - - noteHeader.note = function(val) { - if (!arguments.length) return _note; - _note = val; - return noteHeader; - }; - - - return noteHeader; - } - - function uiNoteReport() { - var _note; - - function noteReport(selection) { - var url; - if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) { - url = services.osm.noteReportURL(_note); - } - - var link = selection.selectAll('.note-report') - .data(url ? [url] : []); - - // exit - link.exit() - .remove(); - - // enter - var linkEnter = link.enter() - .append('a') - .attr('class', 'note-report') - .attr('target', '_blank') - .attr('href', function(d) { return d; }) - .call(svgIcon('#iD-icon-out-link', 'inline')); - - linkEnter - .append('span') - .text(_t('note.report')); - } - - - noteReport.note = function(val) { - if (!arguments.length) return _note; - _note = val; - return noteReport; - }; - - return noteReport; - } - - function uiViewOnOSM(context) { - var _what; // an osmEntity or osmNote - - - function viewOnOSM(selection) { - var url; - if (_what instanceof osmEntity) { - url = context.connection().entityURL(_what); - } else if (_what instanceof osmNote) { - url = context.connection().noteURL(_what); - } - - var data = ((!_what || _what.isNew()) ? [] : [_what]); - var link = selection.selectAll('.view-on-osm') - .data(data, function(d) { return d.id; }); - - // exit - link.exit() - .remove(); - - // enter - var linkEnter = link.enter() - .append('a') - .attr('class', 'view-on-osm') - .attr('target', '_blank') - .attr('href', url) - .call(svgIcon('#iD-icon-out-link', 'inline')); - - linkEnter - .append('span') - .text(_t('inspector.view_on_osm')); - } - - - viewOnOSM.what = function(_) { - if (!arguments.length) return _what; - _what = _; - return viewOnOSM; - }; - - return viewOnOSM; - } - - function uiNoteEditor(context) { - var dispatch$1 = dispatch('change'); - var noteComments = uiNoteComments(); - var noteHeader = uiNoteHeader(); - - // var formFields = uiFormFields(context); - - var _note; - var _newNote; - // var _fieldsArr; - - - function noteEditor(selection) { - - var header = selection.selectAll('.header') - .data([0]); - - var headerEnter = header.enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', function() { - context.enter(modeBrowse(context)); - }) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('note.title')); - - - var body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - var editor = body.selectAll('.note-editor') - .data([0]); - - editor.enter() - .append('div') - .attr('class', 'modal-section note-editor') - .merge(editor) - .call(noteHeader.note(_note)) - .call(noteComments.note(_note)) - .call(noteSaveSection); - - var footer = selection.selectAll('.footer') - .data([0]); - - footer.enter() - .append('div') - .attr('class', 'footer') - .merge(footer) - .call(uiViewOnOSM(context).what(_note)) - .call(uiNoteReport().note(_note)); - - - // rerender the note editor on any auth change - var osm = services.osm; - if (osm) { - osm.on('change.note-save', function() { - selection.call(noteEditor); - }); - } - } - - - function noteSaveSection(selection) { - var isSelected = (_note && _note.id === context.selectedNoteID()); - var noteSave = selection.selectAll('.note-save') - .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; }); - - // exit - noteSave.exit() - .remove(); - - // enter - var noteSaveEnter = noteSave.enter() - .append('div') - .attr('class', 'note-save save-section cf'); - - // // if new note, show categories to pick from - // if (_note.isNew()) { - // var presets = presetManager; - - // // NOTE: this key isn't a age and therefore there is no documentation (yet) - // _fieldsArr = [ - // uiField(context, presets.field('category'), null, { show: true, revert: false }), - // ]; - - // _fieldsArr.forEach(function(field) { - // field - // .on('change', changeCategory); - // }); - - // noteSaveEnter - // .append('div') - // .attr('class', 'note-category') - // .call(formFields.fieldsArr(_fieldsArr)); - // } - - // function changeCategory() { - // // NOTE: perhaps there is a better way to get value - // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined; - - // // store the unsaved category with the note itself - // _note = _note.update({ newCategory: val }); - // var osm = services.osm; - // if (osm) { - // osm.replaceNote(_note); // update note cache - // } - // noteSave - // .call(noteSaveButtons); - // } - - noteSaveEnter - .append('h4') - .attr('class', '.note-save-header') - .text(function() { - return _note.isNew() ? _t('note.newDescription') : _t('note.newComment'); - }); - - var commentTextarea = noteSaveEnter - .append('textarea') - .attr('class', 'new-comment-input') - .attr('placeholder', _t('note.inputPlaceholder')) - .attr('maxlength', 1000) - .property('value', function(d) { return d.newComment; }) - .call(utilNoAuto) - .on('keydown.note-input', keydown) - .on('input.note-input', changeInput) - .on('blur.note-input', changeInput); - - if (_newNote) { - // autofocus the comment field for new notes - commentTextarea.node().focus(); - } - - // update - noteSave = noteSaveEnter - .merge(noteSave) - .call(userDetails) - .call(noteSaveButtons); - - - // fast submit if user presses cmd+enter - function keydown() { - if (!(event.keyCode === 13 && event.metaKey)) return; - - var osm = services.osm; - if (!osm) return; - - var hasAuth = osm.authenticated(); - if (!hasAuth) return; - - if (!_note.newComment) return; - - event.preventDefault(); - - select(this) - .on('keydown.note-input', null); - - // focus on button and submit - window.setTimeout(function() { - if (_note.isNew()) { - noteSave.selectAll('.save-button').node().focus(); - clickSave(_note); - } else { - noteSave.selectAll('.comment-button').node().focus(); - clickComment(_note); - } - }, 10); - } - - - function changeInput() { - var input = select(this); - var val = input.property('value').trim() || undefined; - - // store the unsaved comment with the note itself - _note = _note.update({ newComment: val }); - - var osm = services.osm; - if (osm) { - osm.replaceNote(_note); // update note cache - } - - noteSave - .call(noteSaveButtons); - } - } - - - function userDetails(selection) { - var detailSection = selection.selectAll('.detail-section') - .data([0]); - - detailSection = detailSection.enter() - .append('div') - .attr('class', 'detail-section') - .merge(detailSection); - - var osm = services.osm; - if (!osm) return; - - // Add warning if user is not logged in - var hasAuth = osm.authenticated(); - var authWarning = detailSection.selectAll('.auth-warning') - .data(hasAuth ? [] : [0]); - - authWarning.exit() - .transition() - .duration(200) - .style('opacity', 0) - .remove(); - - var authEnter = authWarning.enter() - .insert('div', '.tag-reference-body') - .attr('class', 'field-warning auth-warning') - .style('opacity', 0); - - authEnter - .call(svgIcon('#iD-icon-alert', 'inline')); - - authEnter - .append('span') - .text(_t('note.login')); - - authEnter - .append('a') - .attr('target', '_blank') - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(_t('login')) - .on('click.note-login', function() { - event.preventDefault(); - osm.authenticate(); - }); - - authEnter - .transition() - .duration(200) - .style('opacity', 1); - - - var prose = detailSection.selectAll('.note-save-prose') - .data(hasAuth ? [0] : []); - - prose.exit() - .remove(); - - prose = prose.enter() - .append('p') - .attr('class', 'note-save-prose') - .text(_t('note.upload_explanation')) - .merge(prose); - - osm.userDetails(function(err, user) { - if (err) return; - - var userLink = select(document.createElement('div')); - - if (user.image_url) { - userLink - .append('img') - .attr('src', user.image_url) - .attr('class', 'icon pre-text user-icon'); - } - - userLink - .append('a') - .attr('class', 'user-info') - .text(user.display_name) - .attr('href', osm.userURL(user.display_name)) - .attr('tabindex', -1) - .attr('target', '_blank'); - - prose - .html(_t('note.upload_explanation_with_user', { user: userLink.html() })); - }); - } - - - function noteSaveButtons(selection) { - var osm = services.osm; - var hasAuth = osm && osm.authenticated(); - - var isSelected = (_note && _note.id === context.selectedNoteID()); - var buttonSection = selection.selectAll('.buttons') - .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; }); - - // exit - buttonSection.exit() - .remove(); - - // enter - var buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons'); - - if (_note.isNew()) { - buttonEnter - .append('button') - .attr('class', 'button cancel-button secondary-action') - .text(_t('confirm.cancel')); - - buttonEnter - .append('button') - .attr('class', 'button save-button action') - .text(_t('note.save')); - - } else { - buttonEnter - .append('button') - .attr('class', 'button status-button action'); - - buttonEnter - .append('button') - .attr('class', 'button comment-button action') - .text(_t('note.comment')); - } - - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.select('.cancel-button') // select and propagate data - .on('click.cancel', clickCancel); - - buttonSection.select('.save-button') // select and propagate data - .attr('disabled', isSaveDisabled) - .on('click.save', clickSave); - - buttonSection.select('.status-button') // select and propagate data - .attr('disabled', (hasAuth ? null : true)) - .text(function(d) { - var action = (d.status === 'open' ? 'close' : 'open'); - var andComment = (d.newComment ? '_comment' : ''); - return _t('note.' + action + andComment); - }) - .on('click.status', clickStatus); - - buttonSection.select('.comment-button') // select and propagate data - .attr('disabled', isSaveDisabled) - .on('click.comment', clickComment); - - - function isSaveDisabled(d) { - return (hasAuth && d.status === 'open' && d.newComment) ? null : true; - } - } - - - - function clickCancel(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.removeNote(d); - } - context.enter(modeBrowse(context)); - dispatch$1.call('change'); - } - - - function clickSave(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.postNoteCreate(d, function(err, note) { - dispatch$1.call('change', note); - }); - } - } - - - function clickStatus(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - var setStatus = (d.status === 'open' ? 'closed' : 'open'); - osm.postNoteUpdate(d, setStatus, function(err, note) { - dispatch$1.call('change', note); - }); - } - } - - function clickComment(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.postNoteUpdate(d, d.status, function(err, note) { - dispatch$1.call('change', note); - }); - } - } - - - noteEditor.note = function(val) { - if (!arguments.length) return _note; - _note = val; - return noteEditor; - }; - - noteEditor.newNote = function(val) { - if (!arguments.length) return _newNote; - _newNote = val; - return noteEditor; - }; - - - return utilRebind(noteEditor, dispatch$1, 'on'); - } - - function modeSelectNote(context, selectedNoteID) { - var mode = { - id: 'select-note', - button: 'browse' - }; - - var _keybinding = utilKeybinding('select-note'); - var _noteEditor = uiNoteEditor(context) - .on('change', function() { - context.map().pan([0,0]); // trigger a redraw - var note = checkSelectedID(); - if (!note) return; - context.ui().sidebar - .show(_noteEditor.note(note)); - }); - - var _behaviors = [ - behaviorBreathe(), - behaviorHover(context), - behaviorSelect(context), - behaviorLasso(context), - modeDragNode(context).behavior, - modeDragNote(context).behavior - ]; - - var _newFeature = false; - - - function checkSelectedID() { - if (!services.osm) return; - var note = services.osm.getNote(selectedNoteID); - if (!note) { - context.enter(modeBrowse(context)); - } - return note; - } - - - // class the note as selected, or return to browse mode if the note is gone - function selectNote(drawn) { - if (!checkSelectedID()) return; - - var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID); - - if (selection.empty()) { - // Return to browse mode if selected DOM elements have - // disappeared because the user moved them out of view.. - var source = event && event.type === 'zoom' && event.sourceEvent; - if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) { - context.enter(modeBrowse(context)); - } - - } else { - selection - .classed('selected', true); - - context.selectedNoteID(selectedNoteID); - } - } - - - function esc() { - if (context.container().select('.combobox').size()) return; - context.enter(modeBrowse(context)); - } - - - mode.zoomToSelected = function() { - if (!services.osm) return; - var note = services.osm.getNote(selectedNoteID); - if (note) { - context.map().centerZoomEase(note.loc, 20); - } - }; - - - mode.newFeature = function(val) { - if (!arguments.length) return _newFeature; - _newFeature = val; - return mode; - }; - - - mode.enter = function() { - var note = checkSelectedID(); - if (!note) return; - - _behaviors.forEach(context.install); - - _keybinding - .on(_t('inspector.zoom_to.key'), mode.zoomToSelected) - .on('⎋', esc, true); - - select(document) - .call(_keybinding); - - selectNote(); - - var sidebar = context.ui().sidebar; - sidebar.show(_noteEditor.note(note).newNote(_newFeature)); - - // expand the sidebar, avoid obscuring the note if needed - sidebar.expand(sidebar.intersects(note.extent())); - - context.map() - .on('drawn.select', selectNote); - }; - - - mode.exit = function() { - _behaviors.forEach(context.uninstall); - - select(document) - .call(_keybinding.unbind); - - context.surface() - .selectAll('.layer-notes .selected') - .classed('selected hover', false); - - context.map() - .on('drawn.select', null); - - context.ui().sidebar - .hide(); - - context.selectedNoteID(null); - }; - - - return mode; - } - - function modeDragNote(context) { - var mode = { - id: 'drag-note', - button: 'browse' - }; - - var edit = behaviorEdit(context); - - var _nudgeInterval; - var _lastLoc; - var _note; // most current note.. dragged note may have stale datum. - - - function startNudge(nudge) { - if (_nudgeInterval) window.clearInterval(_nudgeInterval); - _nudgeInterval = window.setInterval(function() { - context.map().pan(nudge); - doMove(nudge); - }, 50); - } - - - function stopNudge() { - if (_nudgeInterval) { - window.clearInterval(_nudgeInterval); - _nudgeInterval = null; - } - } - - - function origin(note) { - return context.projection(note.loc); - } - - - function start(note) { - _note = note; - var osm = services.osm; - if (osm) { - // Get latest note from cache.. The marker may have a stale datum bound to it - // and dragging it around can sometimes delete the users note comment. - _note = osm.getNote(_note.id); - } - - context.surface().selectAll('.note-' + _note.id) - .classed('active', true); - - context.perform(actionNoop()); - context.enter(mode); - context.selectedNoteID(_note.id); - } - - - function move() { - event.sourceEvent.stopPropagation(); - _lastLoc = context.projection.invert(event.point); - - doMove(); - var nudge = geoViewportEdge(event.point, context.map().dimensions()); - if (nudge) { - startNudge(nudge); - } else { - stopNudge(); - } - } - - - function doMove(nudge) { - nudge = nudge || [0, 0]; - - var currPoint = (event && event.point) || context.projection(_lastLoc); - var currMouse = geoVecSubtract(currPoint, nudge); - var loc = context.projection.invert(currMouse); - - _note = _note.move(loc); - - var osm = services.osm; - if (osm) { - osm.replaceNote(_note); // update note cache - } - - context.replace(actionNoop()); // trigger redraw - } - - - function end() { - context.replace(actionNoop()); // trigger redraw - - context - .selectedNoteID(_note.id) - .enter(modeSelectNote(context, _note.id)); - } - - - var drag = behaviorDrag() - .selector('.layer-touch.markers .target.note.new') - .surface(context.container().select('.main-map').node()) - .origin(origin) - .on('start', start) - .on('move', move) - .on('end', end); - - - mode.enter = function() { - context.install(edit); - }; - - - mode.exit = function() { - context.ui().sidebar.hover.cancel(); - context.uninstall(edit); - - context.surface() - .selectAll('.active') - .classed('active', false); - - stopNudge(); - }; - - mode.behavior = drag; - - return mode; - } - - function uiDataHeader() { - var _datum; - - - function dataHeader(selection) { - var header = selection.selectAll('.data-header') - .data( - (_datum ? [_datum] : []), - function(d) { return d.__featurehash__; } - ); - - header.exit() - .remove(); - - var headerEnter = header.enter() - .append('div') - .attr('class', 'data-header'); - - var iconEnter = headerEnter - .append('div') - .attr('class', 'data-header-icon'); - - iconEnter - .append('div') - .attr('class', 'preset-icon-28') - .call(svgIcon('#iD-icon-data', 'note-fill')); - - headerEnter - .append('div') - .attr('class', 'data-header-label') - .text(_t('map_data.layers.custom.title')); - } - - - dataHeader.datum = function(val) { - if (!arguments.length) return _datum; - _datum = val; - return this; - }; - - - return dataHeader; - } - - // This code assumes that the combobox values will not have duplicate entries. - // It is keyed on the `value` of the entry. Data should be an array of objects like: - // [{ - // value: 'display text', // required - // title: 'hover text' // optional - // }, ...] - - var _comboHideTimerID; - - function uiCombobox(context, klass) { - var dispatch$1 = dispatch('accept', 'cancel'); - var container = context.container(); - - var _suggestions = []; - var _data = []; - var _fetched = {}; - var _selected = null; - var _canAutocomplete = true; - var _caseSensitive = false; - var _cancelFetch = false; - var _minItems = 2; - var _tDown = 0; - var _mouseEnterHandler, _mouseLeaveHandler; - - var _fetcher = function(val, cb) { - cb(_data.filter(function(d) { - var terms = d.terms || []; - terms.push(d.value); - return terms.some(function(term) { - return term - .toString() - .toLowerCase() - .indexOf(val.toLowerCase()) !== -1; - }); - })); - }; - - var combobox = function(input, attachTo) { - if (!input || input.empty()) return; - - input - .classed('combobox-input', true) - .on('focus.combo-input', focus) - .on('blur.combo-input', blur) - .on('keydown.combo-input', keydown) - .on('keyup.combo-input', keyup) - .on('input.combo-input', change) - .on('mousedown.combo-input', mousedown) - .each(function() { - var parent = this.parentNode; - var sibling = this.nextSibling; - - select(parent).selectAll('.combobox-caret') - .filter(function(d) { return d === input.node(); }) - .data([input.node()]) - .enter() - .insert('div', function() { return sibling; }) - .attr('class', 'combobox-caret') - .on('mousedown.combo-caret', function() { - event.preventDefault(); // don't steal focus from input - input.node().focus(); // focus the input as if it was clicked - mousedown(); - }) - .on('mouseup.combo-caret', function() { - event.preventDefault(); // don't steal focus from input - mouseup(); - }); - }); - - - function mousedown() { - if (event.button !== 0) return; // left click only - _tDown = +new Date(); - - // clear selection - var start = input.property('selectionStart'); - var end = input.property('selectionEnd'); - if (start !== end) { - var val = utilGetSetValue(input); - input.node().setSelectionRange(val.length, val.length); - return; - } - - input.on('mouseup.combo-input', mouseup); - } - - - function mouseup() { - input.on('mouseup.combo-input', null); - if (event.button !== 0) return; // left click only - if (input.node() !== document.activeElement) return; // exit if this input is not focused - - var start = input.property('selectionStart'); - var end = input.property('selectionEnd'); - if (start !== end) return; // exit if user is selecting - - // not showing or showing for a different field - try to show it. - var combo = container.selectAll('.combobox'); - if (combo.empty() || combo.datum() !== input.node()) { - var tOrig = _tDown; - window.setTimeout(function() { - if (tOrig !== _tDown) return; // exit if user double clicked - fetchComboData('', function() { - show(); - render(); - }); - }, 250); - - } else { - hide(); - } - } - - - function focus() { - fetchComboData(''); // prefetch values (may warm taginfo cache) - } - - - function blur() { - _comboHideTimerID = window.setTimeout(hide, 75); - } - - - function show() { - hide(); // remove any existing - - container - .insert('div', ':first-child') - .datum(input.node()) - .attr('class', 'combobox' + (klass ? ' combobox-' + klass : '')) - .style('position', 'absolute') - .style('display', 'block') - .style('left', '0px') - .on('mousedown.combo-container', function () { - // prevent moving focus out of the input field - event.preventDefault(); - }); - - container - .on('scroll.combo-scroll', render, true); - } - - - function hide() { - if (_comboHideTimerID) { - window.clearTimeout(_comboHideTimerID); - _comboHideTimerID = undefined; - } - - container.selectAll('.combobox') - .remove(); - - container - .on('scroll.combo-scroll', null); - } - - - function keydown() { - var shown = !container.selectAll('.combobox').empty(); - var tagName = input.node() ? input.node().tagName.toLowerCase() : ''; - - switch (event.keyCode) { - case 8: // ⌫ Backspace - case 46: // ⌦ Delete - event.stopPropagation(); - _selected = null; - render(); - input.on('input.combo-input', function() { - var start = input.property('selectionStart'); - input.node().setSelectionRange(start, start); - input.on('input.combo-input', change); - }); - break; - - case 9: // ⇥ Tab - accept(); - break; - - case 13: // ↩ Return - event.preventDefault(); - event.stopPropagation(); - break; - - case 38: // ↑ Up arrow - if (tagName === 'textarea' && !shown) return; - event.preventDefault(); - if (tagName === 'input' && !shown) { - show(); - } - nav(-1); - break; - - case 40: // ↓ Down arrow - if (tagName === 'textarea' && !shown) return; - event.preventDefault(); - if (tagName === 'input' && !shown) { - show(); - } - nav(+1); - break; - } - } - - - function keyup() { - switch (event.keyCode) { - case 27: // ⎋ Escape - cancel(); - break; - - case 13: // ↩ Return - accept(); - break; - } - } - - - // Called whenever the input value is changed (e.g. on typing) - function change() { - fetchComboData(value(), function() { - _selected = null; - var val = input.property('value'); - - if (_suggestions.length) { - if (input.property('selectionEnd') === val.length) { - _selected = tryAutocomplete(); - } - - if (!_selected) { - _selected = val; - } - } - - if (val.length) { - var combo = container.selectAll('.combobox'); - if (combo.empty()) { - show(); - } - } else { - hide(); - } - - render(); - }); - } - - - // Called when the user presses up/down arrows to navigate the list - function nav(dir) { - if (_suggestions.length) { - // try to determine previously selected index.. - var index = -1; - for (var i = 0; i < _suggestions.length; i++) { - if (_selected && _suggestions[i].value === _selected) { - index = i; - break; - } - } - - // pick new _selected - index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0); - _selected = _suggestions[index].value; - input.property('value', _selected); - } - - render(); - ensureVisible(); - } - - - function ensureVisible() { - var combo = container.selectAll('.combobox'); - if (combo.empty()) return; - - var containerRect = container.node().getBoundingClientRect(); - var comboRect = combo.node().getBoundingClientRect(); - - if (comboRect.bottom > containerRect.bottom) { - var node = attachTo ? attachTo.node() : input.node(); - node.scrollIntoView({ behavior: 'instant', block: 'center' }); - render(); - } - - // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move - var selected = combo.selectAll('.combobox-option.selected').node(); - if (selected) { - selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } - } - - - function value() { - var value = input.property('value'); - var start = input.property('selectionStart'); - var end = input.property('selectionEnd'); - - if (start && end) { - value = value.substring(0, start); - } - - return value; - } - - - function fetchComboData(v, cb) { - _cancelFetch = false; - - _fetcher.call(input, v, function(results) { - // already chose a value, don't overwrite or autocomplete it - if (_cancelFetch) return; - - _suggestions = results; - results.forEach(function(d) { _fetched[d.value] = d; }); - - if (cb) { - cb(); - } - }); - } - - - function tryAutocomplete() { - if (!_canAutocomplete) return; - - var val = _caseSensitive ? value() : value().toLowerCase(); - if (!val) return; - - // Don't autocomplete if user is typing a number - #4935 - if (!isNaN(parseFloat(val)) && isFinite(val)) return; - - var bestIndex = -1; - for (var i = 0; i < _suggestions.length; i++) { - var suggestion = _suggestions[i].value; - var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); - - // if search string matches suggestion exactly, pick it.. - if (compare === val) { - bestIndex = i; - break; - - // otherwise lock in the first result that starts with the search string.. - } else if (bestIndex === -1 && compare.indexOf(val) === 0) { - bestIndex = i; - } - } - - if (bestIndex !== -1) { - var bestVal = _suggestions[bestIndex].value; - input.property('value', bestVal); - input.node().setSelectionRange(val.length, bestVal.length); - return bestVal; - } - } - - - function render() { - if (_suggestions.length < _minItems || document.activeElement !== input.node()) { - hide(); - return; - } - - var shown = !container.selectAll('.combobox').empty(); - if (!shown) return; - - var combo = container.selectAll('.combobox'); - var options = combo.selectAll('.combobox-option') - .data(_suggestions, function(d) { return d.value; }); - - options.exit() - .remove(); - - // enter/update - options.enter() - .append('a') - .attr('class', 'combobox-option') - .attr('title', function(d) { return d.title; }) - .text(function(d) { return d.display || d.value; }) - .on('mouseenter', _mouseEnterHandler) - .on('mouseleave', _mouseLeaveHandler) - .merge(options) - .classed('selected', function(d) { return d.value === _selected; }) - .on('click.combo-option', accept) - .order(); - - var node = attachTo ? attachTo.node() : input.node(); - var containerRect = container.node().getBoundingClientRect(); - var rect = node.getBoundingClientRect(); - - combo - .style('left', (rect.left + 5 - containerRect.left) + 'px') - .style('width', (rect.width - 10) + 'px') - .style('top', (rect.height + rect.top - containerRect.top) + 'px'); - } - - - // Dispatches an 'accept' event - // Then hides the combobox. - function accept(d) { - _cancelFetch = true; - var thiz = input.node(); - - if (d) { // user clicked on a suggestion - utilGetSetValue(input, d.value); // replace field contents - utilTriggerEvent(input, 'change'); - } - - // clear (and keep) selection - var val = utilGetSetValue(input); - thiz.setSelectionRange(val.length, val.length); - - d = _fetched[val]; - dispatch$1.call('accept', thiz, d, val); - hide(); - } - - - // Dispatches an 'cancel' event - // Then hides the combobox. - function cancel() { - _cancelFetch = true; - var thiz = input.node(); - - // clear (and remove) selection, and replace field contents - var val = utilGetSetValue(input); - var start = input.property('selectionStart'); - var end = input.property('selectionEnd'); - val = val.slice(0, start) + val.slice(end); - utilGetSetValue(input, val); - thiz.setSelectionRange(val.length, val.length); - - dispatch$1.call('cancel', thiz); - hide(); - } - - }; - - - combobox.canAutocomplete = function(val) { - if (!arguments.length) return _canAutocomplete; - _canAutocomplete = val; - return combobox; - }; - - combobox.caseSensitive = function(val) { - if (!arguments.length) return _caseSensitive; - _caseSensitive = val; - return combobox; - }; - - combobox.data = function(val) { - if (!arguments.length) return _data; - _data = val; - return combobox; - }; - - combobox.fetcher = function(val) { - if (!arguments.length) return _fetcher; - _fetcher = val; - return combobox; - }; - - combobox.minItems = function(val) { - if (!arguments.length) return _minItems; - _minItems = val; - return combobox; - }; - - combobox.itemsMouseEnter = function(val) { - if (!arguments.length) return _mouseEnterHandler; - _mouseEnterHandler = val; - return combobox; - }; - - combobox.itemsMouseLeave = function(val) { - if (!arguments.length) return _mouseLeaveHandler; - _mouseLeaveHandler = val; - return combobox; - }; - - return utilRebind(combobox, dispatch$1, 'on'); - } - - - uiCombobox.off = function(input, context) { - input - .on('focus.combo-input', null) - .on('blur.combo-input', null) - .on('keydown.combo-input', null) - .on('keyup.combo-input', null) - .on('input.combo-input', null) - .on('mousedown.combo-input', null) - .on('mouseup.combo-input', null); - - - context.container() - .on('scroll.combo-scroll', null); - }; - - // toggles the visibility of ui elements, using a combination of the - // hide class, which sets display=none, and a d3 transition for opacity. - // this will cause blinking when called repeatedly, so check that the - // value actually changes between calls. - function uiToggle(show, callback) { - return function(selection) { - selection - .style('opacity', show ? 0 : 1) - .classed('hide', false) - .transition() - .style('opacity', show ? 1 : 0) - .on('end', function() { - select(this) - .classed('hide', !show) - .style('opacity', null); - if (callback) callback.apply(this); - }); - }; - } - - function uiDisclosure(context, key, expandedDefault) { - var dispatch$1 = dispatch('toggled'); - var _expanded; - var _title = utilFunctor(''); - var _updatePreference = true; - var _content = function () {}; - - - var disclosure = function(selection) { - - if (_expanded === undefined || _expanded === null) { - // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)` - - var preference = corePreferences('disclosure.' + key + '.expanded'); - _expanded = preference === null ? !!expandedDefault : (preference === 'true'); - } - - var hideToggle = selection.selectAll('.hide-toggle-' + key) - .data([0]); - - // enter - var hideToggleEnter = hideToggle.enter() - .append('a') - .attr('href', '#') - .attr('class', 'hide-toggle hide-toggle-' + key) - .call(svgIcon('', 'pre-text', 'hide-toggle-icon')); - - hideToggleEnter - .append('span') - .attr('class', 'hide-toggle-text'); - - // update - hideToggle = hideToggleEnter - .merge(hideToggle); - - hideToggle - .on('click', toggle) - .classed('expanded', _expanded); - - hideToggle.selectAll('.hide-toggle-text') - .text(_title()); - - hideToggle.selectAll('.hide-toggle-icon') - .attr('xlink:href', _expanded ? '#iD-icon-down' - : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward' - ); - - - var wrap = selection.selectAll('.disclosure-wrap') - .data([0]); - - // enter/update - wrap = wrap.enter() - .append('div') - .attr('class', 'disclosure-wrap disclosure-wrap-' + key) - .merge(wrap) - .classed('hide', !_expanded); - - if (_expanded) { - wrap - .call(_content); - } - - - function toggle() { - event.preventDefault(); - - _expanded = !_expanded; - - if (_updatePreference) { - corePreferences('disclosure.' + key + '.expanded', _expanded); - } - - hideToggle - .classed('expanded', _expanded); - - hideToggle.selectAll('.hide-toggle-icon') - .attr('xlink:href', _expanded ? '#iD-icon-down' - : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward' - ); - - wrap - .call(uiToggle(_expanded)); - - if (_expanded) { - wrap - .call(_content); - } - - dispatch$1.call('toggled', this, _expanded); - } - }; - - - disclosure.title = function(val) { - if (!arguments.length) return _title; - _title = utilFunctor(val); - return disclosure; - }; - - - disclosure.expanded = function(val) { - if (!arguments.length) return _expanded; - _expanded = val; - return disclosure; - }; - - - disclosure.updatePreference = function(val) { - if (!arguments.length) return _updatePreference; - _updatePreference = val; - return disclosure; - }; - - - disclosure.content = function(val) { - if (!arguments.length) return _content; - _content = val; - return disclosure; - }; - - - return utilRebind(disclosure, dispatch$1, 'on'); - } - - // A unit of controls or info to be used in a layout, such as within a pane. - // Can be labeled and collapsible. - function uiSection(id, context) { - - var _classes = utilFunctor(''); - var _shouldDisplay; - var _content; - - var _disclosure; - var _title; - var _expandedByDefault = utilFunctor(true); - var _disclosureContent; - var _disclosureExpanded; - - var _containerSelection = select(null); - - var section = { - id: id - }; - - section.classes = function(val) { - if (!arguments.length) return _classes; - _classes = utilFunctor(val); - return section; - }; - - section.title = function(val) { - if (!arguments.length) return _title; - _title = utilFunctor(val); - return section; - }; - - section.expandedByDefault = function(val) { - if (!arguments.length) return _expandedByDefault; - _expandedByDefault = utilFunctor(val); - return section; - }; - - section.shouldDisplay = function(val) { - if (!arguments.length) return _shouldDisplay; - _shouldDisplay = utilFunctor(val); - return section; - }; - - section.content = function(val) { - if (!arguments.length) return _content; - _content = val; - return section; - }; - - section.disclosureContent = function(val) { - if (!arguments.length) return _disclosureContent; - _disclosureContent = val; - return section; - }; - - section.disclosureExpanded = function(val) { - if (!arguments.length) return _disclosureExpanded; - _disclosureExpanded = val; - return section; - }; - - // may be called multiple times - section.render = function(selection) { - - _containerSelection = selection - .selectAll('.section-' + id) - .data([0]); - - var sectionEnter = _containerSelection - .enter() - .append('div') - .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || '')); - - _containerSelection = sectionEnter - .merge(_containerSelection); - - _containerSelection - .call(renderContent); - }; - - section.reRender = function() { - _containerSelection - .call(renderContent); - }; - - section.selection = function() { - return _containerSelection; - }; - - section.disclosure = function() { - return _disclosure; - }; - - // may be called multiple times - function renderContent(selection) { - if (_shouldDisplay) { - var shouldDisplay = _shouldDisplay(); - selection.classed('hide', !shouldDisplay); - if (!shouldDisplay) { - selection.html(''); - return; - } - } - - if (_disclosureContent) { - if (!_disclosure) { - _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()) - .title(_title || '') - /*.on('toggled', function(expanded) { - if (expanded) { selection.node().parentNode.scrollTop += 200; } - })*/ - .content(_disclosureContent); - } - if (_disclosureExpanded !== undefined) { - _disclosure.expanded(_disclosureExpanded); - _disclosureExpanded = undefined; - } - selection - .call(_disclosure); - - return; - } - - if (_content) { - selection - .call(_content); - } - } - - return section; - } - - // Pass `which` object of the form: - // { - // key: 'string', // required - // value: 'string' // optional - // } - // -or- - // { - // rtype: 'string' // relation type (e.g. 'multipolygon') - // } - // -or- - // { - // qid: 'string' // brand wikidata (e.g. 'Q37158') - // } - // - function uiTagReference(what) { - var wikibase = what.qid ? services.wikidata : services.osmWikibase; - var tagReference = {}; - - var _button = select(null); - var _body = select(null); - var _loaded; - var _showing; - - - function load() { - if (!wikibase) return; - - _button - .classed('tag-reference-loading', true); - - wikibase.getDocs(what, gotDocs); - } - - - function gotDocs(err, docs) { - _body.html(''); - - if (!docs || !docs.title) { - _body - .append('p') - .attr('class', 'tag-reference-description') - .text(_t('inspector.no_documentation_key')); - done(); - return; - } - - if (docs.imageURL) { - _body - .append('img') - .attr('class', 'tag-reference-wiki-image') - .attr('src', docs.imageURL) - .on('load', function() { done(); }) - .on('error', function() { select(this).remove(); done(); }); - } else { - done(); - } - - _body - .append('p') - .attr('class', 'tag-reference-description') - .text(docs.description || _t('inspector.no_documentation_key')) - .append('a') - .attr('class', 'tag-reference-edit') - .attr('target', '_blank') - .attr('tabindex', -1) - .attr('title', _t('inspector.edit_reference')) - .attr('href', docs.editURL) - .call(svgIcon('#iD-icon-edit', 'inline')); - - if (docs.wiki) { - _body - .append('a') - .attr('class', 'tag-reference-link') - .attr('target', '_blank') - .attr('tabindex', -1) - .attr('href', docs.wiki.url) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(_t(docs.wiki.text)); - } - - // Add link to info about "good changeset comments" - #2923 - if (what.key === 'comment') { - _body - .append('a') - .attr('class', 'tag-reference-comment-link') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .attr('href', _t('commit.about_changeset_comments_link')) - .append('span') - .text(_t('commit.about_changeset_comments')); - } - } - - - function done() { - _loaded = true; - - _button - .classed('tag-reference-loading', false); - - _body - .classed('expanded', true) - .transition() - .duration(200) - .style('max-height', '200px') - .style('opacity', '1'); - - _showing = true; - - _button.selectAll('svg.icon use').each(function() { - var iconUse = select(this); - if (iconUse.attr('href') === '#iD-icon-info') { - iconUse.attr('href', '#iD-icon-info-filled'); - } - }); - } - - - function hide() { - _body - .transition() - .duration(200) - .style('max-height', '0px') - .style('opacity', '0') - .on('end', function () { - _body.classed('expanded', false); - }); - - _showing = false; - - _button.selectAll('svg.icon use').each(function() { - var iconUse = select(this); - if (iconUse.attr('href') === '#iD-icon-info-filled') { - iconUse.attr('href', '#iD-icon-info'); - } - }); - - } - - - tagReference.button = function(selection, klass, iconName) { - _button = selection.selectAll('.tag-reference-button') - .data([0]); - - _button = _button.enter() - .append('button') - .attr('class', 'tag-reference-button ' + klass) - .attr('title', _t('icons.information')) - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-' + (iconName || 'inspect'))) - .merge(_button); - - _button - .on('click', function () { - event.stopPropagation(); - event.preventDefault(); - this.blur(); // avoid keeping focus on the button - #4641 - if (_showing) { - hide(); - } else if (_loaded) { - done(); - } else { - load(); - } - }); - }; - - - tagReference.body = function(selection) { - var itemID = what.qid || what.rtype || (what.key + '-' + what.value); - _body = selection.selectAll('.tag-reference-body') - .data([itemID], function(d) { return d; }); - - _body.exit() - .remove(); - - _body = _body.enter() - .append('div') - .attr('class', 'tag-reference-body') - .style('max-height', '0') - .style('opacity', '0') - .merge(_body); - - if (_showing === false) { - hide(); - } - }; - - - tagReference.showing = function(val) { - if (!arguments.length) return _showing; - _showing = val; - return tagReference; - }; - - - return tagReference; - } - - function uiSectionRawTagEditor(id, context) { - - var section = uiSection(id, context) - .classes('raw-tag-editor') - .title(function() { - var count = Object.keys(_tags).filter(function(d) { return d; }).length; - return _t('inspector.title_count', { title: _t('inspector.tags'), count: count }); - }) - .expandedByDefault(false) - .disclosureContent(renderDisclosureContent); - - var taginfo = services.taginfo; - var dispatch$1 = dispatch('change'); - var availableViews = [ - { id: 'text', icon: '#fas-i-cursor' }, - { id: 'list', icon: '#fas-th-list' } - ]; - - var _tagView = (corePreferences('raw-tag-editor-view') || 'list'); // 'list, 'text' - var _readOnlyTags = []; - // the keys in the order we want them to display - var _orderedKeys = []; - var _showBlank = false; - var _pendingChange = null; - var _state; - var _presets; - var _tags; - var _entityIDs; - - function renderDisclosureContent(wrap) { - - // remove deleted keys - _orderedKeys = _orderedKeys.filter(function(key) { - return _tags[key] !== undefined; - }); - - // When switching to a different entity or changing the state (hover/select) - // reorder the keys alphabetically. - // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here. - // Otherwise leave their order alone - #5857, #5927 - var all = Object.keys(_tags).sort(); - var missingKeys = utilArrayDifference(all, _orderedKeys); - for (var i in missingKeys) { - _orderedKeys.push(missingKeys[i]); - } - - // assemble row data - var rowData = _orderedKeys.map(function(key, i) { - return { index: i, key: key, value: _tags[key] }; - }); - - // append blank row last, if necessary - if (!rowData.length || _showBlank) { - _showBlank = false; - rowData.push({ index: rowData.length, key: '', value: '' }); - } - - - // View Options - var options = wrap.selectAll('.raw-tag-options') - .data([0]); - - options.exit() - .remove(); - - var optionsEnter = options.enter() - .insert('div', ':first-child') - .attr('class', 'raw-tag-options'); - - var optionEnter = optionsEnter.selectAll('.raw-tag-option') - .data(availableViews, function(d) { return d.id; }) - .enter(); - - optionEnter - .append('button') - .attr('class', function(d) { - return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : ''); - }) - .attr('title', function(d) { return _t('icons.' + d.id); }) - .on('click', function(d) { - _tagView = d.id; - corePreferences('raw-tag-editor-view', d.id); - - wrap.selectAll('.raw-tag-option') - .classed('selected', function(datum) { return datum === d; }); - - wrap.selectAll('.tag-text') - .classed('hide', (d.id !== 'text')) - .each(setTextareaHeight); - - wrap.selectAll('.tag-list, .add-row') - .classed('hide', (d.id !== 'list')); - }) - .each(function(d) { - select(this) - .call(svgIcon(d.icon)); - }); - - - // View as Text - var textData = rowsToText(rowData); - var textarea = wrap.selectAll('.tag-text') - .data([0]); - - textarea = textarea.enter() - .append('textarea') - .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : '')) - .call(utilNoAuto) - .attr('placeholder', _t('inspector.key_value')) - .attr('spellcheck', 'false') - .merge(textarea); - - textarea - .call(utilGetSetValue, textData) - .each(setTextareaHeight) - .on('input', setTextareaHeight) - .on('blur', textChanged) - .on('change', textChanged); - - - // View as List - var list = wrap.selectAll('.tag-list') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')) - .merge(list); - - - // Container for the Add button - var addRowEnter = wrap.selectAll('.add-row') - .data([0]) - .enter() - .append('div') - .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : '')); - - addRowEnter - .append('button') - .attr('class', 'add-tag') - .call(svgIcon('#iD-icon-plus', 'light')) - .on('click', addTag); - - addRowEnter - .append('div') - .attr('class', 'space-value'); // preserve space - - addRowEnter - .append('div') - .attr('class', 'space-buttons'); // preserve space - - - // Tag list items - var items = list.selectAll('.tag-row') - .data(rowData, function(d) { return d.key; }); - - items.exit() - .each(unbind) - .remove(); - - - // Enter - var itemsEnter = items.enter() - .append('li') - .attr('class', 'tag-row') - .classed('readonly', isReadOnly); - - var innerWrap = itemsEnter.append('div') - .attr('class', 'inner-wrap'); - - innerWrap - .append('div') - .attr('class', 'key-wrap') - .append('input') - .property('type', 'text') - .attr('class', 'key') - .call(utilNoAuto) - .on('blur', keyChange) - .on('change', keyChange); - - innerWrap - .append('div') - .attr('class', 'value-wrap') - .append('input') - .property('type', 'text') - .attr('class', 'value') - .call(utilNoAuto) - .on('blur', valueChange) - .on('change', valueChange) - .on('keydown.push-more', pushMore); - - innerWrap - .append('button') - .attr('tabindex', -1) - .attr('class', 'form-field-button remove') - .attr('title', _t('icons.remove')) - .call(svgIcon('#iD-operation-delete')); - - - // Update - items = items - .merge(itemsEnter) - .sort(function(a, b) { return a.index - b.index; }); - - items - .each(function(d) { - var row = select(this); - var key = row.select('input.key'); // propagate bound data - var value = row.select('input.value'); // propagate bound data - - if (_entityIDs && taginfo && _state !== 'hover') { - bindTypeahead(key, value); - } - - var reference; - - if (typeof d.value !== 'string') { - reference = uiTagReference({ key: d.key }); - } else { - var isRelation = _entityIDs && _entityIDs.some(function(entityID) { - return context.entity(entityID).type === 'relation'; - }); - if (isRelation && d.key === 'type') { - reference = uiTagReference({ rtype: d.value }); - } else { - reference = uiTagReference({ key: d.key, value: d.value }); - } - } - - if (_state === 'hover') { - reference.showing(false); - } - - row.select('.inner-wrap') // propagate bound data - .call(reference.button); - - row.call(reference.body); - - row.select('button.remove'); // propagate bound data - }); - - items.selectAll('input.key') - .attr('title', function(d) { return d.key; }) - .call(utilGetSetValue, function(d) { return d.key; }) - .attr('readonly', function(d) { - return (isReadOnly(d) || (typeof d.value !== 'string')) || null; - }); - - items.selectAll('input.value') - .attr('title', function(d) { - return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value; - }) - .classed('mixed', function(d) { - return Array.isArray(d.value); - }) - .attr('placeholder', function(d) { - return typeof d.value === 'string' ? null : _t('inspector.multiple_values'); - }) - .call(utilGetSetValue, function(d) { - return typeof d.value === 'string' ? d.value : ''; - }) - .attr('readonly', function(d) { - return isReadOnly(d) || null; - }); - - items.selectAll('button.remove') - .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878 - - } - - function isReadOnly(d) { - for (var i = 0; i < _readOnlyTags.length; i++) { - if (d.key.match(_readOnlyTags[i]) !== null) { - return true; - } - } - return false; - } - - function setTextareaHeight() { - if (_tagView !== 'text') return; - - var selection = select(this); - selection.style('height', null); - selection.style('height', selection.node().scrollHeight + 5 + 'px'); - } - - function stringify(s) { - return JSON.stringify(s).slice(1, -1); // without leading/trailing " - } - - function unstringify(s) { - var leading = ''; - var trailing = ''; - if (s.length < 1 || s.charAt(0) !== '"') { - leading = '"'; - } - if (s.length < 2 || s.charAt(s.length - 1) !== '"' || - (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') - ) { - trailing = '"'; - } - return JSON.parse(leading + s + trailing); - } - - function rowsToText(rows) { - var str = rows - .filter(function(row) { return row.key && row.key.trim() !== ''; }) - .map(function(row) { - var rawVal = row.value; - if (typeof rawVal !== 'string') rawVal = '*'; - var val = rawVal ? stringify(rawVal) : ''; - return stringify(row.key) + '=' + val; - }) - .join('\n'); - - if (_state !== 'hover' && str.length) { - return str + '\n'; - } - return str; - } - - function textChanged() { - var newText = this.value.trim(); - var newTags = {}; - newText.split('\n').forEach(function(row) { - var m = row.match(/^\s*([^=]+)=(.*)$/); - if (m !== null) { - var k = context.cleanTagKey(unstringify(m[1].trim())); - var v = context.cleanTagValue(unstringify(m[2].trim())); - newTags[k] = v; - } - }); - - var tagDiff = utilTagDiff(_tags, newTags); - if (!tagDiff.length) return; - - _pendingChange = _pendingChange || {}; - - tagDiff.forEach(function(change) { - if (isReadOnly({ key: change.key })) return; - - // skip unchanged multiselection placeholders - if (change.newVal === '*' && typeof change.oldVal !== 'string') return; - - if (change.type === '-') { - _pendingChange[change.key] = undefined; - } else if (change.type === '+') { - _pendingChange[change.key] = change.newVal || ''; - } - }); - - if (Object.keys(_pendingChange).length === 0) { - _pendingChange = null; - return; - } - - scheduleChange(); - } - - function pushMore() { - // if pressing Tab on the last value field with content, add a blank row - if (event.keyCode === 9 && !event.shiftKey && - section.selection().selectAll('.tag-list li:last-child input.value').node() === this && - utilGetSetValue(select(this))) { - addTag(); - } - } - - function bindTypeahead(key, value) { - if (isReadOnly(key.datum())) return; - - if (Array.isArray(value.datum().value)) { - value.call(uiCombobox(context, 'tag-value') - .minItems(1) - .fetcher(function(value, callback) { - var keyString = utilGetSetValue(key); - if (!_tags[keyString]) return; - var data = _tags[keyString].filter(Boolean).map(function(tagValue) { - return { - value: tagValue, - title: tagValue - }; - }); - callback(data); - })); - return; - } - - var geometry = context.graph().geometry(_entityIDs[0]); - - key.call(uiCombobox(context, 'tag-key') - .fetcher(function(value, callback) { - taginfo.keys({ - debounce: true, - geometry: geometry, - query: value - }, function(err, data) { - if (!err) { - var filtered = data.filter(function(d) { return _tags[d.value] === undefined; }); - callback(sort(value, filtered)); - } - }); - })); - - value.call(uiCombobox(context, 'tag-value') - .fetcher(function(value, callback) { - taginfo.values({ - debounce: true, - key: utilGetSetValue(key), - geometry: geometry, - query: value - }, function(err, data) { - if (!err) callback(sort(value, data)); - }); - })); - - - function sort(value, data) { - var sameletter = []; - var other = []; - for (var i = 0; i < data.length; i++) { - if (data[i].value.substring(0, value.length) === value) { - sameletter.push(data[i]); - } else { - other.push(data[i]); - } - } - return sameletter.concat(other); - } - } - - function unbind() { - var row = select(this); - - row.selectAll('input.key') - .call(uiCombobox.off, context); - - row.selectAll('input.value') - .call(uiCombobox.off, context); - } - - function keyChange(d) { - if (select(this).attr('readonly')) return; - - var kOld = d.key; - - // exit if we are currently about to delete this row anyway - #6366 - if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return; - - var kNew = context.cleanTagKey(this.value.trim()); - - // allow no change if the key should be readonly - if (isReadOnly({ key: kNew })) { - this.value = kOld; - return; - } - - if (kNew && - kNew !== kOld && - _tags[kNew] !== undefined) { - // new key is already in use, switch focus to the existing row - - this.value = kOld; // reset the key - section.selection().selectAll('.tag-list input.value') - .each(function(d) { - if (d.key === kNew) { // send focus to that other value combo instead - var input = select(this).node(); - input.focus(); - input.select(); - } - }); - return; - } - - - var row = this.parentNode.parentNode; - var inputVal = select(row).selectAll('input.value'); - var vNew = context.cleanTagValue(utilGetSetValue(inputVal)); - - _pendingChange = _pendingChange || {}; - - if (kOld) { - _pendingChange[kOld] = undefined; - } - - _pendingChange[kNew] = vNew; - - // update the ordered key index so this row doesn't change position - var existingKeyIndex = _orderedKeys.indexOf(kOld); - if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew; - - d.key = kNew; // update datum to avoid exit/enter on tag update - d.value = vNew; - - this.value = kNew; - utilGetSetValue(inputVal, vNew); - scheduleChange(); - } - - function valueChange(d) { - if (isReadOnly(d)) return; - - // exit if this is a multiselection and no value was entered - if (typeof d.value !== 'string' && !this.value) return; - - // exit if we are currently about to delete this row anyway - #6366 - if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return; - - _pendingChange = _pendingChange || {}; - - _pendingChange[d.key] = context.cleanTagValue(this.value); - scheduleChange(); - } - - function removeTag(d) { - if (isReadOnly(d)) return; - - if (d.key === '') { // removing the blank row - _showBlank = false; - section.reRender(); - - } else { - // remove the key from the ordered key index - _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; }); - - _pendingChange = _pendingChange || {}; - _pendingChange[d.key] = undefined; - scheduleChange(); - } - } - - function addTag() { - // Delay render in case this click is blurring an edited combo. - // Without the setTimeout, the `content` render would wipe out the pending tag change. - window.setTimeout(function() { - _showBlank = true; - section.reRender(); - section.selection().selectAll('.tag-list li:last-child input.key').node().focus(); - }, 20); - } - - function scheduleChange() { - // Cache IDs in case the editor is reloaded before the change event is called. - #6028 - var entityIDs = _entityIDs; - - // Delay change in case this change is blurring an edited combo. - #5878 - window.setTimeout(function() { - if (!_pendingChange) return; - - dispatch$1.call('change', this, entityIDs, _pendingChange); - _pendingChange = null; - }, 10); - } - - - section.state = function(val) { - if (!arguments.length) return _state; - if (_state !== val) { - _orderedKeys = []; - _state = val; - } - return section; - }; - - - section.presets = function(val) { - if (!arguments.length) return _presets; - _presets = val; - if (_presets && _presets.length && _presets[0].isFallback()) { - section.disclosureExpanded(true); - } else { - section.disclosureExpanded(null); - } - return section; - }; - - - section.tags = function(val) { - if (!arguments.length) return _tags; - _tags = val; - return section; - }; - - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) { - _entityIDs = val; - _orderedKeys = []; - } - return section; - }; - - - // pass an array of regular expressions to test against the tag key - section.readOnlyTags = function(val) { - if (!arguments.length) return _readOnlyTags; - _readOnlyTags = val; - return section; - }; - - - return utilRebind(section, dispatch$1, 'on'); - } - - function uiDataEditor(context) { - var dataHeader = uiDataHeader(); - var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context) - .expandedByDefault(true) - .readOnlyTags([/./]); - var _datum; - - - function dataEditor(selection) { - - var header = selection.selectAll('.header') - .data([0]); - - var headerEnter = header.enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', function() { - context.enter(modeBrowse(context)); - }) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('map_data.title')); - - - var body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - var editor = body.selectAll('.data-editor') - .data([0]); - - // enter/update - editor.enter() - .append('div') - .attr('class', 'modal-section data-editor') - .merge(editor) - .call(dataHeader.datum(_datum)); - - var rte = body.selectAll('.raw-tag-editor') - .data([0]); - - // enter/update - rte.enter() - .append('div') - .attr('class', 'raw-tag-editor data-editor') - .merge(rte) - .call(rawTagEditor - .tags((_datum && _datum.properties) || {}) - .state('hover') - .render - ) - .selectAll('textarea.tag-text') - .attr('readonly', true) - .classed('readonly', true); - } - - - dataEditor.datum = function(val) { - if (!arguments.length) return _datum; - _datum = val; - return this; - }; - - - return dataEditor; - } - - function modeSelectData(context, selectedDatum) { - var mode = { - id: 'select-data', - button: 'browse' - }; - - var keybinding = utilKeybinding('select-data'); - var dataEditor = uiDataEditor(context); - - var behaviors = [ - behaviorBreathe(), - behaviorHover(context), - behaviorSelect(context), - behaviorLasso(context), - modeDragNode(context).behavior, - modeDragNote(context).behavior - ]; - - - // class the data as selected, or return to browse mode if the data is gone - function selectData(drawn) { - var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__); - - if (selection.empty()) { - // Return to browse mode if selected DOM elements have - // disappeared because the user moved them out of view.. - var source = event && event.type === 'zoom' && event.sourceEvent; - if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) { - context.enter(modeBrowse(context)); - } - } else { - selection.classed('selected', true); - } - } - - - function esc() { - if (context.container().select('.combobox').size()) return; - context.enter(modeBrowse(context)); - } - - - mode.zoomToSelected = function() { - var extent = geoExtent(d3_geoBounds(selectedDatum)); - context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent)); - }; - - - mode.enter = function() { - behaviors.forEach(context.install); - - keybinding - .on(_t('inspector.zoom_to.key'), mode.zoomToSelected) - .on('⎋', esc, true); - - select(document) - .call(keybinding); - - selectData(); - - var sidebar = context.ui().sidebar; - sidebar.show(dataEditor.datum(selectedDatum)); - - // expand the sidebar, avoid obscuring the data if needed - var extent = geoExtent(d3_geoBounds(selectedDatum)); - sidebar.expand(sidebar.intersects(extent)); - - context.map() - .on('drawn.select-data', selectData); - }; - - - mode.exit = function() { - behaviors.forEach(context.uninstall); - - select(document) - .call(keybinding.unbind); - - context.surface() - .selectAll('.layer-mapdata .selected') - .classed('selected hover', false); - - context.map() - .on('drawn.select-data', null); - - context.ui().sidebar - .hide(); - }; - - - return mode; - } - - function uiImproveOsmComments() { - let _qaItem; - - function issueComments(selection) { - // make the div immediately so it appears above the buttons - let comments = selection.selectAll('.comments-container') - .data([0]); - - comments = comments.enter() - .append('div') - .attr('class', 'comments-container') - .merge(comments); - - // must retrieve comments from API before they can be displayed - services.improveOSM.getComments(_qaItem) - .then(d => { - if (!d.comments) return; // nothing to do here - - const commentEnter = comments.selectAll('.comment') - .data(d.comments) - .enter() - .append('div') - .attr('class', 'comment'); - - commentEnter - .append('div') - .attr('class', 'comment-avatar') - .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); - - const mainEnter = commentEnter - .append('div') - .attr('class', 'comment-main'); - - const metadataEnter = mainEnter - .append('div') - .attr('class', 'comment-metadata'); - - metadataEnter - .append('div') - .attr('class', 'comment-author') - .each(function(d) { - const osm = services.osm; - let selection = select(this); - if (osm && d.username) { - selection = selection - .append('a') - .attr('class', 'comment-author-link') - .attr('href', osm.userURL(d.username)) - .attr('tabindex', -1) - .attr('target', '_blank'); - } - selection - .text(d => d.username); - }); - - metadataEnter - .append('div') - .attr('class', 'comment-date') - .text(d => _t('note.status.commented', { when: localeDateString(d.timestamp) })); - - mainEnter - .append('div') - .attr('class', 'comment-text') - .append('p') - .text(d => d.text); - }) - .catch(err => { - console.log(err); // eslint-disable-line no-console - }); - } - - function localeDateString(s) { - if (!s) return null; - const options = { day: 'numeric', month: 'short', year: 'numeric' }; - const d = new Date(s * 1000); // timestamp is served in seconds, date takes ms - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(_mainLocalizer.localeCode(), options); - } - - issueComments.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return issueComments; - }; - - return issueComments; - } - - function uiImproveOsmDetails(context) { - let _qaItem; - - - function issueDetail(d) { - if (d.desc) return d.desc; - const issueKey = d.issueKey; - d.replacements = d.replacements || {}; - d.replacements.default = _t('inspector.unknown'); // special key `default` works as a fallback string - return _t(`QA.improveOSM.error_types.${issueKey}.description`, d.replacements); - } - - - function improveOsmDetails(selection) { - const details = selection.selectAll('.error-details') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - details.exit() - .remove(); - - const detailsEnter = details.enter() - .append('div') - .attr('class', 'error-details qa-details-container'); - - - // description - const descriptionEnter = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - descriptionEnter - .append('h4') - .text(() => _t('QA.keepRight.detail_description')); - - descriptionEnter - .append('div') - .attr('class', 'qa-details-description-text') - .html(issueDetail); - - // If there are entity links in the error message.. - let relatedEntities = []; - descriptionEnter.selectAll('.error_entity_link, .error_object_link') - .each(function() { - const link = select(this); - const isObjectLink = link.classed('error_object_link'); - const entityID = isObjectLink ? - (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId) - : this.textContent; - const entity = context.hasEntity(entityID); - - relatedEntities.push(entityID); - - // Add click handler - link - .on('mouseenter', () => { - utilHighlightEntities([entityID], true, context); - }) - .on('mouseleave', () => { - utilHighlightEntities([entityID], false, context); - }) - .on('click', () => { - event.preventDefault(); - - utilHighlightEntities([entityID], false, context); - - const osmlayer = context.layers().layer('osm'); - if (!osmlayer.enabled()) { - osmlayer.enabled(true); - } - - context.map().centerZoom(_qaItem.loc, 20); - - if (entity) { - context.enter(modeSelect(context, [entityID])); - } else { - context.loadEntity(entityID, () => { - context.enter(modeSelect(context, [entityID])); - }); - } - }); - - // Replace with friendly name if possible - // (The entity may not yet be loaded into the graph) - if (entity) { - let name = utilDisplayName(entity); // try to use common name - - if (!name && !isObjectLink) { - const preset = _mainPresetIndex.match(entity, context.graph()); - name = preset && !preset.isFallback() && preset.name(); // fallback to preset name - } - - if (name) { - this.innerText = name; - } - } - }); - - // Don't hide entities related to this error - #5880 - context.features().forceVisible(relatedEntities); - context.map().pan([0,0]); // trigger a redraw - } - - improveOsmDetails.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmDetails; - }; - - return improveOsmDetails; - } - - function uiImproveOsmHeader() { - let _qaItem; - - - function issueTitle(d) { - const issueKey = d.issueKey; - d.replacements = d.replacements || {}; - d.replacements.default = _t('inspector.unknown'); // special key `default` works as a fallback string - return _t(`QA.improveOSM.error_types.${issueKey}.title`, d.replacements); - } - - - function improveOsmHeader(selection) { - const header = selection.selectAll('.qa-header') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - header.exit() - .remove(); - - const headerEnter = header.enter() - .append('div') - .attr('class', 'qa-header'); - - const svgEnter = headerEnter - .append('div') - .attr('class', 'qa-header-icon') - .classed('new', d => d.id < 0) - .append('svg') - .attr('width', '20px') - .attr('height', '30px') - .attr('viewbox', '0 0 20 30') - .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - svgEnter - .append('polygon') - .attr('fill', 'currentColor') - .attr('class', 'qaItem-fill') - .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); - - svgEnter - .append('use') - .attr('class', 'icon-annotation') - .attr('width', '13px') - .attr('height', '13px') - .attr('transform', 'translate(3.5, 5)') - .attr('xlink:href', d => { - const picon = d.icon; - if (!picon) { - return ''; - } else { - const isMaki = /^maki-/.test(picon); - return `#${picon}${isMaki ? '-11' : ''}`; - } - }); - - headerEnter - .append('div') - .attr('class', 'qa-header-label') - .text(issueTitle); - } - - improveOsmHeader.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmHeader; - }; - - return improveOsmHeader; - } - - function uiImproveOsmEditor(context) { - const dispatch$1 = dispatch('change'); - const qaDetails = uiImproveOsmDetails(context); - const qaComments = uiImproveOsmComments(); - const qaHeader = uiImproveOsmHeader(); - - let _qaItem; - - function improveOsmEditor(selection) { - - const headerEnter = selection.selectAll('.header') - .data([0]) - .enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', () => context.enter(modeBrowse(context))) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('QA.improveOSM.title')); - - let body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - const editor = body.selectAll('.qa-editor') - .data([0]); - - editor.enter() - .append('div') - .attr('class', 'modal-section qa-editor') - .merge(editor) - .call(qaHeader.issue(_qaItem)) - .call(qaDetails.issue(_qaItem)) - .call(qaComments.issue(_qaItem)) - .call(improveOsmSaveSection); - } - - function improveOsmSaveSection(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment)); - let saveSection = selection.selectAll('.qa-save') - .data( - (isShown ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - // exit - saveSection.exit() - .remove(); - - // enter - const saveSectionEnter = saveSection.enter() - .append('div') - .attr('class', 'qa-save save-section cf'); - - saveSectionEnter - .append('h4') - .attr('class', '.qa-save-header') - .text(_t('note.newComment')); - - saveSectionEnter - .append('textarea') - .attr('class', 'new-comment-input') - .attr('placeholder', _t('QA.keepRight.comment_placeholder')) - .attr('maxlength', 1000) - .property('value', d => d.newComment) - .call(utilNoAuto) - .on('input', changeInput) - .on('blur', changeInput); - - // update - saveSection = saveSectionEnter - .merge(saveSection) - .call(qaSaveButtons); - - function changeInput() { - const input = select(this); - let val = input.property('value').trim(); - - if (val === '') { - val = undefined; - } - - // store the unsaved comment with the issue itself - _qaItem = _qaItem.update({ newComment: val }); - - const qaService = services.improveOSM; - if (qaService) { - qaService.replaceItem(_qaItem); - } - - saveSection - .call(qaSaveButtons); - } - } - - function qaSaveButtons(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - let buttonSection = selection.selectAll('.buttons') - .data((isSelected ? [_qaItem] : []), d => d.status + d.id); - - // exit - buttonSection.exit() - .remove(); - - // enter - const buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons'); - - buttonEnter - .append('button') - .attr('class', 'button comment-button action') - .text(_t('QA.keepRight.save_comment')); - - buttonEnter - .append('button') - .attr('class', 'button close-button action'); - - buttonEnter - .append('button') - .attr('class', 'button ignore-button action'); - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.select('.comment-button') - .attr('disabled', d => d.newComment ? null : true) - .on('click.comment', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - - buttonSection.select('.close-button') - .text(d => { - const andComment = (d.newComment ? '_comment' : ''); - return _t(`QA.keepRight.close${andComment}`); - }) - .on('click.close', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - d.newStatus = 'SOLVED'; - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - - buttonSection.select('.ignore-button') - .text(d => { - const andComment = (d.newComment ? '_comment' : ''); - return _t(`QA.keepRight.ignore${andComment}`); - }) - .on('click.ignore', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.improveOSM; - if (qaService) { - d.newStatus = 'INVALID'; - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - } - - // NOTE: Don't change method name until UI v3 is merged - improveOsmEditor.error = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return improveOsmEditor; - }; - - return utilRebind(improveOsmEditor, dispatch$1, 'on'); - } - - function uiKeepRightDetails(context) { - let _qaItem; - - - function issueDetail(d) { - const { itemType, parentIssueType } = d; - const unknown = _t('inspector.unknown'); - let replacements = d.replacements || {}; - replacements.default = unknown; // special key `default` works as a fallback string - - let detail = _t(`QA.keepRight.errorTypes.${itemType}.description`, replacements); - if (detail === unknown) { - detail = _t(`QA.keepRight.errorTypes.${parentIssueType}.description`, replacements); - } - return detail; - } - - - function keepRightDetails(selection) { - const details = selection.selectAll('.error-details') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - details.exit() - .remove(); - - const detailsEnter = details.enter() - .append('div') - .attr('class', 'error-details qa-details-container'); - - // description - const descriptionEnter = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - descriptionEnter - .append('h4') - .text(() => _t('QA.keepRight.detail_description')); - - descriptionEnter - .append('div') - .attr('class', 'qa-details-description-text') - .html(issueDetail); - - // If there are entity links in the error message.. - let relatedEntities = []; - descriptionEnter.selectAll('.error_entity_link, .error_object_link') - .each(function() { - const link = select(this); - const isObjectLink = link.classed('error_object_link'); - const entityID = isObjectLink ? - (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId) - : this.textContent; - const entity = context.hasEntity(entityID); - - relatedEntities.push(entityID); - - // Add click handler - link - .on('mouseenter', () => { - utilHighlightEntities([entityID], true, context); - }) - .on('mouseleave', () => { - utilHighlightEntities([entityID], false, context); - }) - .on('click', () => { - event.preventDefault(); - - utilHighlightEntities([entityID], false, context); - - const osmlayer = context.layers().layer('osm'); - if (!osmlayer.enabled()) { - osmlayer.enabled(true); - } - - context.map().centerZoomEase(_qaItem.loc, 20); - - if (entity) { - context.enter(modeSelect(context, [entityID])); - } else { - context.loadEntity(entityID, () => { - context.enter(modeSelect(context, [entityID])); - }); - } - }); - - // Replace with friendly name if possible - // (The entity may not yet be loaded into the graph) - if (entity) { - let name = utilDisplayName(entity); // try to use common name - - if (!name && !isObjectLink) { - const preset = _mainPresetIndex.match(entity, context.graph()); - name = preset && !preset.isFallback() && preset.name(); // fallback to preset name - } - - if (name) { - this.innerText = name; - } - } - }); - - // Don't hide entities related to this issue - #5880 - context.features().forceVisible(relatedEntities); - context.map().pan([0,0]); // trigger a redraw - } - - keepRightDetails.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return keepRightDetails; - }; - - return keepRightDetails; - } - - function uiKeepRightHeader() { - let _qaItem; - - - function issueTitle(d) { - const { itemType, parentIssueType } = d; - const unknown = _t('inspector.unknown'); - let replacements = d.replacements || {}; - replacements.default = unknown; // special key `default` works as a fallback string - - let title = _t(`QA.keepRight.errorTypes.${itemType}.title`, replacements); - if (title === unknown) { - title = _t(`QA.keepRight.errorTypes.${parentIssueType}.title`, replacements); - } - return title; - } - - - function keepRightHeader(selection) { - const header = selection.selectAll('.qa-header') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - header.exit() - .remove(); - - const headerEnter = header.enter() - .append('div') - .attr('class', 'qa-header'); - - const iconEnter = headerEnter - .append('div') - .attr('class', 'qa-header-icon') - .classed('new', d => d.id < 0); - - iconEnter - .append('div') - .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`) - .call(svgIcon('#iD-icon-bolt', 'qaItem-fill')); - - headerEnter - .append('div') - .attr('class', 'qa-header-label') - .text(issueTitle); - } - - - keepRightHeader.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return keepRightHeader; - }; - - return keepRightHeader; - } - - function uiViewOnKeepRight() { - let _qaItem; - - function viewOnKeepRight(selection) { - let url; - if (services.keepRight && (_qaItem instanceof QAItem)) { - url = services.keepRight.issueURL(_qaItem); - } - - const link = selection.selectAll('.view-on-keepRight') - .data(url ? [url] : []); - - // exit - link.exit() - .remove(); - - // enter - const linkEnter = link.enter() - .append('a') - .attr('class', 'view-on-keepRight') - .attr('target', '_blank') - .attr('rel', 'noopener') // security measure - .attr('href', d => d) - .call(svgIcon('#iD-icon-out-link', 'inline')); - - linkEnter - .append('span') - .text(_t('inspector.view_on_keepRight')); - } - - viewOnKeepRight.what = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return viewOnKeepRight; - }; - - return viewOnKeepRight; - } - - function uiKeepRightEditor(context) { - const dispatch$1 = dispatch('change'); - const qaDetails = uiKeepRightDetails(context); - const qaHeader = uiKeepRightHeader(); - - let _qaItem; - - function keepRightEditor(selection) { - - const headerEnter = selection.selectAll('.header') - .data([0]) - .enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', () => context.enter(modeBrowse(context))) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('QA.keepRight.title')); - - - let body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - const editor = body.selectAll('.qa-editor') - .data([0]); - - editor.enter() - .append('div') - .attr('class', 'modal-section qa-editor') - .merge(editor) - .call(qaHeader.issue(_qaItem)) - .call(qaDetails.issue(_qaItem)) - .call(keepRightSaveSection); - - - const footer = selection.selectAll('.footer') - .data([0]); - - footer.enter() - .append('div') - .attr('class', 'footer') - .merge(footer) - .call(uiViewOnKeepRight().what(_qaItem)); - } - - - function keepRightSaveSection(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - const isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment)); - let saveSection = selection.selectAll('.qa-save') - .data( - (isShown ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - // exit - saveSection.exit() - .remove(); - - // enter - const saveSectionEnter = saveSection.enter() - .append('div') - .attr('class', 'qa-save save-section cf'); - - saveSectionEnter - .append('h4') - .attr('class', '.qa-save-header') - .text(_t('QA.keepRight.comment')); - - saveSectionEnter - .append('textarea') - .attr('class', 'new-comment-input') - .attr('placeholder', _t('QA.keepRight.comment_placeholder')) - .attr('maxlength', 1000) - .property('value', d => d.newComment || d.comment) - .call(utilNoAuto) - .on('input', changeInput) - .on('blur', changeInput); - - // update - saveSection = saveSectionEnter - .merge(saveSection) - .call(qaSaveButtons); - - function changeInput() { - const input = select(this); - let val = input.property('value').trim(); - - if (val === _qaItem.comment) { - val = undefined; - } - - // store the unsaved comment with the issue itself - _qaItem = _qaItem.update({ newComment: val }); - - const qaService = services.keepRight; - if (qaService) { - qaService.replaceItem(_qaItem); // update keepright cache - } - - saveSection - .call(qaSaveButtons); - } - } - - - function qaSaveButtons(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - let buttonSection = selection.selectAll('.buttons') - .data((isSelected ? [_qaItem] : []), d => d.status + d.id); - - // exit - buttonSection.exit() - .remove(); - - // enter - const buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons'); - - buttonEnter - .append('button') - .attr('class', 'button comment-button action') - .text(_t('QA.keepRight.save_comment')); - - buttonEnter - .append('button') - .attr('class', 'button close-button action'); - - buttonEnter - .append('button') - .attr('class', 'button ignore-button action'); - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.select('.comment-button') // select and propagate data - .attr('disabled', d => d.newComment ? null : true) - .on('click.comment', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.keepRight; - if (qaService) { - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - - buttonSection.select('.close-button') // select and propagate data - .text(d => { - const andComment = (d.newComment ? '_comment' : ''); - return _t(`QA.keepRight.close${andComment}`); - }) - .on('click.close', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.keepRight; - if (qaService) { - d.newStatus = 'ignore_t'; // ignore temporarily (item fixed) - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - - buttonSection.select('.ignore-button') // select and propagate data - .text(d => { - const andComment = (d.newComment ? '_comment' : ''); - return _t(`QA.keepRight.ignore${andComment}`); - }) - .on('click.ignore', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.keepRight; - if (qaService) { - d.newStatus = 'ignore'; // ignore permanently (false positive) - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - } - - // NOTE: Don't change method name until UI v3 is merged - keepRightEditor.error = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return keepRightEditor; - }; - - return utilRebind(keepRightEditor, dispatch$1, 'on'); - } - - function uiOsmoseDetails(context) { - let _qaItem; - - function issueString(d, type) { - if (!d) return ''; - - // Issue strings are cached from Osmose API - const s = services.osmose.getStrings(d.itemType); - return (type in s) ? s[type] : ''; - } - - - function osmoseDetails(selection) { - const details = selection.selectAll('.error-details') - .data( - _qaItem ? [_qaItem] : [], - d => `${d.id}-${d.status || 0}` - ); - - details.exit() - .remove(); - - const detailsEnter = details.enter() - .append('div') - .attr('class', 'error-details qa-details-container'); - - - // Description - if (issueString(_qaItem, 'detail')) { - const div = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - div - .append('h4') - .text(() => _t('QA.keepRight.detail_description')); - - div - .append('p') - .attr('class', 'qa-details-description-text') - .html(d => issueString(d, 'detail')) - .selectAll('a') - .attr('rel', 'noopener') - .attr('target', '_blank'); - } - - // Elements (populated later as data is requested) - const detailsDiv = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - const elemsDiv = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - // Suggested Fix (musn't exist for every issue type) - if (issueString(_qaItem, 'fix')) { - const div = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - div - .append('h4') - .text(() => _t('QA.osmose.fix_title')); - - div - .append('p') - .html(d => issueString(d, 'fix')) - .selectAll('a') - .attr('rel', 'noopener') - .attr('target', '_blank'); - } - - // Common Pitfalls (musn't exist for every issue type) - if (issueString(_qaItem, 'trap')) { - const div = detailsEnter - .append('div') - .attr('class', 'qa-details-subsection'); - - div - .append('h4') - .text(() => _t('QA.osmose.trap_title')); - - div - .append('p') - .html(d => issueString(d, 'trap')) - .selectAll('a') - .attr('rel', 'noopener') - .attr('target', '_blank'); - } - - // Save current item to check if UI changed by time request resolves - const thisItem = _qaItem; - services.osmose.loadIssueDetail(_qaItem) - .then(d => { - // No details to add if there are no associated issue elements - if (!d.elems || d.elems.length === 0) return; - - // Do nothing if UI has moved on by the time this resolves - if ( - context.selectedErrorID() !== thisItem.id - && context.container().selectAll(`.qaItem.osmose.hover.itemId-${thisItem.id}`).empty() - ) return; - - // Things like keys and values are dynamically added to a subtitle string - if (d.detail) { - detailsDiv - .append('h4') - .text(() => _t('QA.osmose.detail_title')); - - detailsDiv - .append('p') - .html(d => d.detail) - .selectAll('a') - .attr('rel', 'noopener') - .attr('target', '_blank'); - } - - // Create list of linked issue elements - elemsDiv - .append('h4') - .text(() => _t('QA.osmose.elems_title')); - - elemsDiv - .append('ul').selectAll('li') - .data(d.elems) - .enter() - .append('li') - .append('a') - .attr('class', 'error_entity_link') - .text(d => d) - .each(function() { - const link = select(this); - const entityID = this.textContent; - const entity = context.hasEntity(entityID); - - // Add click handler - link - .on('mouseenter', () => { - utilHighlightEntities([entityID], true, context); - }) - .on('mouseleave', () => { - utilHighlightEntities([entityID], false, context); - }) - .on('click', () => { - event.preventDefault(); - - utilHighlightEntities([entityID], false, context); - - const osmlayer = context.layers().layer('osm'); - if (!osmlayer.enabled()) { - osmlayer.enabled(true); - } - - context.map().centerZoom(d.loc, 20); - - if (entity) { - context.enter(modeSelect(context, [entityID])); - } else { - context.loadEntity(entityID, () => { - context.enter(modeSelect(context, [entityID])); - }); - } - }); - - // Replace with friendly name if possible - // (The entity may not yet be loaded into the graph) - if (entity) { - let name = utilDisplayName(entity); // try to use common name - - if (!name) { - const preset = _mainPresetIndex.match(entity, context.graph()); - name = preset && !preset.isFallback() && preset.name(); // fallback to preset name - } - - if (name) { - this.innerText = name; - } - } - }); - - // Don't hide entities related to this issue - #5880 - context.features().forceVisible(d.elems); - context.map().pan([0,0]); // trigger a redraw - }) - .catch(err => { - console.log(err); // eslint-disable-line no-console - }); - } - - - osmoseDetails.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return osmoseDetails; - }; - - - return osmoseDetails; - } - - function uiOsmoseHeader() { - let _qaItem; - - function issueTitle(d) { - const unknown = _t('inspector.unknown'); - - if (!d) return unknown; - - // Issue titles supplied by Osmose - const s = services.osmose.getStrings(d.itemType); - return ('title' in s) ? s.title : unknown; - } - - function osmoseHeader(selection) { - const header = selection.selectAll('.qa-header') - .data( - (_qaItem ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - header.exit() - .remove(); - - const headerEnter = header.enter() - .append('div') - .attr('class', 'qa-header'); - - const svgEnter = headerEnter - .append('div') - .attr('class', 'qa-header-icon') - .classed('new', d => d.id < 0) - .append('svg') - .attr('width', '20px') - .attr('height', '30px') - .attr('viewbox', '0 0 20 30') - .attr('class', d => `preset-icon-28 qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - svgEnter - .append('polygon') - .attr('fill', d => services.osmose.getColor(d.item)) - .attr('class', 'qaItem-fill') - .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); - - svgEnter - .append('use') - .attr('class', 'icon-annotation') - .attr('width', '13px') - .attr('height', '13px') - .attr('transform', 'translate(3.5, 5)') - .attr('xlink:href', d => { - const picon = d.icon; - - if (!picon) { - return ''; - } else { - const isMaki = /^maki-/.test(picon); - return `#${picon}${isMaki ? '-11' : ''}`; - } - }); - - headerEnter - .append('div') - .attr('class', 'qa-header-label') - .text(issueTitle); - } - - osmoseHeader.issue = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return osmoseHeader; - }; - - return osmoseHeader; - } - - function uiViewOnOsmose() { - let _qaItem; - - function viewOnOsmose(selection) { - let url; - if (services.osmose && (_qaItem instanceof QAItem)) { - url = services.osmose.itemURL(_qaItem); - } - - const link = selection.selectAll('.view-on-osmose') - .data(url ? [url] : []); - - // exit - link.exit() - .remove(); - - // enter - const linkEnter = link.enter() - .append('a') - .attr('class', 'view-on-osmose') - .attr('target', '_blank') - .attr('rel', 'noopener') // security measure - .attr('href', d => d) - .call(svgIcon('#iD-icon-out-link', 'inline')); - - linkEnter - .append('span') - .text(_t('inspector.view_on_osmose')); - } - - viewOnOsmose.what = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return viewOnOsmose; - }; - - return viewOnOsmose; - } - - function uiOsmoseEditor(context) { - const dispatch$1 = dispatch('change'); - const qaDetails = uiOsmoseDetails(context); - const qaHeader = uiOsmoseHeader(); - - let _qaItem; - - function osmoseEditor(selection) { - - const header = selection.selectAll('.header') - .data([0]); - - const headerEnter = header.enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', () => context.enter(modeBrowse(context))) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('QA.osmose.title')); - - let body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - let editor = body.selectAll('.qa-editor') - .data([0]); - - editor.enter() - .append('div') - .attr('class', 'modal-section qa-editor') - .merge(editor) - .call(qaHeader.issue(_qaItem)) - .call(qaDetails.issue(_qaItem)) - .call(osmoseSaveSection); - - const footer = selection.selectAll('.footer') - .data([0]); - - footer.enter() - .append('div') - .attr('class', 'footer') - .merge(footer) - .call(uiViewOnOsmose().what(_qaItem)); - } - - function osmoseSaveSection(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - const isShown = (_qaItem && isSelected); - let saveSection = selection.selectAll('.qa-save') - .data( - (isShown ? [_qaItem] : []), - d => `${d.id}-${d.status || 0}` - ); - - // exit - saveSection.exit() - .remove(); - - // enter - const saveSectionEnter = saveSection.enter() - .append('div') - .attr('class', 'qa-save save-section cf'); - - // update - saveSection = saveSectionEnter - .merge(saveSection) - .call(qaSaveButtons); - } - - function qaSaveButtons(selection) { - const isSelected = (_qaItem && _qaItem.id === context.selectedErrorID()); - let buttonSection = selection.selectAll('.buttons') - .data((isSelected ? [_qaItem] : []), d => d.status + d.id); - - // exit - buttonSection.exit() - .remove(); - - // enter - const buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons'); - - buttonEnter - .append('button') - .attr('class', 'button close-button action'); - - buttonEnter - .append('button') - .attr('class', 'button ignore-button action'); - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.select('.close-button') - .text(() => _t('QA.keepRight.close')) - .on('click.close', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.osmose; - if (qaService) { - d.newStatus = 'done'; - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - - buttonSection.select('.ignore-button') - .text(() => _t('QA.keepRight.ignore')) - .on('click.ignore', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - const qaService = services.osmose; - if (qaService) { - d.newStatus = 'false'; - qaService.postUpdate(d, (err, item) => dispatch$1.call('change', item)); - } - }); - } - - // NOTE: Don't change method name until UI v3 is merged - osmoseEditor.error = function(val) { - if (!arguments.length) return _qaItem; - _qaItem = val; - return osmoseEditor; - }; - - return utilRebind(osmoseEditor, dispatch$1, 'on'); - } - - // NOTE: Don't change name of this until UI v3 is merged - function modeSelectError(context, selectedErrorID, selectedErrorService) { - var mode = { - id: 'select-error', - button: 'browse' - }; - - var keybinding = utilKeybinding('select-error'); - - var errorService = services[selectedErrorService]; - var errorEditor; - switch (selectedErrorService) { - case 'improveOSM': - errorEditor = uiImproveOsmEditor(context) - .on('change', function() { - context.map().pan([0,0]); // trigger a redraw - var error = checkSelectedID(); - if (!error) return; - context.ui().sidebar - .show(errorEditor.error(error)); - }); - break; - case 'keepRight': - errorEditor = uiKeepRightEditor(context) - .on('change', function() { - context.map().pan([0,0]); // trigger a redraw - var error = checkSelectedID(); - if (!error) return; - context.ui().sidebar - .show(errorEditor.error(error)); - }); - break; - case 'osmose': - errorEditor = uiOsmoseEditor(context) - .on('change', function() { - context.map().pan([0,0]); // trigger a redraw - var error = checkSelectedID(); - if (!error) return; - context.ui().sidebar - .show(errorEditor.error(error)); - }); - break; - } - - - var behaviors = [ - behaviorBreathe(), - behaviorHover(context), - behaviorSelect(context), - behaviorLasso(context), - modeDragNode(context).behavior, - modeDragNote(context).behavior - ]; - - - function checkSelectedID() { - if (!errorService) return; - var error = errorService.getError(selectedErrorID); - if (!error) { - context.enter(modeBrowse(context)); - } - return error; - } - - - mode.zoomToSelected = function() { - if (!errorService) return; - var error = errorService.getError(selectedErrorID); - if (error) { - context.map().centerZoomEase(error.loc, 20); - } - }; - - - mode.enter = function() { - var error = checkSelectedID(); - if (!error) return; - - behaviors.forEach(context.install); - keybinding - .on(_t('inspector.zoom_to.key'), mode.zoomToSelected) - .on('⎋', esc, true); - - select(document) - .call(keybinding); - - selectError(); - - var sidebar = context.ui().sidebar; - sidebar.show(errorEditor.error(error)); - - context.map() - .on('drawn.select-error', selectError); - - - // class the error as selected, or return to browse mode if the error is gone - function selectError(drawn) { - if (!checkSelectedID()) return; - - var selection = context.surface() - .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService); - - if (selection.empty()) { - // Return to browse mode if selected DOM elements have - // disappeared because the user moved them out of view.. - var source = event && event.type === 'zoom' && event.sourceEvent; - if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) { - context.enter(modeBrowse(context)); - } - - } else { - selection - .classed('selected', true); - - context.selectedErrorID(selectedErrorID); - } - } - - function esc() { - if (context.container().select('.combobox').size()) return; - context.enter(modeBrowse(context)); - } - }; - - - mode.exit = function() { - behaviors.forEach(context.uninstall); - - select(document) - .call(keybinding.unbind); - - context.surface() - .selectAll('.qaItem.selected') - .classed('selected hover', false); - - context.map() - .on('drawn.select-error', null); - - context.ui().sidebar - .hide(); - - context.selectedErrorID(null); - context.features().forceVisible([]); - }; - - - return mode; - } - - function behaviorSelect(context) { - var _tolerancePx = 4; // see also behaviorDrag - var _lastMouseEvent = null; - var _showMenu = false; - var _downPointers = {}; - var _longPressTimeout = null; - var _lastInteractionType = null; - // the id of the down pointer that's enabling multiselection while down - var _multiselectionPointerId = null; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - - function keydown() { - - if (event.keyCode === 32) { - // don't react to spacebar events during text input - var activeNode = document.activeElement; - if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return; - } - - if (event.keyCode === 93 || // context menu key - event.keyCode === 32) { // spacebar - event.preventDefault(); - } - - if (event.repeat) return; // ignore repeated events for held keys - - // if any key is pressed the user is probably doing something other than long-pressing - cancelLongPress(); - - if (event.shiftKey) { - context.surface() - .classed('behavior-multiselect', true); - } - - if (event.keyCode === 32) { // spacebar - if (!_downPointers.spacebar && _lastMouseEvent) { - cancelLongPress(); - _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar'); - - _downPointers.spacebar = { - firstEvent: _lastMouseEvent, - lastEvent: _lastMouseEvent - }; - } - } - } - - - function keyup() { - cancelLongPress(); - - if (!event.shiftKey) { - context.surface() - .classed('behavior-multiselect', false); - } - - if (event.keyCode === 93) { // context menu key - event.preventDefault(); - _lastInteractionType = 'menukey'; - contextmenu(); - } else if (event.keyCode === 32) { // spacebar - var pointer = _downPointers.spacebar; - if (pointer) { - delete _downPointers.spacebar; - - if (pointer.done) return; - - event.preventDefault(); - _lastInteractionType = 'spacebar'; - click(pointer.firstEvent, pointer.lastEvent, 'spacebar'); - } - } - } - - - function pointerdown() { - var id = (event.pointerId || 'mouse').toString(); - - cancelLongPress(); - - if (event.buttons && event.buttons !== 1) return; - - context.ui().closeEditMenu(); - - _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse')); - - _downPointers[id] = { - firstEvent: event, - lastEvent: event - }; - } - - - function didLongPress(id, interactionType) { - var pointer = _downPointers[id]; - if (!pointer) return; - - for (var i in _downPointers) { - // don't allow this or any currently down pointer to trigger another click - _downPointers[i].done = true; - } - - // treat long presses like right-clicks - _longPressTimeout = null; - _lastInteractionType = interactionType; - _showMenu = true; - - click(pointer.firstEvent, pointer.lastEvent, id); - } - - - function pointermove() { - var id = (event.pointerId || 'mouse').toString(); - if (_downPointers[id]) { - _downPointers[id].lastEvent = event; - } - if (!event.pointerType || event.pointerType === 'mouse') { - _lastMouseEvent = event; - if (_downPointers.spacebar) { - _downPointers.spacebar.lastEvent = event; - } - } - } - - - function pointerup() { - var id = (event.pointerId || 'mouse').toString(); - var pointer = _downPointers[id]; - if (!pointer) return; - - delete _downPointers[id]; - - if (_multiselectionPointerId === id) { - _multiselectionPointerId = null; - } - - if (pointer.done) return; - - click(pointer.firstEvent, event, id); - } - - - function pointercancel() { - var id = (event.pointerId || 'mouse').toString(); - if (!_downPointers[id]) return; - - delete _downPointers[id]; - - if (_multiselectionPointerId === id) { - _multiselectionPointerId = null; - } - } - - - function contextmenu() { - var e = event; - e.preventDefault(); - - if (!+e.clientX && !+e.clientY) { - if (_lastMouseEvent) { - e.sourceEvent = _lastMouseEvent; - } else { - return; - } - } else { - _lastMouseEvent = event; - _lastInteractionType = 'rightclick'; - } - - _showMenu = true; - click(event, event); - } - - - function click(firstEvent, lastEvent, pointerId) { - cancelLongPress(); - - var mapNode = context.container().select('.main-map').node(); - - // Use the `main-map` coordinate system since the surface and supersurface - // are transformed when drag-panning. - var pointGetter = utilFastMouse(mapNode); - var p1 = pointGetter(firstEvent); - var p2 = pointGetter(lastEvent); - var dist = geoVecLength(p1, p2); - - if (dist > _tolerancePx || - !mapContains(lastEvent)) { - - resetProperties(); - return; - } - - var targetDatum = lastEvent.target.__data__; - - var multiselectEntityId; - - if (!_multiselectionPointerId) { - // If a different pointer than the one triggering this click is down on a - // feature, treat this and all future clicks as multiselection until that - // pointer is raised. - var selectPointerInfo = pointerDownOnSelection(pointerId); - if (selectPointerInfo) { - _multiselectionPointerId = selectPointerInfo.pointerId; - // if the other feature isn't selected yet, make sure we select it - multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId; - _downPointers[selectPointerInfo.pointerId].done = true; - } - } - - // support multiselect if data is already selected - var isMultiselect = context.mode().id === 'select' && ( - // and shift key is down - (event && event.shiftKey) || - // or we're lasso-selecting - context.surface().select('.lasso').node() || - // or a pointer is down over a selected feature - (_multiselectionPointerId && !multiselectEntityId) - ); - - processClick(targetDatum, isMultiselect, p2, multiselectEntityId); - - function mapContains(event) { - var rect = mapNode.getBoundingClientRect(); - return event.clientX >= rect.left && - event.clientX <= rect.right && - event.clientY >= rect.top && - event.clientY <= rect.bottom; - } - - function pointerDownOnSelection(skipPointerId) { - var mode = context.mode(); - var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : []; - for (var pointerId in _downPointers) { - if (pointerId === 'spacebar' || pointerId === skipPointerId) continue; - - var pointerInfo = _downPointers[pointerId]; - - var p1 = pointGetter(pointerInfo.firstEvent); - var p2 = pointGetter(pointerInfo.lastEvent); - if (geoVecLength(p1, p2) > _tolerancePx) continue; - - var datum = pointerInfo.firstEvent.target.__data__; - var entity = (datum && datum.properties && datum.properties.entity) || datum; - if (context.graph().hasEntity(entity.id)) return { - pointerId: pointerId, - entityId: entity.id, - selected: selectedIDs.indexOf(entity.id) !== -1 - }; - } - return null; - } - } - - - function processClick(datum, isMultiselect, point, alsoSelectId) { - var mode = context.mode(); - var showMenu = _showMenu; - var interactionType = _lastInteractionType; - - var entity = datum && datum.properties && datum.properties.entity; - if (entity) datum = entity; - - if (datum && datum.type === 'midpoint') { - // treat targeting midpoints as if targeting the parent way - datum = datum.parents[0]; - } - - var newMode; - - if (datum instanceof osmEntity) { - // targeting an entity - var selectedIDs = context.selectedIDs(); - context.selectedNoteID(null); - context.selectedErrorID(null); - - if (!isMultiselect) { - // don't change the selection if we're toggling the menu atop a multiselection - if (!showMenu || - selectedIDs.length <= 1 || - selectedIDs.indexOf(datum.id) === -1) { - - if (alsoSelectId === datum.id) alsoSelectId = null; - - selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); - // always enter modeSelect even if the entity is already - // selected since listeners may expect `context.enter` events, - // e.g. in the walkthrough - newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior); - context.enter(newMode); - } - - } else { - if (selectedIDs.indexOf(datum.id) !== -1) { - // clicked entity is already in the selectedIDs list.. - if (!showMenu) { - // deselect clicked entity, then reenter select mode or return to browse mode.. - selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; }); - newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior); - context.enter(newMode); - } - } else { - // clicked entity is not in the selected list, add it.. - selectedIDs = selectedIDs.concat([datum.id]); - newMode = mode.selectedIDs(selectedIDs); - context.enter(newMode); - } - } - - } else if (datum && datum.__featurehash__ && !isMultiselect) { - // targeting custom data - context - .selectedNoteID(null) - .enter(modeSelectData(context, datum)); - - } else if (datum instanceof osmNote && !isMultiselect) { - // targeting a note - context - .selectedNoteID(datum.id) - .enter(modeSelectNote(context, datum.id)); - - } else if (datum instanceof QAItem & !isMultiselect) { - // targeting an external QA issue - context - .selectedErrorID(datum.id) - .enter(modeSelectError(context, datum.id, datum.service)); - - } else { - // targeting nothing - context.selectedNoteID(null); - context.selectedErrorID(null); - if (!isMultiselect && mode.id !== 'browse') { - context.enter(modeBrowse(context)); - } - } - - context.ui().closeEditMenu(); - - // always request to show the edit menu in case the mode needs it - if (showMenu) context.ui().showEditMenu(point, interactionType); - - resetProperties(); - } - - - function cancelLongPress() { - if (_longPressTimeout) window.clearTimeout(_longPressTimeout); - _longPressTimeout = null; - } - - - function resetProperties() { - cancelLongPress(); - _showMenu = false; - _lastInteractionType = null; - // don't reset _lastMouseEvent since it might still be useful - } - - - function behavior(selection) { - resetProperties(); - _lastMouseEvent = context.map().lastPointerEvent(); - - select(window) - .on('keydown.select', keydown) - .on('keyup.select', keyup) - .on(_pointerPrefix + 'move.select', pointermove, true) - .on(_pointerPrefix + 'up.select', pointerup, true) - .on('pointercancel.select', pointercancel, true) - .on('contextmenu.select-window', function() { - // Edge and IE really like to show the contextmenu on the - // menubar when user presses a keyboard menu button - // even after we've already preventdefaulted the key event. - var e = event; - if (+e.clientX === 0 && +e.clientY === 0) { - event.preventDefault(); - } - }); - - selection - .on(_pointerPrefix + 'down.select', pointerdown) - .on('contextmenu.select', contextmenu); - - if (event && event.shiftKey) { - context.surface() - .classed('behavior-multiselect', true); - } - } - - - behavior.off = function(selection) { - cancelLongPress(); - - select(window) - .on('keydown.select', null) - .on('keyup.select', null) - .on('contextmenu.select-window', null) - .on(_pointerPrefix + 'move.select', null, true) - .on(_pointerPrefix + 'up.select', null, true) - .on('pointercancel.select', null, true); - - selection - .on(_pointerPrefix + 'down.select', null) - .on('contextmenu.select', null); - - context.surface() - .classed('behavior-multiselect', false); - }; - - - return behavior; - } - - function behaviorDrawWay(context, wayID, mode, startGraph) { - - var dispatch$1 = dispatch('rejectedSelfIntersection'); - - var behavior = behaviorDraw(context); - - // Must be set by `drawWay.nodeIndex` before each install of this behavior. - var _nodeIndex; - - var _origWay; - var _wayGeometry; - var _headNodeID; - var _annotation; - - var _pointerHasMoved = false; - - // The osmNode to be placed. - // This is temporary and just follows the mouse cursor until an "add" event occurs. - var _drawNode; - - var _didResolveTempEdit = false; - - function createDrawNode(loc) { - // don't make the draw node until we actually need it - _drawNode = osmNode({ loc: loc }); - - context.pauseChangeDispatch(); - context.replace(function actionAddDrawNode(graph) { - // add the draw node to the graph and insert it into the way - var way = graph.entity(wayID); - return graph - .replace(_drawNode) - .replace(way.addNode(_drawNode.id, _nodeIndex)); - }, _annotation); - context.resumeChangeDispatch(); - - setActiveElements(); - } - - function removeDrawNode() { - - context.pauseChangeDispatch(); - context.replace( - function actionDeleteDrawNode(graph) { - var way = graph.entity(wayID); - return graph - .replace(way.removeNode(_drawNode.id)) - .remove(_drawNode); - }, - _annotation - ); - _drawNode = undefined; - context.resumeChangeDispatch(); - } - - - function keydown() { - if (event.keyCode === utilKeybinding.modifierCodes.alt) { - if (context.surface().classed('nope')) { - context.surface() - .classed('nope-suppressed', true); - } - context.surface() - .classed('nope', false) - .classed('nope-disabled', true); - } - } - - - function keyup() { - if (event.keyCode === utilKeybinding.modifierCodes.alt) { - if (context.surface().classed('nope-suppressed')) { - context.surface() - .classed('nope', true); - } - context.surface() - .classed('nope-suppressed', false) - .classed('nope-disabled', false); - } - } - - - function allowsVertex(d) { - return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph()); - } - - - // related code - // - `mode/drag_node.js` `doMove()` - // - `behavior/draw.js` `click()` - // - `behavior/draw_way.js` `move()` - function move(datum) { - - var loc = context.map().mouseCoordinates(); - - if (!_drawNode) createDrawNode(loc); - - context.surface().classed('nope-disabled', event.altKey); - - var targetLoc = datum && datum.properties && datum.properties.entity && - allowsVertex(datum.properties.entity) && datum.properties.entity.loc; - var targetNodes = datum && datum.properties && datum.properties.nodes; - - if (targetLoc) { // snap to node/vertex - a point target with `.loc` - loc = targetLoc; - - } else if (targetNodes) { // snap to way - a line target with `.nodes` - var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id); - if (choice) { - loc = choice.loc; - } - } - - context.replace(actionMoveNode(_drawNode.id, loc), _annotation); - _drawNode = context.entity(_drawNode.id); - checkGeometry(true /* includeDrawNode */); - } - - - // Check whether this edit causes the geometry to break. - // If so, class the surface with a nope cursor. - // `includeDrawNode` - Only check the relevant line segments if finishing drawing - function checkGeometry(includeDrawNode) { - var nopeDisabled = context.surface().classed('nope-disabled'); - var isInvalid = isInvalidGeometry(includeDrawNode); - - if (nopeDisabled) { - context.surface() - .classed('nope', false) - .classed('nope-suppressed', isInvalid); - } else { - context.surface() - .classed('nope', isInvalid) - .classed('nope-suppressed', false); - } - } - - - function isInvalidGeometry(includeDrawNode) { - - var testNode = _drawNode; - - // we only need to test the single way we're drawing - var parentWay = context.graph().entity(wayID); - var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy - - if (includeDrawNode) { - if (parentWay.isClosed()) { - // don't test the last segment for closed ways - #4655 - // (still test the first segement) - nodes.pop(); - } - } else { // discount the draw node - - if (parentWay.isClosed()) { - if (nodes.length < 3) return false; - if (_drawNode) nodes.splice(-2, 1); - testNode = nodes[nodes.length - 2]; - } else { - // there's nothing we need to test if we ignore the draw node on open ways - return false; - } - } - - return testNode && geoHasSelfIntersections(nodes, testNode.id); - } - - - function undone() { - - // undoing removed the temp edit - _didResolveTempEdit = true; - - context.pauseChangeDispatch(); - - var nextMode; - - if (context.graph() === startGraph) { // we've undone back to the beginning - nextMode = modeSelect(context, [wayID]); - } else { - context.history() - .on('undone.draw', null); - // remove whatever segment was drawn previously - context.undo(); - - if (context.graph() === startGraph) { // we've undone back to the beginning - nextMode = modeSelect(context, [wayID]); - } else { - // continue drawing - nextMode = mode; - } - } - - // clear the redo stack by adding and removing an edit - context.perform(actionNoop()); - context.pop(1); - - context.resumeChangeDispatch(); - context.enter(nextMode); - } - - - function setActiveElements() { - if (!_drawNode) return; - - context.surface().selectAll('.' + _drawNode.id) - .classed('active', true); - } - - - function resetToStartGraph() { - while (context.graph() !== startGraph) { - context.pop(); - } - } - - - var drawWay = function(surface) { - _drawNode = undefined; - _didResolveTempEdit = false; - _origWay = context.entity(wayID); - _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] : - (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]); - _wayGeometry = _origWay.geometry(context.graph()); - _annotation = _t((_origWay.isDegenerate() ? - 'operations.start.annotation.' : - 'operations.continue.annotation.') + _wayGeometry - ); - _pointerHasMoved = false; - - // Push an annotated state for undo to return back to. - // We must make sure to replace or remove it later. - context.pauseChangeDispatch(); - context.perform(actionNoop(), _annotation); - context.resumeChangeDispatch(); - - behavior.hover() - .initialNodeID(_headNodeID); - - behavior - .on('move', function() { - _pointerHasMoved = true; - move.apply(this, arguments); - }) - .on('down', function() { - move.apply(this, arguments); - }) - .on('downcancel', function() { - if (_drawNode) removeDrawNode(); - }) - .on('click', drawWay.add) - .on('clickWay', drawWay.addWay) - .on('clickNode', drawWay.addNode) - .on('undo', context.undo) - .on('cancel', drawWay.cancel) - .on('finish', drawWay.finish); - - select(window) - .on('keydown.drawWay', keydown) - .on('keyup.drawWay', keyup); - - context.map() - .dblclickZoomEnable(false) - .on('drawn.draw', setActiveElements); - - setActiveElements(); - - surface.call(behavior); - - context.history() - .on('undone.draw', undone); - }; - - - drawWay.off = function(surface) { - - if (!_didResolveTempEdit) { - // Drawing was interrupted unexpectedly. - // This can happen if the user changes modes, - // clicks geolocate button, a hashchange event occurs, etc. - - context.pauseChangeDispatch(); - resetToStartGraph(); - context.resumeChangeDispatch(); - } - - _drawNode = undefined; - _nodeIndex = undefined; - - context.map() - .on('drawn.draw', null); - - surface.call(behavior.off) - .selectAll('.active') - .classed('active', false); - - surface - .classed('nope', false) - .classed('nope-suppressed', false) - .classed('nope-disabled', false); - - select(window) - .on('keydown.drawWay', null) - .on('keyup.drawWay', null); - - context.history() - .on('undone.draw', null); - }; - - - function attemptAdd(d, loc, doAdd) { - - if (_drawNode) { - // move the node to the final loc in case move wasn't called - // consistently (e.g. on touch devices) - context.replace(actionMoveNode(_drawNode.id, loc), _annotation); - _drawNode = context.entity(_drawNode.id); - } else { - createDrawNode(loc); - } - - checkGeometry(true /* includeDrawNode */); - if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) { - if (!_pointerHasMoved) { - // prevent the temporary draw node from appearing on touch devices - removeDrawNode(); - } - dispatch$1.call('rejectedSelfIntersection', this); - return; // can't click here - } - - context.pauseChangeDispatch(); - doAdd(); - // we just replaced the temporary edit with the real one - _didResolveTempEdit = true; - context.resumeChangeDispatch(); - - context.enter(mode); - } - - - // Accept the current position of the drawing node - drawWay.add = function(loc, d) { - attemptAdd(d, loc, function() { - // don't need to do anything extra - }); - }; - - - // Connect the way to an existing way - drawWay.addWay = function(loc, edge, d) { - attemptAdd(d, loc, function() { - context.replace( - actionAddMidpoint({ loc: loc, edge: edge }, _drawNode), - _annotation - ); - }); - }; - - - // Connect the way to an existing node - drawWay.addNode = function(node, d) { - - // finish drawing if the mapper targets the prior node - if (node.id === _headNodeID || - // or the first node when drawing an area - (_origWay.isClosed() && node.id === _origWay.first())) { - drawWay.finish(); - return; - } - - attemptAdd(d, node.loc, function() { - context.replace( - function actionReplaceDrawNode(graph) { - // remove the temporary draw node and insert the existing node - // at the same index - - graph = graph - .replace(graph.entity(wayID).removeNode(_drawNode.id)) - .remove(_drawNode); - return graph - .replace(graph.entity(wayID).addNode(node.id, _nodeIndex)); - }, - _annotation - ); - }); - }; - - - // Finish the draw operation, removing the temporary edit. - // If the way has enough nodes to be valid, it's selected. - // Otherwise, delete everything and return to browse mode. - drawWay.finish = function() { - checkGeometry(false /* includeDrawNode */); - if (context.surface().classed('nope')) { - dispatch$1.call('rejectedSelfIntersection', this); - return; // can't click here - } - - context.pauseChangeDispatch(); - // remove the temporary edit - context.pop(1); - _didResolveTempEdit = true; - context.resumeChangeDispatch(); - - var way = context.hasEntity(wayID); - if (!way || way.isDegenerate()) { - drawWay.cancel(); - return; - } - - window.setTimeout(function() { - context.map().dblclickZoomEnable(true); - }, 1000); - - var isNewFeature = !mode.isContinuing; - context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature)); - }; - - - // Cancel the draw operation, delete everything, and return to browse mode. - drawWay.cancel = function() { - context.pauseChangeDispatch(); - resetToStartGraph(); - context.resumeChangeDispatch(); - - window.setTimeout(function() { - context.map().dblclickZoomEnable(true); - }, 1000); - - context.surface() - .classed('nope', false) - .classed('nope-disabled', false) - .classed('nope-suppressed', false); - - context.enter(modeBrowse(context)); - }; - - - drawWay.nodeIndex = function(val) { - if (!arguments.length) return _nodeIndex; - _nodeIndex = val; - return drawWay; - }; - - - drawWay.activeID = function() { - if (!arguments.length) return _drawNode && _drawNode.id; - // no assign - return drawWay; - }; - - - return utilRebind(drawWay, dispatch$1, 'on'); - } - - function modeDrawLine(context, wayID, startGraph, button, affix, continuing) { - var mode = { - button: button, - id: 'draw-line' - }; - - var behavior = behaviorDrawWay(context, wayID, mode, startGraph) - .on('rejectedSelfIntersection.modeDrawLine', function() { - context.ui().flash - .text(_t('self_intersection.error.lines'))(); - }); - - mode.wayID = wayID; - - mode.isContinuing = continuing; - - mode.enter = function() { - behavior - .nodeIndex(affix === 'prefix' ? 0 : undefined); - - context.install(behavior); - }; - - mode.exit = function() { - context.uninstall(behavior); - }; - - mode.selectedIDs = function() { - return [wayID]; - }; - - mode.activeID = function() { - return (behavior && behavior.activeID()) || []; - }; - - return mode; - } - - function operationContinue(context, selectedIDs) { - var graph = context.graph(); - var entities = selectedIDs.map(function(id) { return graph.entity(id); }); - var geometries = Object.assign( - { line: [], vertex: [] }, - utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); }) - ); - var vertex = geometries.vertex[0]; - - - function candidateWays() { - return graph.parentWays(vertex).filter(function(parent) { - return parent.geometry(graph) === 'line' && - !parent.isClosed() && - parent.affix(vertex.id) && - (geometries.line.length === 0 || geometries.line[0] === parent); - }); - } - - - var operation = function() { - var candidate = candidateWays()[0]; - context.enter( - modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true) - ); - }; - - - operation.available = function() { - return geometries.vertex.length === 1 && - geometries.line.length <= 1 && - !context.features().hasHiddenConnections(vertex, context.graph()); - }; - - - operation.disabled = function() { - var candidates = candidateWays(); - if (candidates.length === 0) { - return 'not_eligible'; - } else if (candidates.length > 1) { - return 'multiple'; - } - - return false; - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.continue.' + disable) : - _t('operations.continue.description'); - }; - - - operation.annotation = function() { - return _t('operations.continue.annotation.line'); - }; - - - operation.id = 'continue'; - operation.keys = [_t('operations.continue.key')]; - operation.title = _t('operations.continue.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationCopy(context, selectedIDs) { - - var _multi = selectedIDs.length === 1 ? 'single' : 'multiple'; - - function getFilteredIdsToCopy() { - return selectedIDs.filter(function(selectedID) { - var entity = context.graph().hasEntity(selectedID); - // don't copy untagged vertices separately from ways - return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex'; - }); - } - - var operation = function() { - - if (!getSelectionText()) { - event.preventDefault(); - } - - var graph = context.graph(); - var selected = groupEntities(getFilteredIdsToCopy(), graph); - var canCopy = []; - var skip = {}; - var entity; - var i; - - for (i = 0; i < selected.relation.length; i++) { - entity = selected.relation[i]; - if (!skip[entity.id] && entity.isComplete(graph)) { - canCopy.push(entity.id); - skip = getDescendants(entity.id, graph, skip); - } - } - for (i = 0; i < selected.way.length; i++) { - entity = selected.way[i]; - if (!skip[entity.id]) { - canCopy.push(entity.id); - skip = getDescendants(entity.id, graph, skip); - } - } - for (i = 0; i < selected.node.length; i++) { - entity = selected.node[i]; - if (!skip[entity.id]) { - canCopy.push(entity.id); - } - } - - context.copyIDs(canCopy); - if (_point && - (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) { - // store the anchor coordinates if copying more than a single node - context.copyLonLat(context.projection.invert(_point)); - } else { - context.copyLonLat(null); - } - - }; - - - function groupEntities(ids, graph) { - var entities = ids.map(function (id) { return graph.entity(id); }); - return Object.assign( - { relation: [], way: [], node: [] }, - utilArrayGroupBy(entities, 'type') - ); - } - - - function getDescendants(id, graph, descendants) { - var entity = graph.entity(id); - var children; - - descendants = descendants || {}; - - if (entity.type === 'relation') { - children = entity.members.map(function(m) { return m.id; }); - } else if (entity.type === 'way') { - children = entity.nodes; - } else { - children = []; - } - - for (var i = 0; i < children.length; i++) { - if (!descendants[children[i]]) { - descendants[children[i]] = true; - descendants = getDescendants(children[i], graph, descendants); - } - } - - return descendants; - } - - - function getSelectionText() { - return window.getSelection().toString(); - } - - - operation.available = function() { - return getFilteredIdsToCopy().length > 0; - }; - - - operation.disabled = function() { - var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph()); - if (extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } - return false; - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.copy.' + disable + '.' + _multi) : - _t('operations.copy.description' + '.' + _multi); - }; - - - operation.annotation = function() { - return selectedIDs.length === 1 ? - _t('operations.copy.annotation.single') : - _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() }); - }; - - - var _point; - operation.point = function(val) { - _point = val; - return operation; - }; - - - operation.id = 'copy'; - operation.keys = [uiCmd('⌘C')]; - operation.title = _t('operations.copy.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationDisconnect(context, selectedIDs) { - var _vertexIDs = []; - var _wayIDs = []; - var _otherIDs = []; - var _actions = []; - - selectedIDs.forEach(function(id) { - var entity = context.entity(id); - if (entity.type === 'way'){ - _wayIDs.push(id); - } else if (entity.geometry(context.graph()) === 'vertex') { - _vertexIDs.push(id); - } else { - _otherIDs.push(id); - } - }); - - var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features'; - - if (_vertexIDs.length > 0) { - // At the selected vertices, disconnect the selected ways, if any, else - // disconnect all connected ways - - _extent = utilTotalExtent(_vertexIDs, context.graph()); - - _vertexIDs.forEach(function(vertexID) { - var action = actionDisconnect(vertexID); - - if (_wayIDs.length > 0) { - var waysIDsForVertex = _wayIDs.filter(function(wayID) { - var way = context.entity(wayID); - return way.nodes.indexOf(vertexID) !== -1; - }); - action.limitWays(waysIDsForVertex); - } - _actions.push(action); - }); - - _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.'; - if (_wayIDs.length === 1) { - _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]); - } else { - _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways'; - } - - } else if (_wayIDs.length > 0) { - // Disconnect the selected ways from each other, if they're connected, - // else disconnect them from all connected ways - - var ways = _wayIDs.map(function(id) { - return context.entity(id); - }); - _nodes = utilGetAllNodes(_wayIDs, context.graph()); - _coords = _nodes.map(function(n) { return n.loc; }); - _extent = utilTotalExtent(ways, context.graph()); - - // actions for connected nodes shared by at least two selected ways - var sharedActions = []; - // actions for connected nodes - var unsharedActions = []; - - _nodes.forEach(function(node) { - var action = actionDisconnect(node.id).limitWays(_wayIDs); - if (action.disabled(context.graph()) !== 'not_connected') { - - var count = 0; - for (var i in ways) { - var way = ways[i]; - if (way.nodes.indexOf(node.id) !== -1) { - count += 1; - } - if (count > 1) break; - } - - if (count > 1) { - sharedActions.push(action); - } else { - unsharedActions.push(action); - } - } - }); - - _descriptionID += 'no_points.'; - _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.'; - - if (sharedActions.length) { - // if any nodes are shared, only disconnect the selected ways from each other - _actions = sharedActions; - _descriptionID += 'conjoined'; - _annotationID = 'from_each_other'; - } else { - // if no nodes are shared, disconnect the selected ways from all connected ways - _actions = unsharedActions; - if (_wayIDs.length === 1) { - _descriptionID += context.graph().geometry(_wayIDs[0]); - } else { - _descriptionID += 'separate'; - } - } - } - - - var operation = function() { - context.perform(function(graph) { - return _actions.reduce(function(graph, action) { return action(graph); }, graph); - }, operation.annotation()); - - context.validator().validate(); - }; - - - operation.available = function() { - if (_actions.length === 0) return false; - if (_otherIDs.length !== 0) return false; - - if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) { - return _vertexIDs.some(function(vertexID) { - var way = context.entity(wayID); - return way.nodes.indexOf(vertexID) !== -1; - }); - })) return false; - - return true; - }; - - - operation.disabled = function() { - var reason; - for (var actionIndex in _actions) { - reason = _actions[actionIndex].disabled(context.graph()); - if (reason) return reason; - } - - if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple'); - } else if (_coords && someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - if (disable) { - return _t('operations.disconnect.' + disable); - } - return _t('operations.disconnect.description.' + _descriptionID); - }; - - - operation.annotation = function() { - return _t('operations.disconnect.annotation.' + _annotationID); - }; - - - operation.id = 'disconnect'; - operation.keys = [_t('operations.disconnect.key')]; - operation.title = _t('operations.disconnect.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationDowngrade(context, selectedIDs) { - var affectedFeatureCount = 0; - var downgradeType; - - setDowngradeTypeForEntityIDs(); - - var multi = affectedFeatureCount === 1 ? 'single' : 'multiple'; - - function setDowngradeTypeForEntityIDs() { - for (var i in selectedIDs) { - var entityID = selectedIDs[i]; - var type = downgradeTypeForEntityID(entityID); - if (type) { - affectedFeatureCount += 1; - if (downgradeType && type !== downgradeType) { - downgradeType = 'building_address'; - } else { - downgradeType = type; - } - } - } - } - - function downgradeTypeForEntityID(entityID) { - var graph = context.graph(); - var entity = graph.entity(entityID); - var preset = _mainPresetIndex.match(entity, graph); - - if (!preset || preset.isFallback()) return null; - - if (entity.type === 'node' && - preset.id !== 'address' && - Object.keys(entity.tags).some(function(key) { - return key.match(/^addr:.{1,}/); - })) { - - return 'address'; - } - if (entity.geometry(graph) === 'area' && - entity.tags.building && - !preset.tags.building) { - - return 'building'; - } - - return null; - } - - var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair']; - var addressKeysToKeep = ['source']; - - var operation = function () { - context.perform(function(graph) { - - for (var i in selectedIDs) { - var entityID = selectedIDs[i]; - var type = downgradeTypeForEntityID(entityID); - if (!type) continue; - - var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy - for (var key in tags) { - if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue; - if (type === 'building') { - if (buildingKeysToKeep.indexOf(key) !== -1 || - key.match(/^building:.{1,}/) || - key.match(/^roof:.{1,}/)) continue; - } - // keep address tags for buildings too - if (key.match(/^addr:.{1,}/)) continue; - - delete tags[key]; - } - graph = actionChangeTags(entityID, tags)(graph); - } - return graph; - }, operation.annotation()); - - context.validator().validate(); - - // refresh the select mode to enable the delete operation - context.enter(modeSelect(context, selectedIDs)); - }; - - - operation.available = function () { - return downgradeType; - }; - - - operation.disabled = function () { - if (selectedIDs.some(hasWikidataTag)) { - return 'has_wikidata_tag'; - } - return false; - - function hasWikidataTag(id) { - var entity = context.entity(id); - return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0; - } - }; - - - operation.tooltip = function () { - var disable = operation.disabled(); - return disable ? - _t('operations.downgrade.' + disable + '.' + multi) : - _t('operations.downgrade.description.' + downgradeType); - }; - - - operation.annotation = function () { - var suffix; - if (downgradeType === 'building_address') { - suffix = 'multiple'; - } else { - suffix = downgradeType + '.' + multi; - } - return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount}); - }; - - - operation.id = 'downgrade'; - operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')]; - operation.title = _t('operations.downgrade.title'); - operation.behavior = behaviorOperation(context).which(operation); - - - return operation; - } - - function operationExtract(context, selectedIDs) { - - var _amount = selectedIDs.length === 1 ? 'single' : 'multiple'; - var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) { - return context.graph().hasEntity(entityID) && context.graph().geometry(entityID); - }).filter(Boolean)); - var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature'; - - var _extent; - var _actions = selectedIDs.map(function(entityID) { - var graph = context.graph(); - var entity = graph.hasEntity(entityID); - if (!entity || !entity.hasInterestingTags()) return; - - if (entity.type === 'node' && graph.parentWays(entity).length === 0) return; - - if (entity.type !== 'node') { - var preset = _mainPresetIndex.match(entity, graph); - // only allow extraction from ways/relations if the preset supports points - if (preset.geometry.indexOf('point') === -1) return; - } - - _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph); - - return actionExtract(entityID); - }).filter(Boolean); - - - var operation = function () { - var combinedAction = function(graph) { - _actions.forEach(function(action) { - graph = action(graph); - }); - return graph; - }; - context.perform(combinedAction, operation.annotation()); // do the extract - - var extractedNodeIDs = _actions.map(function(action) { - return action.getExtractedNodeID(); - }); - context.enter(modeSelect(context, extractedNodeIDs)); - }; - - - operation.available = function () { - return _actions.length && selectedIDs.length === _actions.length; - }; - - - operation.disabled = function () { - - if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (selectedIDs.some(function(entityID) { - return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID); - })) { - return 'connected_to_hidden'; - } - - return false; - }; - - - operation.tooltip = function () { - var disableReason = operation.disabled(); - if (disableReason) { - return _t('operations.extract.' + disableReason + '.' + _amount); - } else { - return _t('operations.extract.description.' + _geometryID + '.' + _amount); - } - }; - - - operation.annotation = function () { - return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length }); - }; - - - operation.id = 'extract'; - operation.keys = [_t('operations.extract.key')]; - operation.title = _t('operations.extract.title'); - operation.behavior = behaviorOperation(context).which(operation); - - - return operation; - } - - function operationMerge(context, selectedIDs) { - - var _action = getAction(); - - function getAction() { - // prefer a non-disabled action first - var join = actionJoin(selectedIDs); - if (!join.disabled(context.graph())) return join; - - var merge = actionMerge(selectedIDs); - if (!merge.disabled(context.graph())) return merge; - - var mergePolygon = actionMergePolygon(selectedIDs); - if (!mergePolygon.disabled(context.graph())) return mergePolygon; - - var mergeNodes = actionMergeNodes(selectedIDs); - if (!mergeNodes.disabled(context.graph())) return mergeNodes; - - // otherwise prefer an action with an interesting disabled reason - if (join.disabled(context.graph()) !== 'not_eligible') return join; - if (merge.disabled(context.graph()) !== 'not_eligible') return merge; - if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon; - - return mergeNodes; - } - - var operation = function() { - - if (operation.disabled()) return; - - context.perform(_action, operation.annotation()); - - context.validator().validate(); - - var resultIDs = selectedIDs.filter(context.hasEntity); - if (resultIDs.length > 1) { - var interestingIDs = resultIDs.filter(function(id) { - return context.entity(id).hasInterestingTags(); - }); - if (interestingIDs.length) resultIDs = interestingIDs; - } - context.enter(modeSelect(context, resultIDs)); - }; - - operation.available = function() { - return selectedIDs.length >= 2; - }; - - operation.disabled = function() { - var actionDisabled = _action.disabled(context.graph()); - if (actionDisabled) return actionDisabled; - - var osm = context.connection(); - if (osm && - _action.resultingWayNodesLength && - _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) { - return 'too_many_vertices'; - } - - return false; - }; - - operation.tooltip = function() { - var disabled = operation.disabled(); - if (disabled) { - if (disabled === 'restriction') { - return _t('operations.merge.restriction', - { relation: _mainPresetIndex.item('type/restriction').name() }); - } - return _t('operations.merge.' + disabled); - } - return _t('operations.merge.description'); - }; - - operation.annotation = function() { - return _t('operations.merge.annotation', { n: selectedIDs.length }); - }; - - operation.id = 'merge'; - operation.keys = [_t('operations.merge.key')]; - operation.title = _t('operations.merge.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - // see also `behaviorPaste` - function operationPaste(context) { - - var _pastePoint; - - var operation = function() { - - if (!_pastePoint) return; - - var oldIDs = context.copyIDs(); - if (!oldIDs.length) return; - - var projection = context.projection; - var extent = geoExtent(); - var oldGraph = context.copyGraph(); - var newIDs = []; - - var action = actionCopyEntities(oldIDs, oldGraph); - context.perform(action); - - var copies = action.copies(); - var originals = new Set(); - Object.values(copies).forEach(function(entity) { originals.add(entity.id); }); - - for (var id in copies) { - var oldEntity = oldGraph.entity(id); - var newEntity = copies[id]; - - extent._extend(oldEntity.extent(oldGraph)); - - // Exclude child nodes from newIDs if their parent way was also copied. - var parents = context.graph().parentWays(newEntity); - var parentCopied = parents.some(function(parent) { - return originals.has(parent.id); - }); - - if (!parentCopied) { - newIDs.push(newEntity.id); - } - } - - // Use the location of the copy operation to offset the paste location, - // or else use the center of the pasted extent - var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || - projection(extent.center()); - var delta = geoVecSubtract(_pastePoint, copyPoint); - - // Move the pasted objects to be anchored at the paste location - context.replace(actionMove(newIDs, delta, projection), operation.annotation()); - context.enter(modeSelect(context, newIDs)); - }; - - operation.point = function(val) { - _pastePoint = val; - return operation; - }; - - operation.available = function() { - return context.mode().id === 'browse'; - }; - - operation.disabled = function() { - return !context.copyIDs().length; - }; - - operation.tooltip = function() { - var oldGraph = context.copyGraph(); - var ids = context.copyIDs(); - if (!ids.length) { - return _t('operations.paste.nothing_copied'); - } - return ids.length === 1 ? - _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) : - _t('operations.paste.description.multiple', { n: ids.length.toString() }); - }; - - operation.annotation = function() { - var ids = context.copyIDs(); - return ids.length === 1 ? - _t('operations.paste.annotation.single') : - _t('operations.paste.annotation.multiple', { n: ids.length.toString() }); - }; - - operation.id = 'paste'; - operation.keys = [uiCmd('⌘V')]; - operation.title = _t('operations.paste.title'); - - return operation; - } - - function operationReverse(context, selectedIDs) { - - var operation = function() { - context.perform(function combinedReverseAction(graph) { - actions().forEach(function(action) { - graph = action(graph); - }); - return graph; - }, operation.annotation()); - context.validator().validate(); - }; - - function actions(situation) { - return selectedIDs.map(function(entityID) { - var entity = context.hasEntity(entityID); - if (!entity) return; - - if (situation === 'toolbar') { - if (entity.type === 'way' && - (!entity.isOneWay() && !entity.isSided())) return; - } - - var geometry = entity.geometry(context.graph()); - if (entity.type !== 'node' && geometry !== 'line') return; - - var action = actionReverse(entityID); - if (action.disabled(context.graph())) return; - - return action; - }).filter(Boolean); - } - - function reverseTypeID() { - var acts = actions(); - var nodeActionCount = acts.filter(function(act) { - var entity = context.hasEntity(act.entityID()); - return entity && entity.type === 'node'; - }).length; - var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features'); - if (typeID !== 'features' && acts.length > 1) typeID += 's'; - return typeID; - } - - - operation.available = function(situation) { - return actions(situation).length > 0; - }; - - - operation.disabled = function() { - return false; - }; - - - operation.tooltip = function() { - return _t('operations.reverse.description.' + reverseTypeID()); - }; - - - operation.annotation = function() { - return _t('operations.reverse.annotation.' + reverseTypeID()); - }; - - - operation.id = 'reverse'; - operation.keys = [_t('operations.reverse.key')]; - operation.title = _t('operations.reverse.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationSplit(context, selectedIDs) { - var vertices = selectedIDs - .filter(function(id) { return context.graph().geometry(id) === 'vertex'; }); - var entityID = vertices[0]; - var action = actionSplit(entityID); - var ways = []; - - if (vertices.length === 1) { - if (entityID && selectedIDs.length > 1) { - var ids = selectedIDs.filter(function(id) { return id !== entityID; }); - action.limitWays(ids); - } - ways = action.ways(context.graph()); - } - - - var operation = function() { - var difference = context.perform(action, operation.annotation()); - context.enter(modeSelect(context, difference.extantIDs())); - }; - - - operation.available = function() { - return vertices.length === 1; - }; - - - operation.disabled = function() { - var reason = action.disabled(context.graph()); - if (reason) { - return reason; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } - - return false; - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - if (disable) { - return _t('operations.split.' + disable); - } else if (ways.length === 1) { - return _t('operations.split.description.' + context.graph().geometry(ways[0].id)); - } else { - return _t('operations.split.description.multiple'); - } - }; - - - operation.annotation = function() { - return ways.length === 1 ? - _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) : - _t('operations.split.annotation.multiple', { n: ways.length }); - }; - - - operation.id = 'split'; - operation.keys = [_t('operations.split.key')]; - operation.title = _t('operations.split.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - function operationStraighten(context, selectedIDs) { - var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; }); - var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; }); - var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple'); - - var _nodes = utilGetAllNodes(selectedIDs, context.graph()); - var _coords = _nodes.map(function(n) { return n.loc; }); - var _extent = utilTotalExtent(selectedIDs, context.graph()); - var _action = chooseAction(); - var _geometry; - - - function chooseAction() { - // straighten selected nodes - if (_wayIDs.length === 0 && _nodeIDs.length > 2) { - _geometry = 'points'; - return actionStraightenNodes(_nodeIDs, context.projection); - - // straighten selected ways (possibly between range of 2 selected nodes) - } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) { - var startNodeIDs = []; - var endNodeIDs = []; - - for (var i = 0; i < selectedIDs.length; i++) { - var entity = context.entity(selectedIDs[i]); - if (entity.type === 'node') { - continue; - } else if (entity.type !== 'way' || entity.isClosed()) { - return null; // exit early, can't straighten these - } - - startNodeIDs.push(entity.first()); - endNodeIDs.push(entity.last()); - } - - // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end) - startNodeIDs = startNodeIDs.filter(function(n) { - return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n); - }); - endNodeIDs = endNodeIDs.filter(function(n) { - return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n); - }); - - // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints) - if (utilArrayDifference(startNodeIDs, endNodeIDs).length + - utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; - - // Ensure path contains at least 3 unique nodes - var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()) - .map(function(node) { return node.id; }); - if (wayNodeIDs.length <= 2) return null; - - // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path - if (_nodeIDs.length === 2 && ( - wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1 - )) return null; - - if (_nodeIDs.length) { - // If we're only straightenting between two points, we only need that extent visible - _extent = utilTotalExtent(_nodeIDs, context.graph()); - } - - _geometry = _wayIDs.length === 1 ? 'line' : 'lines'; - return actionStraightenWay(selectedIDs, context.projection); - } - - return null; - } - - - function operation() { - if (!_action) return; - - context.perform(_action, operation.annotation()); - - window.setTimeout(function() { - context.validator().validate(); - }, 300); // after any transition - } - - - operation.available = function() { - return Boolean(_action); - }; - - - operation.disabled = function() { - var reason = _action.disabled(context.graph()); - if (reason) { - return reason; - } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) { - return 'too_large'; - } else if (someMissing()) { - return 'not_downloaded'; - } else if (selectedIDs.some(context.hasHiddenConnections)) { - return 'connected_to_hidden'; - } - - return false; - - - function someMissing() { - if (context.inIntro()) return false; - var osm = context.connection(); - if (osm) { - var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); }); - if (missing.length) { - missing.forEach(function(loc) { context.loadTileAtLoc(loc); }); - return true; - } - } - return false; - } - }; - - - operation.tooltip = function() { - var disable = operation.disabled(); - return disable ? - _t('operations.straighten.' + disable + '.' + _amount) : - _t('operations.straighten.description.' + _geometry); - }; - - - operation.annotation = function() { - return _t('operations.straighten.annotation.' + _geometry); - }; - - - operation.id = 'straighten'; - operation.keys = [_t('operations.straighten.key')]; - operation.title = _t('operations.straighten.title'); - operation.behavior = behaviorOperation(context).which(operation); - - return operation; - } - - var Operations = /*#__PURE__*/Object.freeze({ - __proto__: null, - operationCircularize: operationCircularize, - operationContinue: operationContinue, - operationCopy: operationCopy, - operationDelete: operationDelete, - operationDisconnect: operationDisconnect, - operationDowngrade: operationDowngrade, - operationExtract: operationExtract, - operationMerge: operationMerge, - operationMove: operationMove, - operationOrthogonalize: operationOrthogonalize, - operationPaste: operationPaste, - operationReflectShort: operationReflectShort, - operationReflectLong: operationReflectLong, - operationReverse: operationReverse, - operationRotate: operationRotate, - operationSplit: operationSplit, - operationStraighten: operationStraighten - }); - - var _relatedParent; - - - function modeSelect(context, selectedIDs) { - var mode = { - id: 'select', - button: 'browse' - }; - - var keybinding = utilKeybinding('select'); - - var _breatheBehavior = behaviorBreathe(); - var _modeDragNode = modeDragNode(context); - var _selectBehavior; - var _behaviors = []; - - var _operations = []; - var _newFeature = false; - var _follow = false; - - - function singular() { - if (selectedIDs && selectedIDs.length === 1) { - return context.hasEntity(selectedIDs[0]); - } - } - - function selectedEntities() { - return selectedIDs.map(function(id) { - return context.hasEntity(id); - }).filter(Boolean); - } - - - function checkSelectedIDs() { - var ids = []; - if (Array.isArray(selectedIDs)) { - ids = selectedIDs.filter(function(id) { - return context.hasEntity(id); - }); - } - - if (!ids.length) { - context.enter(modeBrowse(context)); - return false; - } else if ((selectedIDs.length > 1 && ids.length === 1) || - (selectedIDs.length === 1 && ids.length > 1)) { - // switch between single- and multi-select UI - context.enter(modeSelect(context, ids)); - return false; - } - - selectedIDs = ids; - return true; - } - - - // find the common parent ways for nextVertex, previousVertex - function commonParents() { - var graph = context.graph(); - var commonParents = []; - - for (var i = 0; i < selectedIDs.length; i++) { - var entity = context.hasEntity(selectedIDs[i]); - if (!entity || entity.geometry(graph) !== 'vertex') { - return []; // selection includes some not vertexes - } - - var currParents = graph.parentWays(entity).map(function(w) { return w.id; }); - if (!commonParents.length) { - commonParents = currParents; - continue; - } - - commonParents = utilArrayIntersection(commonParents, currParents); - if (!commonParents.length) { - return []; - } - } - - return commonParents; - } - - - function singularParent() { - var parents = commonParents(); - if (!parents || parents.length === 0) { - _relatedParent = null; - return null; - } - - // relatedParent is used when we visit a vertex with multiple - // parents, and we want to remember which parent line we started on. - - if (parents.length === 1) { - _relatedParent = parents[0]; // remember this parent for later - return _relatedParent; - } - - if (parents.indexOf(_relatedParent) !== -1) { - return _relatedParent; // prefer the previously seen parent - } - - return parents[0]; - } - - - mode.selectedIDs = function(val) { - if (!arguments.length) return selectedIDs; - selectedIDs = val; - return mode; - }; - - - mode.zoomToSelected = function() { - context.map().zoomToEase(selectedEntities()); - }; - - - mode.newFeature = function(val) { - if (!arguments.length) return _newFeature; - _newFeature = val; - return mode; - }; - - - mode.selectBehavior = function(val) { - if (!arguments.length) return _selectBehavior; - _selectBehavior = val; - return mode; - }; - - - mode.follow = function(val) { - if (!arguments.length) return _follow; - _follow = val; - return mode; - }; - - function loadOperations() { - - _operations.forEach(function(operation) { - if (operation.behavior) { - context.uninstall(operation.behavior); - } - }); - - _operations = Object.values(Operations) - .map(function(o) { return o(context, selectedIDs); }) - .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; }); - - var copyOperation = operationCopy(context, selectedIDs); - if (copyOperation.available()) { - // group copy operation with delete/downgrade - _operations.push(copyOperation); - } - - var downgradeOperation = operationDowngrade(context, selectedIDs); - // don't allow delete if downgrade is available - var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs); - - _operations.push(lastOperation); - - _operations.forEach(function(operation) { - if (operation.behavior) { - context.install(operation.behavior); - } - }); - - // remove any displayed menu - context.ui().closeEditMenu(); - } - - mode.operations = function() { - return _operations; - }; - - - mode.enter = function() { - if (!checkSelectedIDs()) return; - - context.features().forceVisible(selectedIDs); - - _modeDragNode.restoreSelectedIDs(selectedIDs); - - loadOperations(); - - if (!_behaviors.length) { - if (!_selectBehavior) _selectBehavior = behaviorSelect(context); - - _behaviors = [ - behaviorPaste(context), - _breatheBehavior, - behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), - _selectBehavior, - behaviorLasso(context), - _modeDragNode.behavior, - modeDragNote(context).behavior - ]; - } - _behaviors.forEach(context.install); - - keybinding - .on(_t('inspector.zoom_to.key'), mode.zoomToSelected) - .on(['[', 'pgup'], previousVertex) - .on([']', 'pgdown'], nextVertex) - .on(['{', uiCmd('⌘['), 'home'], firstVertex) - .on(['}', uiCmd('⌘]'), 'end'], lastVertex) - .on(uiCmd('⇧←'), nudgeSelection([-10, 0])) - .on(uiCmd('⇧↑'), nudgeSelection([0, -10])) - .on(uiCmd('⇧→'), nudgeSelection([10, 0])) - .on(uiCmd('⇧↓'), nudgeSelection([0, 10])) - .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0])) - .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100])) - .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0])) - .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100])) - .on(['\\', 'pause'], nextParent) - .on('⎋', esc, true); - - select(document) - .call(keybinding); - - context.ui().sidebar - .select(selectedIDs, _newFeature); - - context.history() - .on('change.select', function() { - loadOperations(); - // reselect after change in case relation members were removed or added - selectElements(); - }) - .on('undone.select', checkSelectedIDs) - .on('redone.select', checkSelectedIDs); - - context.map() - .on('drawn.select', selectElements) - .on('crossEditableZoom.select', function() { - selectElements(); - _breatheBehavior.restartIfNeeded(context.surface()); - }); - - context.map().doubleUpHandler() - .on('doubleUp.modeSelect', didDoubleUp); - - - selectElements(); - - if (_follow) { - var extent = geoExtent(); - var graph = context.graph(); - selectedIDs.forEach(function(id) { - var entity = context.entity(id); - extent._extend(entity.extent(graph)); - }); - - var loc = extent.center(); - context.map().centerEase(loc); - } - - - function nudgeSelection(delta) { - return function() { - // prevent nudging during low zoom selection - if (!context.map().withinEditableZoom()) return; - - var moveOp = operationMove(context, selectedIDs); - if (moveOp.disabled()) { - context.ui().flash - .duration(4000) - .iconName('#iD-operation-' + moveOp.id) - .iconClass('operation disabled') - .text(moveOp.tooltip)(); - } else { - context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation()); - } - }; - } - - - function didDoubleUp(loc) { - if (!context.map().withinEditableZoom()) return; - - var target = select(event.target); - - var datum = target.datum(); - var entity = datum && datum.properties && datum.properties.entity; - if (!entity) return; - - if (entity instanceof osmWay && target.classed('target')) { - var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection); - var prev = entity.nodes[choice.index - 1]; - var next = entity.nodes[choice.index]; - - context.perform( - actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()), - _t('operations.add.annotation.vertex') - ); - - } else if (entity.type === 'midpoint') { - context.perform( - actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()), - _t('operations.add.annotation.vertex')); - } - } - - - function selectElements() { - if (!checkSelectedIDs()) return; - - var surface = context.surface(); - - surface.selectAll('.selected-member') - .classed('selected-member', false); - - surface.selectAll('.selected') - .classed('selected', false); - - surface.selectAll('.related') - .classed('related', false); - - singularParent(); - if (_relatedParent) { - surface.selectAll(utilEntitySelector([_relatedParent])) - .classed('related', true); - } - - if (context.map().withinEditableZoom()) { - // Apply selection styling if not in wide selection - - surface - .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */)) - .classed('selected-member', true); - surface - .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())) - .classed('selected', true); - } - - } - - - function esc() { - if (context.container().select('.combobox').size()) return; - context.enter(modeBrowse(context)); - } - - - function firstVertex() { - event.preventDefault(); - var entity = singular(); - var parent = singularParent(); - var way; - - if (entity && entity.type === 'way') { - way = entity; - } else if (parent) { - way = context.entity(parent); - } - - if (way) { - context.enter( - modeSelect(context, [way.first()]).follow(true) - ); - } - } - - - function lastVertex() { - event.preventDefault(); - var entity = singular(); - var parent = singularParent(); - var way; - - if (entity && entity.type === 'way') { - way = entity; - } else if (parent) { - way = context.entity(parent); - } - - if (way) { - context.enter( - modeSelect(context, [way.last()]).follow(true) - ); - } - } - - - function previousVertex() { - event.preventDefault(); - var parent = singularParent(); - if (!parent) return; - - var way = context.entity(parent); - var length = way.nodes.length; - var curr = way.nodes.indexOf(selectedIDs[0]); - var index = -1; - - if (curr > 0) { - index = curr - 1; - } else if (way.isClosed()) { - index = length - 2; - } - - if (index !== -1) { - context.enter( - modeSelect(context, [way.nodes[index]]).follow(true) - ); - } - } - - - function nextVertex() { - event.preventDefault(); - var parent = singularParent(); - if (!parent) return; - - var way = context.entity(parent); - var length = way.nodes.length; - var curr = way.nodes.indexOf(selectedIDs[0]); - var index = -1; - - if (curr < length - 1) { - index = curr + 1; - } else if (way.isClosed()) { - index = 0; - } - - if (index !== -1) { - context.enter( - modeSelect(context, [way.nodes[index]]).follow(true) - ); - } - } - - - function nextParent() { - event.preventDefault(); - var parents = commonParents(); - if (!parents || parents.length < 2) return; - - var index = parents.indexOf(_relatedParent); - if (index < 0 || index > parents.length - 2) { - _relatedParent = parents[0]; - } else { - _relatedParent = parents[index + 1]; - } - - var surface = context.surface(); - surface.selectAll('.related') - .classed('related', false); - - if (_relatedParent) { - surface.selectAll(utilEntitySelector([_relatedParent])) - .classed('related', true); - } - } - }; - - - mode.exit = function() { - - _newFeature = false; - - _operations.forEach(function(operation) { - if (operation.behavior) { - context.uninstall(operation.behavior); - } - }); - _operations = []; - - _behaviors.forEach(context.uninstall); - - select(document) - .call(keybinding.unbind); - - context.ui().closeEditMenu(); - - context.history() - .on('change.select', null) - .on('undone.select', null) - .on('redone.select', null); - - var surface = context.surface(); - - surface - .selectAll('.selected-member') - .classed('selected-member', false); - - surface - .selectAll('.selected') - .classed('selected', false); - - surface - .selectAll('.highlighted') - .classed('highlighted', false); - - surface - .selectAll('.related') - .classed('related', false); - - context.map().on('drawn.select', null); - context.ui().sidebar.hide(); - context.features().forceVisible([]); - - var entity = singular(); - if (_newFeature && entity && entity.type === 'relation' && - // no tags - Object.keys(entity.tags).length === 0 && - // no parent relations - context.graph().parentRelations(entity).length === 0 && - // no members or one member with no role - (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role)) - ) { - // the user added this relation but didn't edit it at all, so just delete it - var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */); - context.perform(deleteAction, _t('operations.delete.annotation.relation')); - } - }; - - - return mode; - } - - function uiLasso(context) { - var group, polygon; - - lasso.coordinates = []; - - function lasso(selection) { - context.container() - .classed('lasso', true); - - group = selection - .append('g') - .attr('class', 'lasso hide'); - - polygon = group - .append('path') - .attr('class', 'lasso-path'); - - group - .call(uiToggle(true)); - } - - - function draw() { - if (polygon) { - polygon.data([lasso.coordinates]) - .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; }); - } - } - - - lasso.extent = function () { - return lasso.coordinates.reduce(function(extent, point) { - return extent.extend(geoExtent(point)); - }, geoExtent()); - }; - - - lasso.p = function(_) { - if (!arguments.length) return lasso; - lasso.coordinates.push(_); - draw(); - return lasso; - }; - - - lasso.close = function() { - if (group) { - group.call(uiToggle(false, function() { - select(this).remove(); - })); - } - context.container().classed('lasso', false); - }; - - - return lasso; - } - - function behaviorLasso(context) { - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - var behavior = function(selection) { - var lasso; - - - function pointerdown() { - var button = 0; // left - if (event.button === button && event.shiftKey === true) { - lasso = null; - - select(window) - .on(_pointerPrefix + 'move.lasso', pointermove) - .on(_pointerPrefix + 'up.lasso', pointerup); - - event.stopPropagation(); - } - } - - - function pointermove() { - if (!lasso) { - lasso = uiLasso(context); - context.surface().call(lasso); - } - - lasso.p(context.map().mouse()); - } - - - function normalize(a, b) { - return [ - [Math.min(a[0], b[0]), Math.min(a[1], b[1])], - [Math.max(a[0], b[0]), Math.max(a[1], b[1])] - ]; - } - - - function lassoed() { - if (!lasso) return []; - - var graph = context.graph(); - var limitToNodes; - - if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) { - // only select from the visible nodes - limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph)); - } else if (!context.map().editableDataEnabled()) { - return []; - } - - var bounds = lasso.extent().map(context.projection.invert); - var extent = geoExtent(normalize(bounds[0], bounds[1])); - - var intersects = context.history().intersects(extent).filter(function(entity) { - return entity.type === 'node' && - (!limitToNodes || limitToNodes.has(entity)) && - geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && - !context.features().isHidden(entity, graph, entity.geometry(graph)); - }); - - // sort the lassoed nodes as best we can - intersects.sort(function(node1, node2) { - var parents1 = graph.parentWays(node1); - var parents2 = graph.parentWays(node2); - if (parents1.length && parents2.length) { - // both nodes are vertices - - var sharedParents = utilArrayIntersection(parents1, parents2); - if (sharedParents.length) { - var sharedParentNodes = sharedParents[0].nodes; - // vertices are members of the same way; sort them in their listed order - return sharedParentNodes.indexOf(node1.id) - - sharedParentNodes.indexOf(node2.id); - } else { - // vertices do not share a way; group them by their respective parent ways - return parseFloat(parents1[0].id.slice(1)) - - parseFloat(parents2[0].id.slice(1)); - } - - } else if (parents1.length || parents2.length) { - // only one node is a vertex; sort standalone points before vertices - return parents1.length - parents2.length; - } - // both nodes are standalone points; sort left to right - return node1.loc[0] - node2.loc[0]; - }); - - return intersects.map(function(entity) { return entity.id; }); - } - - - function pointerup() { - select(window) - .on(_pointerPrefix + 'move.lasso', null) - .on(_pointerPrefix + 'up.lasso', null); - - if (!lasso) return; - - var ids = lassoed(); - lasso.close(); - - if (ids.length) { - context.enter(modeSelect(context, ids)); - } - } - - selection - .on(_pointerPrefix + 'down.lasso', pointerdown); - }; - - - behavior.off = function(selection) { - selection.on(_pointerPrefix + 'down.lasso', null); - }; - - - return behavior; - } - - function modeBrowse(context) { - var mode = { - button: 'browse', - id: 'browse', - title: _t('modes.browse.title'), - description: _t('modes.browse.description') - }; - var sidebar; - - var _selectBehavior; - var _behaviors = []; - - - mode.selectBehavior = function(val) { - if (!arguments.length) return _selectBehavior; - _selectBehavior = val; - return mode; - }; - - - mode.enter = function() { - if (!_behaviors.length) { - if (!_selectBehavior) _selectBehavior = behaviorSelect(context); - _behaviors = [ - behaviorPaste(context), - behaviorHover(context).on('hover', context.ui().sidebar.hover), - _selectBehavior, - behaviorLasso(context), - modeDragNode(context).behavior, - modeDragNote(context).behavior - ]; - } - _behaviors.forEach(context.install); - - // Get focus on the body. - if (document.activeElement && document.activeElement.blur) { - document.activeElement.blur(); - } - - if (sidebar) { - context.ui().sidebar.show(sidebar); - } else { - context.ui().sidebar.select(null); - } - }; - - - mode.exit = function() { - context.ui().sidebar.hover.cancel(); - _behaviors.forEach(context.uninstall); - - if (sidebar) { - context.ui().sidebar.hide(); - } - }; - - - mode.sidebar = function(_) { - if (!arguments.length) return sidebar; - sidebar = _; - return mode; - }; - - - mode.operations = function() { - return [operationPaste(context)]; - }; - - - return mode; - } - - function behaviorAddWay(context) { - var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode'); - var draw = behaviorDraw(context); - - function behavior(surface) { - draw.on('click', function() { dispatch$1.apply('start', this, arguments); }) - .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); }) - .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); }) - .on('cancel', behavior.cancel) - .on('finish', behavior.cancel); - - context.map() - .dblclickZoomEnable(false); - - surface.call(draw); - } - - - behavior.off = function(surface) { - surface.call(draw.off); - }; - - - behavior.cancel = function() { - window.setTimeout(function() { - context.map().dblclickZoomEnable(true); - }, 1000); - - context.enter(modeBrowse(context)); - }; - - - return utilRebind(behavior, dispatch$1, 'on'); - } - - function behaviorHash(context) { - - // cached window.location.hash - var _cachedHash = null; - // allowable latitude range - var _latitudeLimit = 90 - 1e-8; - - function computedHashParameters() { - var map = context.map(); - var center = map.center(); - var zoom = map.zoom(); - var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); - var oldParams = utilObjectOmit(utilStringQs(window.location.hash), - ['comment', 'source', 'hashtags', 'walkthrough'] - ); - var newParams = {}; - - delete oldParams.id; - var selected = context.selectedIDs().filter(function(id) { - return context.hasEntity(id); - }); - if (selected.length) { - newParams.id = selected.join(','); - } - - newParams.map = zoom.toFixed(2) + - '/' + center[1].toFixed(precision) + - '/' + center[0].toFixed(precision); - - return Object.assign(oldParams, newParams); - } - - function computedHash() { - return '#' + utilQsString(computedHashParameters(), true); - } - - function computedTitle(includeChangeCount) { - - var baseTitle = context.documentTitleBase() || 'iD'; - var contextual; - var changeCount; - var titleID; - - var selected = context.selectedIDs().filter(function(id) { - return context.hasEntity(id); - }); - if (selected.length) { - var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph()); - if (selected.length > 1 ) { - contextual = _t('title.labeled_and_more', { - labeled: firstLabel, - count: (selected.length - 1).toString() - }); - } else { - contextual = firstLabel; - } - titleID = 'context'; - } - - if (includeChangeCount) { - changeCount = context.history().difference().summary().length; - if (changeCount > 0) { - titleID = contextual ? 'changes_context' : 'changes'; - } - } - - if (titleID) { - return _t('title.format.' + titleID, { - changes: changeCount, - base: baseTitle, - context: contextual - }); - } - - return baseTitle; - } - - function updateTitle(includeChangeCount) { - if (!context.setsDocumentTitle()) return; - - var newTitle = computedTitle(includeChangeCount); - if (document.title !== newTitle) { - document.title = newTitle; - } - } - - function updateHashIfNeeded() { - if (context.inIntro()) return; - - var latestHash = computedHash(); - if (_cachedHash !== latestHash) { - _cachedHash = latestHash; - - // Update the URL hash without affecting the browser navigation stack, - // though unavoidably creating a browser history entry - window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash); - - // set the title we want displayed for the browser tab/window - updateTitle(true /* includeChangeCount */); - } - } - - var _throttledUpdate = throttle(updateHashIfNeeded, 500); - var _throttledUpdateTitle = throttle(function() { - updateTitle(true /* includeChangeCount */); - }, 500); - - function hashchange() { - - // ignore spurious hashchange events - if (window.location.hash === _cachedHash) return; - - _cachedHash = window.location.hash; - - var q = utilStringQs(_cachedHash); - var mapArgs = (q.map || '').split('/').map(Number); - - if (mapArgs.length < 3 || mapArgs.some(isNaN)) { - // replace bogus hash - updateHashIfNeeded(); - - } else { - // don't update if the new hash already reflects the state of iD - if (_cachedHash === computedHash()) return; - - var mode = context.mode(); - - context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]); - - if (q.id) { - var ids = q.id.split(',').filter(function(id) { - return context.hasEntity(id); - }); - var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids); - if (ids.length && !skip) { - context.enter(modeSelect(context, ids)); - return; - } - } - - var center = context.map().center(); - var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]); - var maxdist = 500; - - // Don't allow the hash location to change too much while drawing - // This can happen if the user accidently hit the back button. #3996 - if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) { - context.enter(modeBrowse(context)); - return; - } - } - } - - function behavior() { - context.map() - .on('move.behaviorHash', _throttledUpdate); - - context.history() - .on('change.behaviorHash', _throttledUpdateTitle); - - context - .on('enter.behaviorHash', _throttledUpdate); - - select(window) - .on('hashchange.behaviorHash', hashchange); - - if (window.location.hash) { - var q = utilStringQs(window.location.hash); - - if (q.id) { - //if (!context.history().hasRestorableChanges()) { - // targeting specific features: download, select, and zoom to them - context.zoomToEntity(q.id.split(',')[0], !q.map); - //} - } - - if (q.walkthrough === 'true') { - behavior.startWalkthrough = true; - } - - if (q.map) { - behavior.hadHash = true; - } - - hashchange(); - - updateTitle(false); - } - } - - behavior.off = function() { - _throttledUpdate.cancel(); - _throttledUpdateTitle.cancel(); - - context.map() - .on('move.behaviorHash', null); - - context - .on('enter.behaviorHash', null); - - select(window) - .on('hashchange.behaviorHash', null); - - window.location.hash = ''; - }; - - return behavior; - } - - /* - iD.coreDifference represents the difference between two graphs. - It knows how to calculate the set of entities that were - created, modified, or deleted, and also contains the logic - for recursively extending a difference to the complete set - of entities that will require a redraw, taking into account - child and parent relationships. - */ - function coreDifference(base, head) { - var _changes = {}; - var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties' - var _diff = {}; - - function checkEntityID(id) { - var h = head.entities[id]; - var b = base.entities[id]; - - if (h === b) return; - if (_changes[id]) return; - - if (!h && b) { - _changes[id] = { base: b, head: h }; - _didChange.deletion = true; - return; - } - if (h && !b) { - _changes[id] = { base: b, head: h }; - _didChange.addition = true; - return; - } - - if (h && b) { - if (h.members && b.members && !fastDeepEqual(h.members, b.members)) { - _changes[id] = { base: b, head: h }; - _didChange.geometry = true; - _didChange.properties = true; - return; - } - if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) { - _changes[id] = { base: b, head: h }; - _didChange.geometry = true; - } - if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) { - _changes[id] = { base: b, head: h }; - _didChange.geometry = true; - } - if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) { - _changes[id] = { base: b, head: h }; - _didChange.properties = true; - } - } - } - - function load() { - // HOT CODE: there can be many thousands of downloaded entities, so looping - // through them all can become a performance bottleneck. Optimize by - // resolving duplicates and using a basic `for` loop - var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities))); - for (var i = 0; i < ids.length; i++) { - checkEntityID(ids[i]); - } - } - load(); - - - _diff.length = function length() { - return Object.keys(_changes).length; - }; - - - _diff.changes = function changes() { - return _changes; - }; - - _diff.didChange = _didChange; - - - // pass true to include affected relation members - _diff.extantIDs = function extantIDs(includeRelMembers) { - var result = new Set(); - Object.keys(_changes).forEach(function(id) { - if (_changes[id].head) { - result.add(id); - } - - var h = _changes[id].head; - var b = _changes[id].base; - var entity = h || b; - - if (includeRelMembers && entity.type === 'relation') { - var mh = h ? h.members.map(function(m) { return m.id; }) : []; - var mb = b ? b.members.map(function(m) { return m.id; }) : []; - utilArrayUnion(mh, mb).forEach(function(memberID) { - if (head.hasEntity(memberID)) { - result.add(memberID); - } - }); - } - }); - - return Array.from(result); - }; - - - _diff.modified = function modified() { - var result = []; - Object.values(_changes).forEach(function(change) { - if (change.base && change.head) { - result.push(change.head); - } - }); - return result; - }; - - - _diff.created = function created() { - var result = []; - Object.values(_changes).forEach(function(change) { - if (!change.base && change.head) { - result.push(change.head); - } - }); - return result; - }; - - - _diff.deleted = function deleted() { - var result = []; - Object.values(_changes).forEach(function(change) { - if (change.base && !change.head) { - result.push(change.base); - } - }); - return result; - }; - - - _diff.summary = function summary() { - var relevant = {}; - - var keys = Object.keys(_changes); - for (var i = 0; i < keys.length; i++) { - var change = _changes[keys[i]]; - - if (change.head && change.head.geometry(head) !== 'vertex') { - addEntity(change.head, head, change.base ? 'modified' : 'created'); - - } else if (change.base && change.base.geometry(base) !== 'vertex') { - addEntity(change.base, base, 'deleted'); - - } else if (change.base && change.head) { // modified vertex - var moved = !fastDeepEqual(change.base.loc, change.head.loc); - var retagged = !fastDeepEqual(change.base.tags, change.head.tags); - - if (moved) { - addParents(change.head); - } - - if (retagged || (moved && change.head.hasInterestingTags())) { - addEntity(change.head, head, 'modified'); - } - - } else if (change.head && change.head.hasInterestingTags()) { // created vertex - addEntity(change.head, head, 'created'); - - } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex - addEntity(change.base, base, 'deleted'); - } - } - - return Object.values(relevant); - - - function addEntity(entity, graph, changeType) { - relevant[entity.id] = { - entity: entity, - graph: graph, - changeType: changeType - }; - } - - function addParents(entity) { - var parents = head.parentWays(entity); - for (var j = parents.length - 1; j >= 0; j--) { - var parent = parents[j]; - if (!(parent.id in relevant)) { - addEntity(parent, head, 'modified'); - } - } - } - }; - - - // returns complete set of entities that require a redraw - // (optionally within given `extent`) - _diff.complete = function complete(extent) { - var result = {}; - var id, change; - - for (id in _changes) { - change = _changes[id]; - - var h = change.head; - var b = change.base; - var entity = h || b; - var i; - - if (extent && - (!h || !h.intersects(extent, head)) && - (!b || !b.intersects(extent, base))) - continue; - - result[id] = h; - - if (entity.type === 'way') { - var nh = h ? h.nodes : []; - var nb = b ? b.nodes : []; - var diff; - - diff = utilArrayDifference(nh, nb); - for (i = 0; i < diff.length; i++) { - result[diff[i]] = head.hasEntity(diff[i]); - } - - diff = utilArrayDifference(nb, nh); - for (i = 0; i < diff.length; i++) { - result[diff[i]] = head.hasEntity(diff[i]); - } - } - - if (entity.type === 'relation' && entity.isMultipolygon()) { - var mh = h ? h.members.map(function(m) { return m.id; }) : []; - var mb = b ? b.members.map(function(m) { return m.id; }) : []; - var ids = utilArrayUnion(mh, mb); - for (i = 0; i < ids.length; i++) { - var member = head.hasEntity(ids[i]); - if (!member) continue; // not downloaded - if (extent && !member.intersects(extent, head)) continue; // not visible - result[ids[i]] = member; - } - } - - addParents(head.parentWays(entity), result); - addParents(head.parentRelations(entity), result); - } - - return result; - - - function addParents(parents, result) { - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; - if (parent.id in result) continue; - - result[parent.id] = parent; - addParents(head.parentRelations(parent), result); - } - } - }; - - - return _diff; - } - - function coreTree(head) { - // tree for entities - var _rtree = new RBush(); - var _bboxes = {}; - - // maintain a separate tree for granular way segments - var _segmentsRTree = new RBush(); - var _segmentsBBoxes = {}; - var _segmentsByWayId = {}; - - var tree = {}; - - - function entityBBox(entity) { - var bbox = entity.extent(head).bbox(); - bbox.id = entity.id; - _bboxes[entity.id] = bbox; - return bbox; - } - - - function segmentBBox(segment) { - var extent = segment.extent(head); - // extent can be null if the node entites aren't in the graph for some reason - if (!extent) return null; - - var bbox = extent.bbox(); - bbox.segment = segment; - _segmentsBBoxes[segment.id] = bbox; - return bbox; - } - - - function removeEntity(entity) { - _rtree.remove(_bboxes[entity.id]); - delete _bboxes[entity.id]; - - if (_segmentsByWayId[entity.id]) { - _segmentsByWayId[entity.id].forEach(function(segment) { - _segmentsRTree.remove(_segmentsBBoxes[segment.id]); - delete _segmentsBBoxes[segment.id]; - }); - delete _segmentsByWayId[entity.id]; - } - } - - - function loadEntities(entities) { - _rtree.load(entities.map(entityBBox)); - - var segments = []; - entities.forEach(function(entity) { - if (entity.segments) { - var entitySegments = entity.segments(head); - // cache these to make them easy to remove later - _segmentsByWayId[entity.id] = entitySegments; - segments = segments.concat(entitySegments); - } - }); - if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); - } - - - function updateParents(entity, insertions, memo) { - head.parentWays(entity).forEach(function(way) { - if (_bboxes[way.id]) { - removeEntity(way); - insertions[way.id] = way; - } - updateParents(way, insertions, memo); - }); - - head.parentRelations(entity).forEach(function(relation) { - if (memo[entity.id]) return; - memo[entity.id] = true; - if (_bboxes[relation.id]) { - removeEntity(relation); - insertions[relation.id] = relation; - } - updateParents(relation, insertions, memo); - }); - } - - - tree.rebase = function(entities, force) { - var insertions = {}; - - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - if (!entity.visible) continue; - - if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) { - if (!force) { - continue; - } else if (_bboxes[entity.id]) { - removeEntity(entity); - } - } - - insertions[entity.id] = entity; - updateParents(entity, insertions, {}); - } - - loadEntities(Object.values(insertions)); - - return tree; - }; - - - function updateToGraph(graph) { - if (graph === head) return; - - var diff = coreDifference(head, graph); - - head = graph; - - var changed = diff.didChange; - if (!changed.addition && !changed.deletion && !changed.geometry) return; - - var insertions = {}; - - if (changed.deletion) { - diff.deleted().forEach(function(entity) { - removeEntity(entity); - }); - } - - if (changed.geometry) { - diff.modified().forEach(function(entity) { - removeEntity(entity); - insertions[entity.id] = entity; - updateParents(entity, insertions, {}); - }); - } - - if (changed.addition) { - diff.created().forEach(function(entity) { - insertions[entity.id] = entity; - }); - } - - loadEntities(Object.values(insertions)); - } - - // returns an array of entities with bounding boxes overlapping `extent` for the given `graph` - tree.intersects = function(extent, graph) { - updateToGraph(graph); - return _rtree.search(extent.bbox()) - .map(function(bbox) { return graph.entity(bbox.id); }); - }; - - // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph` - tree.waySegments = function(extent, graph) { - updateToGraph(graph); - return _segmentsRTree.search(extent.bbox()) - .map(function(bbox) { return bbox.segment; }); - }; - - - return tree; - } - - function uiModal(selection, blocking) { - let keybinding = utilKeybinding('modal'); - let previous = selection.select('div.modal'); - let animate = previous.empty(); - - previous.transition() - .duration(200) - .style('opacity', 0) - .remove(); - - let shaded = selection - .append('div') - .attr('class', 'shaded') - .style('opacity', 0); - - shaded.close = () => { - shaded - .transition() - .duration(200) - .style('opacity',0) - .remove(); - - modal - .transition() - .duration(200) - .style('top','0px'); - - select(document) - .call(keybinding.unbind); - }; - - - let modal = shaded - .append('div') - .attr('class', 'modal fillL'); - - if (!blocking) { - shaded.on('click.remove-modal', () => { - if (event.target === this) { - shaded.close(); - } - }); - - modal - .append('button') - .attr('class', 'close') - .on('click', shaded.close) - .call(svgIcon('#iD-icon-close')); - - keybinding - .on('⌫', shaded.close) - .on('⎋', shaded.close); - - select(document) - .call(keybinding); - } - - modal - .append('div') - .attr('class', 'content'); - - if (animate) { - shaded.transition().style('opacity', 1); - } else { - shaded.style('opacity', 1); - } - - return shaded; - } - - function uiLoading(context) { - let _modalSelection = select(null); - let _message = ''; - let _blocking = false; - - - let loading = (selection) => { - _modalSelection = uiModal(selection, _blocking); - - let loadertext = _modalSelection.select('.content') - .classed('loading-modal', true) - .append('div') - .attr('class', 'modal-section fillL'); - - loadertext - .append('img') - .attr('class', 'loader') - .attr('src', context.imagePath('loader-white.gif')); - - loadertext - .append('h3') - .text(_message); - - _modalSelection.select('button.close') - .attr('class', 'hide'); - - return loading; - }; - - - loading.message = function(val) { - if (!arguments.length) return _message; - _message = val; - return loading; - }; - - - loading.blocking = function(val) { - if (!arguments.length) return _blocking; - _blocking = val; - return loading; - }; - - - loading.close = () => { - _modalSelection.remove(); - }; - - - loading.isShown = () => { - return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode; - }; - - - return loading; - } - - function coreHistory(context) { - var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone'); - var lock = utilSessionMutex('lock'); - - // restorable if iD not open in another window/tab and a saved history exists in localStorage - var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history')); - - var duration = 150; - var _imageryUsed = []; - var _photoOverlaysUsed = []; - var _checkpoints = {}; - var _pausedGraph; - var _stack; - var _index; - var _tree; - - - // internal _act, accepts list of actions and eased time - function _act(actions, t) { - actions = Array.prototype.slice.call(actions); - - var annotation; - if (typeof actions[actions.length - 1] !== 'function') { - annotation = actions.pop(); - } - - var graph = _stack[_index].graph; - for (var i = 0; i < actions.length; i++) { - graph = actions[i](graph, t); - } - - return { - graph: graph, - annotation: annotation, - imageryUsed: _imageryUsed, - photoOverlaysUsed: _photoOverlaysUsed, - transform: context.projection.transform(), - selectedIDs: context.selectedIDs() - }; - } - - - // internal _perform with eased time - function _perform(args, t) { - var previous = _stack[_index].graph; - _stack = _stack.slice(0, _index + 1); - var actionResult = _act(args, t); - _stack.push(actionResult); - _index++; - return change(previous); - } - - - // internal _replace with eased time - function _replace(args, t) { - var previous = _stack[_index].graph; - // assert(_index == _stack.length - 1) - var actionResult = _act(args, t); - _stack[_index] = actionResult; - return change(previous); - } - - - // internal _overwrite with eased time - function _overwrite(args, t) { - var previous = _stack[_index].graph; - if (_index > 0) { - _index--; - _stack.pop(); - } - _stack = _stack.slice(0, _index + 1); - var actionResult = _act(args, t); - _stack.push(actionResult); - _index++; - return change(previous); - } - - - // determine difference and dispatch a change event - function change(previous) { - var difference = coreDifference(previous, history.graph()); - if (!_pausedGraph) { - dispatch$1.call('change', this, difference); - } - return difference; - } - - - // iD uses namespaced keys so multiple installations do not conflict - function getKey(n) { - return 'iD_' + window.location.origin + '_' + n; - } - - - var history = { - - graph: function() { - return _stack[_index].graph; - }, - - - tree: function() { - return _tree; - }, - - - base: function() { - return _stack[0].graph; - }, - - - merge: function(entities/*, extent*/) { - var stack = _stack.map(function(state) { return state.graph; }); - _stack[0].graph.rebase(entities, stack, false); - _tree.rebase(entities, false); - - dispatch$1.call('merge', this, entities); - }, - - - perform: function() { - // complete any transition already in progress - select(document).interrupt('history.perform'); - - var transitionable = false; - var action0 = arguments[0]; - - if (arguments.length === 1 || - (arguments.length === 2 && (typeof arguments[1] !== 'function'))) { - transitionable = !!action0.transitionable; - } - - if (transitionable) { - var origArguments = arguments; - select(document) - .transition('history.perform') - .duration(duration) - .ease(linear$1) - .tween('history.tween', function() { - return function(t) { - if (t < 1) _overwrite([action0], t); - }; - }) - .on('start', function() { - _perform([action0], 0); - }) - .on('end interrupt', function() { - _overwrite(origArguments, 1); - }); - - } else { - return _perform(arguments); - } - }, - - - replace: function() { - select(document).interrupt('history.perform'); - return _replace(arguments, 1); - }, - - - // Same as calling pop and then perform - overwrite: function() { - select(document).interrupt('history.perform'); - return _overwrite(arguments, 1); - }, - - - pop: function(n) { - select(document).interrupt('history.perform'); - - var previous = _stack[_index].graph; - if (isNaN(+n) || +n < 0) { - n = 1; - } - while (n-- > 0 && _index > 0) { - _index--; - _stack.pop(); - } - return change(previous); - }, - - - // Back to the previous annotated state or _index = 0. - undo: function() { - select(document).interrupt('history.perform'); - - var previousStack = _stack[_index]; - var previous = previousStack.graph; - while (_index > 0) { - _index--; - if (_stack[_index].annotation) break; - } - - dispatch$1.call('undone', this, _stack[_index], previousStack); - return change(previous); - }, - - - // Forward to the next annotated state. - redo: function() { - select(document).interrupt('history.perform'); - - var previousStack = _stack[_index]; - var previous = previousStack.graph; - var tryIndex = _index; - while (tryIndex < _stack.length - 1) { - tryIndex++; - if (_stack[tryIndex].annotation) { - _index = tryIndex; - dispatch$1.call('redone', this, _stack[_index], previousStack); - break; - } - } - - return change(previous); - }, - - - pauseChangeDispatch: function() { - if (!_pausedGraph) { - _pausedGraph = _stack[_index].graph; - } - }, - - - resumeChangeDispatch: function() { - if (_pausedGraph) { - var previous = _pausedGraph; - _pausedGraph = null; - return change(previous); - } - }, - - - undoAnnotation: function() { - var i = _index; - while (i >= 0) { - if (_stack[i].annotation) return _stack[i].annotation; - i--; - } - }, - - - redoAnnotation: function() { - var i = _index + 1; - while (i <= _stack.length - 1) { - if (_stack[i].annotation) return _stack[i].annotation; - i++; - } - }, - - - // Returns the entities from the active graph with bounding boxes - // overlapping the given `extent`. - intersects: function(extent) { - return _tree.intersects(extent, _stack[_index].graph); - }, - - - difference: function() { - var base = _stack[0].graph; - var head = _stack[_index].graph; - return coreDifference(base, head); - }, - - - changes: function(action) { - var base = _stack[0].graph; - var head = _stack[_index].graph; - - if (action) { - head = action(head); - } - - var difference = coreDifference(base, head); - - return { - modified: difference.modified(), - created: difference.created(), - deleted: difference.deleted() - }; - }, - - - hasChanges: function() { - return this.difference().length() > 0; - }, - - - imageryUsed: function(sources) { - if (sources) { - _imageryUsed = sources; - return history; - } else { - var s = new Set(); - _stack.slice(1, _index + 1).forEach(function(state) { - state.imageryUsed.forEach(function(source) { - if (source !== 'Custom') { - s.add(source); - } - }); - }); - return Array.from(s); - } - }, - - - photoOverlaysUsed: function(sources) { - if (sources) { - _photoOverlaysUsed = sources; - return history; - } else { - var s = new Set(); - _stack.slice(1, _index + 1).forEach(function(state) { - if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) { - state.photoOverlaysUsed.forEach(function(photoOverlay) { - s.add(photoOverlay); - }); - } - }); - return Array.from(s); - } - }, - - - // save the current history state - checkpoint: function(key) { - _checkpoints[key] = { - stack: _stack, - index: _index - }; - return history; - }, - - - // restore history state to a given checkpoint or reset completely - reset: function(key) { - if (key !== undefined && _checkpoints.hasOwnProperty(key)) { - _stack = _checkpoints[key].stack; - _index = _checkpoints[key].index; - } else { - _stack = [{graph: coreGraph()}]; - _index = 0; - _tree = coreTree(_stack[0].graph); - _checkpoints = {}; - } - dispatch$1.call('change'); - return history; - }, - - - // `toIntroGraph()` is used to export the intro graph used by the walkthrough. - // - // To use it: - // 1. Start the walkthrough. - // 2. Get to a "free editing" tutorial step - // 3. Make your edits to the walkthrough map - // 4. In your browser dev console run: - // `id.history().toIntroGraph()` - // 5. This outputs stringified JSON to the browser console - // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor - toIntroGraph: function() { - var nextID = { n: 0, r: 0, w: 0 }; - var permIDs = {}; - var graph = this.graph(); - var baseEntities = {}; - - // clone base entities.. - Object.values(graph.base().entities).forEach(function(entity) { - var copy = copyIntroEntity(entity); - baseEntities[copy.id] = copy; - }); - - // replace base entities with head entities.. - Object.keys(graph.entities).forEach(function(id) { - var entity = graph.entities[id]; - if (entity) { - var copy = copyIntroEntity(entity); - baseEntities[copy.id] = copy; - } else { - delete baseEntities[id]; - } - }); - - // swap temporary for permanent ids.. - Object.values(baseEntities).forEach(function(entity) { - if (Array.isArray(entity.nodes)) { - entity.nodes = entity.nodes.map(function(node) { - return permIDs[node] || node; - }); - } - if (Array.isArray(entity.members)) { - entity.members = entity.members.map(function(member) { - member.id = permIDs[member.id] || member.id; - return member; - }); - } - }); - - return JSON.stringify({ dataIntroGraph: baseEntities }); - - - function copyIntroEntity(source) { - var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); - - // Note: the copy is no longer an osmEntity, so it might not have `tags` - if (copy.tags && !Object.keys(copy.tags)) { - delete copy.tags; - } - - if (Array.isArray(copy.loc)) { - copy.loc[0] = +copy.loc[0].toFixed(6); - copy.loc[1] = +copy.loc[1].toFixed(6); - } - - var match = source.id.match(/([nrw])-\d*/); // temporary id - if (match !== null) { - var nrw = match[1]; - var permID; - do { permID = nrw + (++nextID[nrw]); } - while (baseEntities.hasOwnProperty(permID)); - - copy.id = permIDs[source.id] = permID; - } - return copy; - } - }, - - - toJSON: function() { - if (!this.hasChanges()) return; - - var allEntities = {}; - var baseEntities = {}; - var base = _stack[0]; - - var s = _stack.map(function(i) { - var modified = []; - var deleted = []; - - Object.keys(i.graph.entities).forEach(function(id) { - var entity = i.graph.entities[id]; - if (entity) { - var key = osmEntity.key(entity); - allEntities[key] = entity; - modified.push(key); - } else { - deleted.push(id); - } - - // make sure that the originals of changed or deleted entities get merged - // into the base of the _stack after restoring the data from JSON. - if (id in base.graph.entities) { - baseEntities[id] = base.graph.entities[id]; - } - if (entity && entity.nodes) { - // get originals of pre-existing child nodes - entity.nodes.forEach(function(nodeID) { - if (nodeID in base.graph.entities) { - baseEntities[nodeID] = base.graph.entities[nodeID]; - } - }); - } - // get originals of parent entities too - var baseParents = base.graph._parentWays[id]; - if (baseParents) { - baseParents.forEach(function(parentID) { - if (parentID in base.graph.entities) { - baseEntities[parentID] = base.graph.entities[parentID]; - } - }); - } - }); - - var x = {}; - - if (modified.length) x.modified = modified; - if (deleted.length) x.deleted = deleted; - if (i.imageryUsed) x.imageryUsed = i.imageryUsed; - if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed; - if (i.annotation) x.annotation = i.annotation; - if (i.transform) x.transform = i.transform; - if (i.selectedIDs) x.selectedIDs = i.selectedIDs; - - return x; - }); - - return JSON.stringify({ - version: 3, - entities: Object.values(allEntities), - baseEntities: Object.values(baseEntities), - stack: s, - nextIDs: osmEntity.id.next, - index: _index, - // note the time the changes were saved - timestamp: (new Date()).getTime() - }); - }, - - - fromJSON: function(json, loadChildNodes) { - var h = JSON.parse(json); - var loadComplete = true; - - osmEntity.id.next = h.nextIDs; - _index = h.index; - - if (h.version === 2 || h.version === 3) { - var allEntities = {}; - - h.entities.forEach(function(entity) { - allEntities[osmEntity.key(entity)] = osmEntity(entity); - }); - - if (h.version === 3) { - // This merges originals for changed entities into the base of - // the _stack even if the current _stack doesn't have them (for - // example when iD has been restarted in a different region) - var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); }); - var stack = _stack.map(function(state) { return state.graph; }); - _stack[0].graph.rebase(baseEntities, stack, true); - _tree.rebase(baseEntities, true); - - // When we restore a modified way, we also need to fetch any missing - // childnodes that would normally have been downloaded with it.. #2142 - if (loadChildNodes) { - var osm = context.connection(); - var baseWays = baseEntities - .filter(function(e) { return e.type === 'way'; }); - var nodeIDs = baseWays - .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []); - var missing = nodeIDs - .filter(function(n) { return !_stack[0].graph.hasEntity(n); }); - - if (missing.length && osm) { - loadComplete = false; - context.map().redrawEnable(false); - - var loading = uiLoading(context).blocking(true); - context.container().call(loading); - - var childNodesLoaded = function(err, result) { - if (!err) { - var visibleGroups = utilArrayGroupBy(result.data, 'visible'); - var visibles = visibleGroups.true || []; // alive nodes - var invisibles = visibleGroups.false || []; // deleted nodes - - if (visibles.length) { - var visibleIDs = visibles.map(function(entity) { return entity.id; }); - var stack = _stack.map(function(state) { return state.graph; }); - missing = utilArrayDifference(missing, visibleIDs); - _stack[0].graph.rebase(visibles, stack, true); - _tree.rebase(visibles, true); - } - - // fetch older versions of nodes that were deleted.. - invisibles.forEach(function(entity) { - osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded); - }); - } - - if (err || !missing.length) { - loading.close(); - context.map().redrawEnable(true); - dispatch$1.call('change'); - dispatch$1.call('restore', this); - } - }; - - osm.loadMultiple(missing, childNodesLoaded); - } - } - } - - _stack = h.stack.map(function(d) { - var entities = {}, entity; - - if (d.modified) { - d.modified.forEach(function(key) { - entity = allEntities[key]; - entities[entity.id] = entity; - }); - } - - if (d.deleted) { - d.deleted.forEach(function(id) { - entities[id] = undefined; - }); - } - - return { - graph: coreGraph(_stack[0].graph).load(entities), - annotation: d.annotation, - imageryUsed: d.imageryUsed, - photoOverlaysUsed: d.photoOverlaysUsed, - transform: d.transform, - selectedIDs: d.selectedIDs - }; - }); - - } else { // original version - _stack = h.stack.map(function(d) { - var entities = {}; - - for (var i in d.entities) { - var entity = d.entities[i]; - entities[i] = entity === 'undefined' ? undefined : osmEntity(entity); - } - - d.graph = coreGraph(_stack[0].graph).load(entities); - return d; - }); - } - - var transform = _stack[_index].transform; - if (transform) { - context.map().transformEase(transform, 0); // 0 = immediate, no easing - } - - if (loadComplete) { - dispatch$1.call('change'); - dispatch$1.call('restore', this); - } - - return history; - }, - - - lock: function() { - return lock.lock(); - }, - - - unlock: function() { - lock.unlock(); - }, - - - save: function() { - if (lock.locked() && - // don't overwrite existing, unresolved changes - !_hasUnresolvedRestorableChanges) { - - corePreferences(getKey('saved_history'), history.toJSON() || null); - } - return history; - }, - - - // delete the history version saved in localStorage - clearSaved: function() { - context.debouncedSave.cancel(); - if (lock.locked()) { - _hasUnresolvedRestorableChanges = false; - corePreferences(getKey('saved_history'), null); - - // clear the changeset metadata associated with the saved history - corePreferences('comment', null); - corePreferences('hashtags', null); - corePreferences('source', null); - } - return history; - }, - - - savedHistoryJSON: function() { - return corePreferences(getKey('saved_history')); - }, - - - hasRestorableChanges: function() { - return _hasUnresolvedRestorableChanges; - }, - - - // load history from a version stored in localStorage - restore: function() { - if (lock.locked()) { - _hasUnresolvedRestorableChanges = false; - var json = this.savedHistoryJSON(); - if (json) history.fromJSON(json, true); - } - }, - - - _getKey: getKey - - }; - - - history.reset(); - - return utilRebind(history, dispatch$1, 'on'); - } - - /** - * Look for roads that can be connected to other roads with a short extension - */ - function validationAlmostJunction(context) { - const type = 'almost_junction'; - const EXTEND_TH_METERS = 5; - const WELD_TH_METERS = 0.75; - // Comes from considering bounding case of parallel ways - const CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; - // Comes from considering bounding case of perpendicular ways - const SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS); - - function isHighway(entity) { - return entity.type === 'way' - && osmRoutableHighwayTagValues[entity.tags.highway]; - } - - function isTaggedAsNotContinuing(node) { - return node.tags.noexit === 'yes' - || node.tags.amenity === 'parking_entrance' - || (node.tags.entrance && node.tags.entrance !== 'no'); - } - - - const validation = function checkAlmostJunction(entity, graph) { - if (!isHighway(entity)) return []; - if (entity.isDegenerate()) return []; - - const tree = context.history().tree(); - const extendableNodeInfos = findConnectableEndNodesByExtension(entity); - - let issues = []; - - extendableNodeInfos.forEach(extendableNodeInfo => { - issues.push(new validationIssue({ - type, - subtype: 'highway-highway', - severity: 'warning', - message(context) { - const entity1 = context.hasEntity(this.entityIds[0]); - if (this.entityIds[0] === this.entityIds[2]) { - return entity1 ? _t('issues.almost_junction.self.message', { - feature: utilDisplayLabel(entity1, context.graph()) - }) : ''; - } else { - const entity2 = context.hasEntity(this.entityIds[2]); - return (entity1 && entity2) ? _t('issues.almost_junction.message', { - feature: utilDisplayLabel(entity1, context.graph()), - feature2: utilDisplayLabel(entity2, context.graph()) - }) : ''; - } - }, - reference: showReference, - entityIds: [ - entity.id, - extendableNodeInfo.node.id, - extendableNodeInfo.wid, - ], - loc: extendableNodeInfo.node.loc, - hash: JSON.stringify(extendableNodeInfo.node.loc), - data: { - midId: extendableNodeInfo.mid.id, - edge: extendableNodeInfo.edge, - cross_loc: extendableNodeInfo.cross_loc - }, - dynamicFixes: makeFixes - })); - }); - - return issues; - - function makeFixes(context) { - let fixes = [new validationIssueFix({ - icon: 'iD-icon-abutment', - title: _t('issues.fix.connect_features.title'), - onClick(context) { - const annotation = _t('issues.fix.connect_almost_junction.annotation'); - const [, endNodeId, crossWayId] = this.issue.entityIds; - const midNode = context.entity(this.issue.data.midId); - const endNode = context.entity(endNodeId); - const crossWay = context.entity(crossWayId); - - // When endpoints are close, just join if resulting small change in angle (#7201) - const nearEndNodes = findNearbyEndNodes(endNode, crossWay); - if (nearEndNodes.length > 0) { - const collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes); - if (collinear) { - context.perform( - actionMergeNodes([collinear.id, endNode.id], collinear.loc), - annotation - ); - return; - } - } - - const targetEdge = this.issue.data.edge; - const crossLoc = this.issue.data.cross_loc; - const edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])]; - const closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); - - // already a point nearby, just connect to that - if (closestNodeInfo.distance < WELD_TH_METERS) { - context.perform( - actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), - annotation - ); - // else add the end node to the edge way - } else { - context.perform( - actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode), - annotation - ); - } - } - })]; - - const node = context.hasEntity(this.entityIds[1]); - if (node && !node.hasInterestingTags()) { - // node has no descriptive tags, suggest noexit fix - fixes.push(new validationIssueFix({ - icon: 'maki-barrier', - title: _t('issues.fix.tag_as_disconnected.title'), - onClick(context) { - const nodeID = this.issue.entityIds[1]; - const tags = Object.assign({}, context.entity(nodeID).tags); - tags.noexit = 'yes'; - context.perform( - actionChangeTags(nodeID, tags), - _t('issues.fix.tag_as_disconnected.annotation') - ); - } - })); - } - - return fixes; - } - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.almost_junction.highway-highway.reference')); - } - - function isExtendableCandidate(node, way) { - // can not accurately test vertices on tiles not downloaded from osm - #5938 - const osm = services.osm; - if (osm && !osm.isDataLoaded(node.loc)) { - return false; - } - if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) { - return false; - } - - let occurences = 0; - for (const index in way.nodes) { - if (way.nodes[index] === node.id) { - occurences += 1; - if (occurences > 1) { - return false; - } - } - } - return true; - } - - function findConnectableEndNodesByExtension(way) { - let results = []; - if (way.isClosed()) return results; - - let testNodes; - const indices = [0, way.nodes.length - 1]; - indices.forEach(nodeIndex => { - const nodeID = way.nodes[nodeIndex]; - const node = graph.entity(nodeID); - - if (!isExtendableCandidate(node, way)) return; - - const connectionInfo = canConnectByExtend(way, nodeIndex); - if (!connectionInfo) return; - - testNodes = graph.childNodes(way).slice(); // shallow copy - testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); - - // don't flag issue if connecting the ways would cause self-intersection - if (geoHasSelfIntersections(testNodes, nodeID)) return; - - results.push(connectionInfo); - }); - - return results; - } - - function findNearbyEndNodes(node, way) { - return [ - way.nodes[0], - way.nodes[way.nodes.length - 1] - ].map(d => graph.entity(d)) - .filter(d => { - // Node cannot be near to itself, but other endnode of same way could be - return d.id !== node.id - && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH; - }); - } - - function findSmallJoinAngle(midNode, tipNode, endNodes) { - // Both nodes could be close, so want to join whichever is closest to collinear - let joinTo; - let minAngle = Infinity; - - // Checks midNode -> tipNode -> endNode for collinearity - endNodes.forEach(endNode => { - const a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI; - const a2 = geoAngle(midNode, endNode, context.projection) + Math.PI; - const diff = Math.max(a1, a2) - Math.min(a1, a2); - - if (diff < minAngle) { - joinTo = endNode; - minAngle = diff; - } - }); - - /* Threshold set by considering right angle triangle - based on node joining threshold and extension distance */ - if (minAngle <= SIG_ANGLE_TH) return joinTo; - - return null; - } - - function hasTag(tags, key) { - return tags[key] !== undefined && tags[key] !== 'no'; - } - - function canConnectWays(way, way2) { - - // allow self-connections - if (way.id === way2.id) return true; - - // if one is bridge or tunnel, both must be bridge or tunnel - if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && - !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false; - if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) && - !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) return false; - - // must have equivalent layers and levels - const layer1 = way.tags.layer || '0', - layer2 = way2.tags.layer || '0'; - if (layer1 !== layer2) return false; - - const level1 = way.tags.level || '0', - level2 = way2.tags.level || '0'; - if (level1 !== level2) return false; - - return true; - } - - function canConnectByExtend(way, endNodeIdx) { - const tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point - const midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge - const tipNode = graph.entity(tipNid); - const midNode = graph.entity(midNid); - const lon = tipNode.loc[0]; - const lat = tipNode.loc[1]; - const lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2; - const lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2; - const queryExtent = geoExtent([ - [lon - lon_range, lat - lat_range], - [lon + lon_range, lat + lat_range] - ]); - - // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location - const edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc); - const t = EXTEND_TH_METERS / edgeLen + 1.0; - const extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); - - // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways - const segmentInfos = tree.waySegments(queryExtent, graph); - for (let i = 0; i < segmentInfos.length; i++) { - let segmentInfo = segmentInfos[i]; - - let way2 = graph.entity(segmentInfo.wayId); - - if (!isHighway(way2)) continue; - - if (!canConnectWays(way, way2)) continue; - - let nAid = segmentInfo.nodes[0], - nBid = segmentInfo.nodes[1]; - - if (nAid === tipNid || nBid === tipNid) continue; - - let nA = graph.entity(nAid), - nB = graph.entity(nBid); - let crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]); - if (crossLoc) { - return { - mid: midNode, - node: tipNode, - wid: way2.id, - edge: [nA.id, nB.id], - cross_loc: crossLoc - }; - } - } - return null; - } - }; - - validation.type = type; - - return validation; - } - - function validationCloseNodes(context) { - var type = 'close_nodes'; - - var pointThresholdMeters = 0.2; - - var validation = function(entity, graph) { - if (entity.type === 'node') { - return getIssuesForNode(entity); - } else if (entity.type === 'way') { - return getIssuesForWay(entity); - } - return []; - - function getIssuesForNode(node) { - var parentWays = graph.parentWays(node); - if (parentWays.length) { - return getIssuesForVertex(node, parentWays); - } else { - return getIssuesForDetachedPoint(node); - } - } - - function wayTypeFor(way) { - - if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary'; - if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor'; - if ((way.tags.building && way.tags.building !== 'no') || - (way.tags['building:part'] && way.tags['building:part'] !== 'no')) return 'building'; - if (osmPathHighwayTagValues[way.tags.highway]) return 'path'; - - var parentRelations = graph.parentRelations(way); - for (var i in parentRelations) { - var relation = parentRelations[i]; - - if (relation.tags.type === 'boundary') return 'boundary'; - - if (relation.isMultipolygon()) { - if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor'; - if ((relation.tags.building && relation.tags.building !== 'no') || - (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) return 'building'; - } - } - - return 'other'; - } - - function shouldCheckWay(way) { - - // don't flag issues where merging would create degenerate ways - if (way.nodes.length <= 2 || - (way.isClosed() && way.nodes.length <= 4)) return false; - - var bbox = way.extent(graph).bbox(); - var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); - // don't flag close nodes in very small ways - if (hypotenuseMeters < 1.5) return false; - - return true; - } - - function getIssuesForWay(way) { - if (!shouldCheckWay(way)) return []; - - var issues = [], - nodes = graph.childNodes(way); - for (var i = 0; i < nodes.length - 1; i++) { - var node1 = nodes[i]; - var node2 = nodes[i+1]; - - var issue = getWayIssueIfAny(node1, node2, way); - if (issue) issues.push(issue); - } - return issues; - } - - function getIssuesForVertex(node, parentWays) { - var issues = []; - - function checkForCloseness(node1, node2, way) { - var issue = getWayIssueIfAny(node1, node2, way); - if (issue) issues.push(issue); - } - - for (var i = 0; i < parentWays.length; i++) { - var parentWay = parentWays[i]; - - if (!shouldCheckWay(parentWay)) continue; - - var lastIndex = parentWay.nodes.length - 1; - for (var j = 0; j < parentWay.nodes.length; j++) { - if (j !== 0) { - if (parentWay.nodes[j-1] === node.id) { - checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay); - } - } - if (j !== lastIndex) { - if (parentWay.nodes[j+1] === node.id) { - checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay); - } - } - } - } - return issues; - } - - function thresholdMetersForWay(way) { - if (!shouldCheckWay(way)) return 0; - - var wayType = wayTypeFor(way); - - // don't flag boundaries since they might be highly detailed and can't be easily verified - if (wayType === 'boundary') return 0; - // expect some features to be mapped with higher levels of detail - if (wayType === 'indoor') return 0.01; - if (wayType === 'building') return 0.05; - if (wayType === 'path') return 0.1; - return 0.2; - } - - function getIssuesForDetachedPoint(node) { - - var issues = []; - - var lon = node.loc[0]; - var lat = node.loc[1]; - var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2; - var lat_range = geoMetersToLat(pointThresholdMeters) / 2; - var queryExtent = geoExtent([ - [lon - lon_range, lat - lat_range], - [lon + lon_range, lat + lat_range] - ]); - - var intersected = context.history().tree().intersects(queryExtent, graph); - for (var j = 0; j < intersected.length; j++) { - var nearby = intersected[j]; - - if (nearby.id === node.id) continue; - if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue; - - if (nearby.loc === node.loc || - geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) { - - // allow very close points if tags indicate the z-axis might vary - var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true }; - var zAxisDifferentiates = false; - for (var key in zAxisKeys) { - var nodeValue = node.tags[key] || '0'; - var nearbyValue = nearby.tags[key] || '0'; - if (nodeValue !== nearbyValue) { - zAxisDifferentiates = true; - break; - } - } - if (zAxisDifferentiates) continue; - - issues.push(new validationIssue({ - type: type, - subtype: 'detached', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]), - entity2 = context.hasEntity(this.entityIds[1]); - return (entity && entity2) ? _t('issues.close_nodes.detached.message', { - feature: utilDisplayLabel(entity, context.graph()), - feature2: utilDisplayLabel(entity2, context.graph()) - }) : ''; - }, - reference: showReference, - entityIds: [node.id, nearby.id], - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-operation-disconnect', - title: _t('issues.fix.move_points_apart.title') - }), - new validationIssueFix({ - icon: 'iD-icon-layers', - title: _t('issues.fix.use_different_layers_or_levels.title') - }) - ]; - } - })); - } - } - - return issues; - - function showReference(selection) { - var referenceText = _t('issues.close_nodes.detached.reference'); - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(referenceText); - } - } - - function getWayIssueIfAny(node1, node2, way) { - if (node1.id === node2.id || - (node1.hasInterestingTags() && node2.hasInterestingTags())) { - return null; - } - - if (node1.loc !== node2.loc) { - var parentWays1 = graph.parentWays(node1); - var parentWays2 = new Set(graph.parentWays(node2)); - - var sharedWays = parentWays1.filter(function(parentWay) { - return parentWays2.has(parentWay); - }); - - var thresholds = sharedWays.map(function(parentWay) { - return thresholdMetersForWay(parentWay); - }); - - var threshold = Math.min(...thresholds); - var distance = geoSphericalDistance(node1.loc, node2.loc); - if (distance > threshold) return null; - } - - return new validationIssue({ - type: type, - subtype: 'vertices', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : ''; - }, - reference: showReference, - entityIds: [way.id, node1.id, node2.id], - loc: node1.loc, - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-icon-plus', - title: _t('issues.fix.merge_points.title'), - onClick: function(context) { - var entityIds = this.issue.entityIds; - var action = actionMergeNodes([entityIds[1], entityIds[2]]); - context.perform(action, _t('issues.fix.merge_close_vertices.annotation')); - } - }), - new validationIssueFix({ - icon: 'iD-operation-disconnect', - title: _t('issues.fix.move_points_apart.title') - }) - ]; - } - }); - - function showReference(selection) { - var referenceText = _t('issues.close_nodes.reference'); - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(referenceText); - } - } - - }; - - - validation.type = type; - - return validation; - } - - function validationCrossingWays(context) { - var type = 'crossing_ways'; - - // returns the way or its parent relation, whichever has a useful feature type - function getFeatureWithFeatureTypeTagsForWay(way, graph) { - if (getFeatureType(way, graph) === null) { - // if the way doesn't match a feature type, check its parent relations - var parentRels = graph.parentRelations(way); - for (var i = 0; i < parentRels.length; i++) { - var rel = parentRels[i]; - if (getFeatureType(rel, graph) !== null) { - return rel; - } - } - } - return way; - } - - - function hasTag(tags, key) { - return tags[key] !== undefined && tags[key] !== 'no'; - } - - function taggedAsIndoor(tags) { - return hasTag(tags, 'indoor') || - hasTag(tags, 'level') || - tags.highway === 'corridor'; - } - - function allowsBridge(featureType) { - return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway'; - } - function allowsTunnel(featureType) { - return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway'; - } - - - function getFeatureTypeForCrossingCheck(way, graph) { - var feature = getFeatureWithFeatureTypeTagsForWay(way, graph); - return getFeatureType(feature, graph); - } - - // discard - var ignoredBuildings = { - demolished: true, dismantled: true, proposed: true, razed: true - }; - - - function getFeatureType(entity, graph) { - - var geometry = entity.geometry(graph); - if (geometry !== 'line' && geometry !== 'area') return null; - - var tags = entity.tags; - - if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building'; - if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; - - // don't check railway or waterway areas - if (geometry !== 'line') return null; - - if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway'; - if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway'; - - return null; - } - - - function isLegitCrossing(way1, featureType1, way2, featureType2) { - var tags1 = way1.tags; - var tags2 = way2.tags; - - // assume 0 by default - var level1 = tags1.level || '0'; - var level2 = tags2.level || '0'; - - if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) { - // assume features don't interact if they're indoor on different levels - return true; - } - - // assume 0 by default; don't use way.layer() since we account for structures here - var layer1 = tags1.layer || '0'; - var layer2 = tags2.layer || '0'; - - if (allowsBridge(featureType1) && allowsBridge(featureType2)) { - if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true; - if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; - // crossing bridges must use different layers - if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true; - } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true; - else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true; - - if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) { - if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true; - if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; - // crossing tunnels must use different layers - if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true; - } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) return true; - else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) return true; - - // don't flag crossing waterways and pier/highways - if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true; - if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true; - - if (featureType1 === 'building' || featureType2 === 'building') { - // for building crossings, different layers are enough - if (layer1 !== layer2) return true; - } - return false; - } - - - // highway values for which we shouldn't recommend connecting to waterways - var highwaysDisallowingFords = { - motorway: true, motorway_link: true, trunk: true, trunk_link: true, - primary: true, primary_link: true, secondary: true, secondary_link: true - }; - var nonCrossingHighways = { track: true }; - - function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) { - var featureType1 = getFeatureType(entity1, graph); - var featureType2 = getFeatureType(entity2, graph); - - var geometry1 = entity1.geometry(graph); - var geometry2 = entity2.geometry(graph); - var bothLines = geometry1 === 'line' && geometry2 === 'line'; - - if (featureType1 === featureType2) { - if (featureType1 === 'highway') { - var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway]; - var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway]; - if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) { - // one feature is a path but not both - - var roadFeature = entity1IsPath ? entity2 : entity1; - if (nonCrossingHighways[roadFeature.tags.highway]) { - // don't mark path connections with certain roads as crossings - return {}; - } - var pathFeature = entity1IsPath ? entity1 : entity2; - if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) { - // if the path is a crossing, match the crossing type - return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {}; - } - // don't add a `crossing` subtag to ambiguous crossings - return bothLines ? { highway: 'crossing' } : {}; - } - return {}; - } - if (featureType1 === 'waterway') return {}; - if (featureType1 === 'railway') return {}; - - } else { - var featureTypes = [featureType1, featureType2]; - if (featureTypes.indexOf('highway') !== -1) { - if (featureTypes.indexOf('railway') !== -1) { - if (osmPathHighwayTagValues[entity1.tags.highway] || - osmPathHighwayTagValues[entity2.tags.highway]) { - // path-rail connections use this tag - return bothLines ? { railway: 'crossing' } : {}; - } else { - // road-rail connections use this tag - return bothLines ? { railway: 'level_crossing' } : {}; - } - } - - if (featureTypes.indexOf('waterway') !== -1) { - // do not allow fords on structures - if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null; - if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null; - - if (highwaysDisallowingFords[entity1.tags.highway] || - highwaysDisallowingFords[entity2.tags.highway]) { - // do not allow fords on major highways - return null; - } - return bothLines ? { ford: 'yes' } : {}; - } - } - } - return null; - } - - - function findCrossingsByWay(way1, graph, tree) { - var edgeCrossInfos = []; - if (way1.type !== 'way') return edgeCrossInfos; - - var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph); - if (way1FeatureType === null) return edgeCrossInfos; - - var checkedSingleCrossingWays = {}; - - // declare vars ahead of time to reduce garbage collection - var i, j; - var extent; - var n1, n2, nA, nB, nAId, nBId; - var segment1, segment2; - var oneOnly; - var segmentInfos, segment2Info, way2, way2FeatureType; - var way1Nodes = graph.childNodes(way1); - var comparedWays = {}; - for (i = 0; i < way1Nodes.length - 1; i++) { - n1 = way1Nodes[i]; - n2 = way1Nodes[i + 1]; - extent = geoExtent([ - [ - Math.min(n1.loc[0], n2.loc[0]), - Math.min(n1.loc[1], n2.loc[1]) - ], - [ - Math.max(n1.loc[0], n2.loc[0]), - Math.max(n1.loc[1], n2.loc[1]) - ] - ]); - - // Optimize by only checking overlapping segments, not every segment - // of overlapping ways - segmentInfos = tree.waySegments(extent, graph); - - for (j = 0; j < segmentInfos.length; j++) { - segment2Info = segmentInfos[j]; - - // don't check for self-intersection in this validation - if (segment2Info.wayId === way1.id) continue; - - // skip if this way was already checked and only one issue is needed - if (checkedSingleCrossingWays[segment2Info.wayId]) continue; - - // mark this way as checked even if there are no crossings - comparedWays[segment2Info.wayId] = true; - - way2 = graph.hasEntity(segment2Info.wayId); - if (!way2) continue; - - // only check crossing highway, waterway, building, and railway - way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph); - if (way2FeatureType === null || - isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) { - continue; - } - - // create only one issue for building crossings - oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building'; - - nAId = segment2Info.nodes[0]; - nBId = segment2Info.nodes[1]; - if (nAId === n1.id || nAId === n2.id || - nBId === n1.id || nBId === n2.id) { - // n1 or n2 is a connection node; skip - continue; - } - nA = graph.hasEntity(nAId); - if (!nA) continue; - nB = graph.hasEntity(nBId); - if (!nB) continue; - - segment1 = [n1.loc, n2.loc]; - segment2 = [nA.loc, nB.loc]; - var point = geoLineIntersection(segment1, segment2); - if (point) { - edgeCrossInfos.push({ - wayInfos: [ - { - way: way1, - featureType: way1FeatureType, - edge: [n1.id, n2.id] - }, - { - way: way2, - featureType: way2FeatureType, - edge: [nA.id, nB.id] - } - ], - crossPoint: point - }); - if (oneOnly) { - checkedSingleCrossingWays[way2.id] = true; - break; - } - } - } - } - return edgeCrossInfos; - } - - - function waysToCheck(entity, graph) { - var featureType = getFeatureType(entity, graph); - if (!featureType) return []; - - if (entity.type === 'way') { - return [entity]; - } else if (entity.type === 'relation') { - return entity.members.reduce(function(array, member) { - if (member.type === 'way' && - // only look at geometry ways - (!member.role || member.role === 'outer' || member.role === 'inner')) { - var entity = graph.hasEntity(member.id); - // don't add duplicates - if (entity && array.indexOf(entity) === -1) { - array.push(entity); - } - } - return array; - }, []); - } - return []; - } - - - var validation = function checkCrossingWays(entity, graph) { - - var tree = context.history().tree(); - - var ways = waysToCheck(entity, graph); - - var issues = []; - // declare these here to reduce garbage collection - var wayIndex, crossingIndex, crossings; - for (wayIndex in ways) { - crossings = findCrossingsByWay(ways[wayIndex], graph, tree); - for (crossingIndex in crossings) { - issues.push(createIssue(crossings[crossingIndex], graph)); - } - } - return issues; - }; - - - function createIssue(crossing, graph) { - - // use the entities with the tags that define the feature type - crossing.wayInfos.sort(function(way1Info, way2Info) { - var type1 = way1Info.featureType; - var type2 = way2Info.featureType; - if (type1 === type2) { - return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph); - } else if (type1 === 'waterway') { - return true; - } else if (type2 === 'waterway') { - return false; - } - return type1 < type2; - }); - var entities = crossing.wayInfos.map(function(wayInfo) { - return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph); - }); - var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge]; - var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType]; - - var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph); - - var featureType1 = crossing.wayInfos[0].featureType; - var featureType2 = crossing.wayInfos[1].featureType; - - var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags); - var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && - allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel'); - var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && - allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge'); - - var subtype = [featureType1, featureType2].sort().join('-'); - - var crossingTypeID = subtype; - - if (isCrossingIndoors) { - crossingTypeID = 'indoor-indoor'; - } else if (isCrossingTunnels) { - crossingTypeID = 'tunnel-tunnel'; - } else if (isCrossingBridges) { - crossingTypeID = 'bridge-bridge'; - } - if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) { - crossingTypeID += '_connectable'; - } - - return new validationIssue({ - type: type, - subtype: subtype, - severity: 'warning', - message: function(context) { - var graph = context.graph(); - var entity1 = graph.hasEntity(this.entityIds[0]), - entity2 = graph.hasEntity(this.entityIds[1]); - return (entity1 && entity2) ? _t('issues.crossing_ways.message', { - feature: utilDisplayLabel(entity1, graph), - feature2: utilDisplayLabel(entity2, graph) - }) : ''; - }, - reference: showReference, - entityIds: entities.map(function(entity) { - return entity.id; - }), - data: { - edges: edges, - featureTypes: featureTypes, - connectionTags: connectionTags - }, - // differentiate based on the loc since two ways can cross multiple times - hash: crossing.crossPoint.toString() + - // if the edges change then so does the fix - edges.slice().sort(function(edge1, edge2) { - // order to assure hash is deterministic - return edge1[0] < edge2[0] ? -1 : 1; - }).toString() + - // ensure the correct connection tags are added in the fix - JSON.stringify(connectionTags), - loc: crossing.crossPoint, - dynamicFixes: function(context) { - var mode = context.mode(); - if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return []; - - var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1; - var selectedFeatureType = this.data.featureTypes[selectedIndex]; - var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0]; - - var fixes = []; - - if (connectionTags) { - fixes.push(makeConnectWaysFix(this.data.connectionTags)); - } - - if (isCrossingIndoors) { - fixes.push(new validationIssueFix({ - icon: 'iD-icon-layers', - title: _t('issues.fix.use_different_levels.title') - })); - } else if (isCrossingTunnels || - isCrossingBridges || - featureType1 === 'building' || - featureType2 === 'building') { - - fixes.push(makeChangeLayerFix('higher')); - fixes.push(makeChangeLayerFix('lower')); - - // can only add bridge/tunnel if both features are lines - } else if (context.graph().geometry(this.entityIds[0]) === 'line' && - context.graph().geometry(this.entityIds[1]) === 'line') { - - // don't recommend adding bridges to waterways since they're uncommmon - if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') { - fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge')); - } - - // don't recommend adding tunnels under waterways since they're uncommmon - var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway'; - if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) { - fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel')); - } - } - - // repositioning the features is always an option - fixes.push(new validationIssueFix({ - icon: 'iD-operation-move', - title: _t('issues.fix.reposition_features.title') - })); - - return fixes; - } - }); - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference')); - } - } - - function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){ - return new validationIssueFix({ - icon: iconName, - title: _t('issues.fix.' + fixTitleID + '.title'), - onClick: function(context) { - var mode = context.mode(); - if (!mode || mode.id !== 'select') return; - - var selectedIDs = mode.selectedIDs(); - if (selectedIDs.length !== 1) return; - - var selectedWayID = selectedIDs[0]; - if (!context.hasEntity(selectedWayID)) return; - - var resultWayIDs = [selectedWayID]; - - var edge, crossedEdge, crossedWayID; - if (this.issue.entityIds[0] === selectedWayID) { - edge = this.issue.data.edges[0]; - crossedEdge = this.issue.data.edges[1]; - crossedWayID = this.issue.entityIds[1]; - } else { - edge = this.issue.data.edges[1]; - crossedEdge = this.issue.data.edges[0]; - crossedWayID = this.issue.entityIds[0]; - } - - var crossingLoc = this.issue.loc; - - var projection = context.projection; - - var action = function actionAddStructure(graph) { - - var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])]; - - var crossedWay = graph.hasEntity(crossedWayID); - // use the explicit width of the crossed feature as the structure length, if available - var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width); - if (!structLengthMeters) { - // if no explicit width is set, approximate the width based on the tags - structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters(); - } - if (structLengthMeters) { - if (getFeatureType(crossedWay, graph) === 'railway') { - // bridges over railways are generally much longer than the rail bed itself, compensate - structLengthMeters *= 2; - } - } else { - // should ideally never land here since all rail/water/road tags should have an implied width - structLengthMeters = 8; - } - - var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI; - var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI; - var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2); - if (crossingAngle > Math.PI) crossingAngle -= Math.PI; - // lengthen the structure to account for the angle of the crossing - structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2; - - // add padding since the structure must extend past the edges of the crossed feature - structLengthMeters += 4; - - // clamp the length to a reasonable range - structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50); - - function geomToProj(geoPoint) { - return [ - geoLonToMeters(geoPoint[0], geoPoint[1]), - geoLatToMeters(geoPoint[1]) - ]; - } - function projToGeom(projPoint) { - var lat = geoMetersToLat(projPoint[1]); - return [ - geoMetersToLon(projPoint[0], lat), - lat - ]; - } - - var projEdgeNode1 = geomToProj(edgeNodes[0].loc); - var projEdgeNode2 = geomToProj(edgeNodes[1].loc); - - var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2); - - var projectedCrossingLoc = geomToProj(crossingLoc); - var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / - geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc); - - function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) { - var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio; - return projToGeom([ - projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, - projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters - ]); - } - - var endpointLocGetter1 = function(lengthMeters) { - return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters); - }; - var endpointLocGetter2 = function(lengthMeters) { - return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters); - }; - - // avoid creating very short edges from splitting too close to another node - var minEdgeLengthMeters = 0.55; - - // decide where to bound the structure along the way, splitting as necessary - function determineEndpoint(edge, endNode, locGetter) { - var newNode; - - var idealLengthMeters = structLengthMeters / 2; - - // distance between the crossing location and the end of the edge, - // the maximum length of this side of the structure - var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc); - - if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) { - // the edge is long enough to insert a new node - - // the loc that would result in the full expected length - var idealNodeLoc = locGetter(idealLengthMeters); - - newNode = osmNode(); - graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph); - - } else { - var edgeCount = 0; - endNode.parentIntersectionWays(graph).forEach(function(way) { - way.nodes.forEach(function(nodeID) { - if (nodeID === endNode.id) { - if ((endNode.id === way.first() && endNode.id !== way.last()) || - (endNode.id === way.last() && endNode.id !== way.first())) { - edgeCount += 1; - } else { - edgeCount += 2; - } - } - }); - }); - - if (edgeCount >= 3) { - // the end node is a junction, try to leave a segment - // between it and the structure - #7202 - - var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters; - if (insetLength > minEdgeLengthMeters) { - var insetNodeLoc = locGetter(insetLength); - newNode = osmNode(); - graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph); - } - } - } - - // if the edge is too short to subdivide as desired, then - // just bound the structure at the existing end node - if (!newNode) newNode = endNode; - - var splitAction = actionSplit(newNode.id) - .limitWays(resultWayIDs); // only split selected or created ways - - // do the split - graph = splitAction(graph); - if (splitAction.getCreatedWayIDs().length) { - resultWayIDs.push(splitAction.getCreatedWayIDs()[0]); - } - - return newNode; - } - - var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1); - var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2); - - var structureWay = resultWayIDs.map(function(id) { - return graph.entity(id); - }).find(function(way) { - return way.nodes.indexOf(structEndNode1.id) !== -1 && - way.nodes.indexOf(structEndNode2.id) !== -1; - }); - - var tags = Object.assign({}, structureWay.tags); // copy tags - if (bridgeOrTunnel === 'bridge'){ - tags.bridge = 'yes'; - tags.layer = '1'; - } else { - var tunnelValue = 'yes'; - if (getFeatureType(structureWay, graph) === 'waterway') { - // use `tunnel=culvert` for waterways by default - tunnelValue = 'culvert'; - } - tags.tunnel = tunnelValue; - tags.layer = '-1'; - } - // apply the structure tags to the way - graph = actionChangeTags(structureWay.id, tags)(graph); - return graph; - }; - - context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation')); - context.enter(modeSelect(context, resultWayIDs)); - } - }); - } - - function makeConnectWaysFix(connectionTags) { - - var fixTitleID = 'connect_features'; - if (connectionTags.ford) { - fixTitleID = 'connect_using_ford'; - } - - return new validationIssueFix({ - icon: 'iD-icon-crossing', - title: _t('issues.fix.' + fixTitleID + '.title'), - onClick: function(context) { - var loc = this.issue.loc; - var connectionTags = this.issue.data.connectionTags; - var edges = this.issue.data.edges; - - context.perform( - function actionConnectCrossingWays(graph) { - // create the new node for the points - var node = osmNode({ loc: loc, tags: connectionTags }); - graph = graph.replace(node); - - var nodesToMerge = [node.id]; - var mergeThresholdInMeters = 0.75; - - edges.forEach(function(edge) { - var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])]; - var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc); - // if there is already a point nearby, use that - if (closestNodeInfo.distance < mergeThresholdInMeters) { - nodesToMerge.push(closestNodeInfo.node.id); - // else add the new node to the way - } else { - graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph); - } - }); - - if (nodesToMerge.length > 1) { - // if we're using nearby nodes, merge them with the new node - graph = actionMergeNodes(nodesToMerge, loc)(graph); - } - - return graph; - }, - _t('issues.fix.connect_crossing_features.annotation') - ); - } - }); - } - - function makeChangeLayerFix(higherOrLower) { - return new validationIssueFix({ - icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'), - title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'), - onClick: function(context) { - - var mode = context.mode(); - if (!mode || mode.id !== 'select') return; - - var selectedIDs = mode.selectedIDs(); - if (selectedIDs.length !== 1) return; - - var selectedID = selectedIDs[0]; - if (!this.issue.entityIds.some(function(entityId) { - return entityId === selectedID; - })) return; - - var entity = context.hasEntity(selectedID); - if (!entity) return; - - var tags = Object.assign({}, entity.tags); // shallow copy - var layer = tags.layer && Number(tags.layer); - if (layer && !isNaN(layer)) { - if (higherOrLower === 'higher') { - layer += 1; - } else { - layer -= 1; - } - } else { - if (higherOrLower === 'higher') { - layer = 1; - } else { - layer = -1; - } - } - tags.layer = layer.toString(); - context.perform( - actionChangeTags(entity.id, tags), - _t('operations.change_tags.annotation') - ); - } - }); - } - - validation.type = type; - - return validation; - } - - function validationDisconnectedWay() { - var type = 'disconnected_way'; - - function isTaggedAsHighway(entity) { - return osmRoutableHighwayTagValues[entity.tags.highway]; - } - - var validation = function checkDisconnectedWay(entity, graph) { - - var routingIslandWays = routingIslandForEntity(entity); - if (!routingIslandWays) return []; - - return [new validationIssue({ - type: type, - subtype: 'highway', - severity: 'warning', - message: function(context) { - if (this.entityIds.length === 1) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : ''; - } - return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() }); - }, - reference: showReference, - entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }), - dynamicFixes: makeFixes - })]; - - - function makeFixes(context) { - - var fixes = []; - - var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]); - - if (singleEntity) { - - if (singleEntity.type === 'way' && !singleEntity.isClosed()) { - - var textDirection = _mainLocalizer.textDirection(); - - var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start'); - if (startFix) fixes.push(startFix); - - var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end'); - if (endFix) fixes.push(endFix); - } - if (!fixes.length) { - fixes.push(new validationIssueFix({ - title: _t('issues.fix.connect_feature.title') - })); - } - - fixes.push(new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.delete_feature.title'), - entityIds: [singleEntity.id], - onClick: function(context) { - var id = this.issue.entityIds[0]; - var operation = operationDelete(context, [id]); - if (!operation.disabled()) { - operation(); - } - } - })); - } else { - fixes.push(new validationIssueFix({ - title: _t('issues.fix.connect_features.title') - })); - } - - return fixes; - } - - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.disconnected_way.routable.reference')); - } - - function routingIslandForEntity(entity) { - - var routingIsland = new Set(); // the interconnected routable features - var waysToCheck = []; // the queue of remaining routable ways to traverse - - function queueParentWays(node) { - graph.parentWays(node).forEach(function(parentWay) { - if (!routingIsland.has(parentWay) && // only check each feature once - isRoutableWay(parentWay, false)) { // only check routable features - routingIsland.add(parentWay); - waysToCheck.push(parentWay); - } - }); - } - - if (entity.type === 'way' && isRoutableWay(entity, true)) { - - routingIsland.add(entity); - waysToCheck.push(entity); - - } else if (entity.type === 'node' && isRoutableNode(entity)) { - - routingIsland.add(entity); - queueParentWays(entity); - - } else { - // this feature isn't routable, cannot be a routing island - return null; - } - - while (waysToCheck.length) { - var wayToCheck = waysToCheck.pop(); - var childNodes = graph.childNodes(wayToCheck); - for (var i in childNodes) { - var vertex = childNodes[i]; - - if (isConnectedVertex(vertex)) { - // found a link to the wider network, not a routing island - return null; - } - - if (isRoutableNode(vertex)) { - routingIsland.add(vertex); - } - - queueParentWays(vertex); - } - } - - // no network link found, this is a routing island, return its members - return routingIsland; - } - - function isConnectedVertex(vertex) { - // assume ways overlapping unloaded tiles are connected to the wider road network - #5938 - var osm = services.osm; - if (osm && !osm.isDataLoaded(vertex.loc)) return true; - - // entrances are considered connected - if (vertex.tags.entrance && - vertex.tags.entrance !== 'no') return true; - if (vertex.tags.amenity === 'parking_entrance') return true; - - return false; - } - - function isRoutableNode(node) { - // treat elevators as distinct features in the highway network - if (node.tags.highway === 'elevator') return true; - return false; - } - - function isRoutableWay(way, ignoreInnerWays) { - if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true; - - return graph.parentRelations(way).some(function(parentRelation) { - if (parentRelation.tags.type === 'route' && - parentRelation.tags.route === 'ferry') return true; - - if (parentRelation.isMultipolygon() && - isTaggedAsHighway(parentRelation) && - (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true; - }); - } - - function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) { - var vertex = graph.hasEntity(vertexID); - if (!vertex || vertex.tags.noexit === 'yes') return null; - - var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') || - (whichEnd === 'end' && textDirection === 'rtl'); - - return new validationIssueFix({ - icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''), - title: _t('issues.fix.continue_from_' + whichEnd + '.title'), - entityIds: [vertexID], - onClick: function(context) { - var wayId = this.issue.entityIds[0]; - var way = context.hasEntity(wayId); - var vertexId = this.entityIds[0]; - var vertex = context.hasEntity(vertexId); - - if (!way || !vertex) return; - - // make sure the vertex is actually visible and editable - var map = context.map(); - if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) { - map.zoomToEase(vertex); - } - - context.enter( - modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true) - ); - } - }); - } - - }; - - validation.type = type; - - return validation; - } - - function validationFormatting() { - var type = 'invalid_format'; - - var validation = function(entity) { - var issues = []; - - function isValidEmail(email) { - // Emails in OSM are going to be official so they should be pretty simple - // Using negated lists to better support all possible unicode characters (#6494) - var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; - - // An empty value is also acceptable - return (!email || valid_email.test(email)); - } - /* - function isSchemePresent(url) { - var valid_scheme = /^https?:\/\//i; - return (!url || valid_scheme.test(url)); - } - */ - function showReferenceEmail(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.invalid_format.email.reference')); - } - /* - function showReferenceWebsite(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(t('issues.invalid_format.website.reference')); - } - - if (entity.tags.website) { - // Multiple websites are possible - // If ever we support ES6, arrow functions make this nicer - var websites = entity.tags.website - .split(';') - .map(function(s) { return s.trim(); }) - .filter(function(x) { return !isSchemePresent(x); }); - - if (websites.length) { - issues.push(new validationIssue({ - type: type, - subtype: 'website', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? t('issues.invalid_format.website.message' + this.data, - { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : ''; - }, - reference: showReferenceWebsite, - entityIds: [entity.id], - hash: websites.join(), - data: (websites.length > 1) ? '_multi' : '' - })); - } - } - */ - if (entity.tags.email) { - // Multiple emails are possible - var emails = entity.tags.email - .split(';') - .map(function(s) { return s.trim(); }) - .filter(function(x) { return !isValidEmail(x); }); - - if (emails.length) { - issues.push(new validationIssue({ - type: type, - subtype: 'email', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.invalid_format.email.message' + this.data, - { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : ''; - }, - reference: showReferenceEmail, - entityIds: [entity.id], - hash: emails.join(), - data: (emails.length > 1) ? '_multi' : '' - })); - } - } - - return issues; - }; - - validation.type = type; - - return validation; - } - - function validationHelpRequest(context) { - var type = 'help_request'; - - var validation = function checkFixmeTag(entity) { - - if (!entity.tags.fixme) return []; - - // don't flag fixmes on features added by the user - if (entity.version === undefined) return []; - - if (entity.v !== undefined) { - var baseEntity = context.history().base().hasEntity(entity.id); - // don't flag fixmes added by the user on existing features - if (!baseEntity || !baseEntity.tags.fixme) return []; - } - - return [new validationIssue({ - type: type, - subtype: 'fixme_tag', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : ''; - }, - dynamicFixes: function() { - return [ - new validationIssueFix({ - title: _t('issues.fix.address_the_concern.title') - }) - ]; - }, - reference: showReference, - entityIds: [entity.id] - })]; - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.fixme_tag.reference')); - } - }; - - validation.type = type; - - return validation; - } - - function validationImpossibleOneway() { - var type = 'impossible_oneway'; - - var validation = function checkImpossibleOneway(entity, graph) { - - if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return []; - - if (entity.isClosed()) return []; - - if (!typeForWay(entity)) return []; - - if (!isOneway(entity)) return []; - - var firstIssues = issuesForNode(entity, entity.first()); - var lastIssues = issuesForNode(entity, entity.last()); - - return firstIssues.concat(lastIssues); - - function typeForWay(way) { - if (way.geometry(graph) !== 'line') return null; - - if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway'; - if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway'; - return null; - } - - function isOneway(way) { - if (way.tags.oneway === 'yes') return true; - if (way.tags.oneway) return false; - - for (var key in way.tags) { - if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) { - return true; - } - } - return false; - } - - function nodeOccursMoreThanOnce(way, nodeID) { - var occurences = 0; - for (var index in way.nodes) { - if (way.nodes[index] === nodeID) { - occurences += 1; - if (occurences > 1) return true; - } - } - return false; - } - - function isConnectedViaOtherTypes(way, node) { - - var wayType = typeForWay(way); - - if (wayType === 'highway') { - // entrances are considered connected - if (node.tags.entrance && node.tags.entrance !== 'no') return true; - if (node.tags.amenity === 'parking_entrance') return true; - } else if (wayType === 'waterway') { - if (node.id === way.first()) { - // multiple waterways may start at the same spring - if (node.tags.natural === 'spring') return true; - } else { - // multiple waterways may end at the same drain - if (node.tags.manhole === 'drain') return true; - } - } - - return graph.parentWays(node).some(function(parentWay) { - if (parentWay.id === way.id) return false; - - if (wayType === 'highway') { - - // allow connections to highway areas - if (parentWay.geometry(graph) === 'area' && - osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; - - // count connections to ferry routes as connected - if (parentWay.tags.route === 'ferry') return true; - - return graph.parentRelations(parentWay).some(function(parentRelation) { - if (parentRelation.tags.type === 'route' && - parentRelation.tags.route === 'ferry') return true; - - // allow connections to highway multipolygons - return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway]; - }); - } else if (wayType === 'waterway') { - // multiple waterways may start or end at a water body at the same node - if (parentWay.tags.natural === 'water' || - parentWay.tags.natural === 'coastline') return true; - } - return false; - }); - } - - function issuesForNode(way, nodeID) { - - var isFirst = nodeID === way.first(); - - var wayType = typeForWay(way); - - // ignore if this way is self-connected at this node - if (nodeOccursMoreThanOnce(way, nodeID)) return []; - - var osm = services.osm; - if (!osm) return []; - - var node = graph.hasEntity(nodeID); - - // ignore if this node or its tile are unloaded - if (!node || !osm.isDataLoaded(node.loc)) return []; - - if (isConnectedViaOtherTypes(way, node)) return []; - - var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) { - if (parentWay.id === way.id) return false; - return typeForWay(parentWay) === wayType; - }); - - // assume it's okay for waterways to start or end disconnected for now - if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return []; - - var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) { - return isOneway(attachedWay); - }); - - // ignore if the way is connected to some non-oneway features - if (attachedOneways.length < attachedWaysOfSameType.length) return []; - - if (attachedOneways.length) { - var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) { - if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true; - if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true; - return false; - }); - if (connectedEndpointsOkay) return []; - } - - var placement = isFirst ? 'start' : 'end', - messageID = wayType + '.', - referenceID = wayType + '.'; - - if (wayType === 'waterway') { - messageID += 'connected.' + placement; - referenceID += 'connected'; - } else { - messageID += placement; - referenceID += placement; - } - - return [new validationIssue({ - type: type, - subtype: wayType, - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.impossible_oneway.' + messageID + '.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: getReference(referenceID), - entityIds: [way.id, node.id], - dynamicFixes: function() { - - var fixes = []; - - if (attachedOneways.length) { - fixes.push(new validationIssueFix({ - icon: 'iD-operation-reverse', - title: _t('issues.fix.reverse_feature.title'), - entityIds: [way.id], - onClick: function(context) { - var id = this.issue.entityIds[0]; - context.perform(actionReverse(id), _t('operations.reverse.annotation')); - } - })); - } - if (node.tags.noexit !== 'yes') { - var textDirection = _mainLocalizer.textDirection(); - var useLeftContinue = (isFirst && textDirection === 'ltr') || - (!isFirst && textDirection === 'rtl'); - fixes.push(new validationIssueFix({ - icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''), - title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'), - onClick: function(context) { - var entityID = this.issue.entityIds[0]; - var vertexID = this.issue.entityIds[1]; - var way = context.entity(entityID); - var vertex = context.entity(vertexID); - continueDrawing(way, vertex, context); - } - })); - } - - return fixes; - }, - loc: node.loc - })]; - - function getReference(referenceID) { - return function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.impossible_oneway.' + referenceID + '.reference')); - }; - } - } - }; - - function continueDrawing(way, vertex, context) { - // make sure the vertex is actually visible and editable - var map = context.map(); - if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) { - map.zoomToEase(vertex); - } - - context.enter( - modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true) - ); - } - - validation.type = type; - - return validation; - } - - function validationIncompatibleSource() { - var type = 'incompatible_source'; - var invalidSources = [ - { - id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive' - } - ]; - - var validation = function checkIncompatibleSource(entity) { - - var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';'); - - if (!entitySources) return []; - - var issues = []; - - invalidSources.forEach(function(invalidSource) { - - var hasInvalidSource = entitySources.some(function(source) { - if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false; - if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false; - return true; - }); - - if (!hasInvalidSource) return; - - issues.push(new validationIssue({ - type: type, - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: getReference(invalidSource.id), - entityIds: [entity.id], - dynamicFixes: function() { - return [ - new validationIssueFix({ - title: _t('issues.fix.remove_proprietary_data.title') - }) - ]; - } - })); - }); - - return issues; - - - function getReference(id) { - return function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.incompatible_source.' + id + '.reference')); - }; - } - }; - - validation.type = type; - - return validation; - } - - function validationMaprules() { - var type = 'maprules'; - - var validation = function checkMaprules(entity, graph) { - if (!services.maprules) return []; - - var rules = services.maprules.validationRules(); - var issues = []; - - for (var i = 0; i < rules.length; i++) { - var rule = rules[i]; - rule.findIssues(entity, graph, issues); - } - - return issues; - }; - - - validation.type = type; - - return validation; - } - - function validationMismatchedGeometry() { - var type = 'mismatched_geometry'; - - function tagSuggestingLineIsArea(entity) { - if (entity.type !== 'way' || entity.isClosed()) return null; - - var tagSuggestingArea = entity.tagSuggestingArea(); - if (!tagSuggestingArea) { - return null; - } - - var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line'); - var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area'); - if (asLine && asArea && (asLine === asArea)) { - // these tags also allow lines and making this an area wouldn't matter - return null; - } - - return tagSuggestingArea; - } - - - function makeConnectEndpointsFixOnClick(way, graph) { - // must have at least three nodes to close this automatically - if (way.nodes.length < 3) return null; - - var nodes = graph.childNodes(way), testNodes; - var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc); - - // if the distance is very small, attempt to merge the endpoints - if (firstToLastDistanceMeters < 0.75) { - testNodes = nodes.slice(); // shallow copy - testNodes.pop(); - testNodes.push(testNodes[0]); - // make sure this will not create a self-intersection - if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) { - return function(context) { - var way = context.entity(this.issue.entityIds[0]); - context.perform( - actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc), - _t('issues.fix.connect_endpoints.annotation') - ); - }; - } - } - - // if the points were not merged, attempt to close the way - testNodes = nodes.slice(); // shallow copy - testNodes.push(testNodes[0]); - // make sure this will not create a self-intersection - if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) { - return function(context) { - var wayId = this.issue.entityIds[0]; - var way = context.entity(wayId); - var nodeId = way.nodes[0]; - var index = way.nodes.length; - context.perform( - actionAddVertex(wayId, nodeId, index), - _t('issues.fix.connect_endpoints.annotation') - ); - }; - } - } - - function lineTaggedAsAreaIssue(entity) { - - var tagSuggestingArea = tagSuggestingLineIsArea(entity); - if (!tagSuggestingArea) return null; - - return new validationIssue({ - type: type, - subtype: 'area_as_line', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.tag_suggests_area.message', { - feature: utilDisplayLabel(entity, context.graph()), - tag: utilTagText({ tags: tagSuggestingArea }) - }) : ''; - }, - reference: showReference, - entityIds: [entity.id], - hash: JSON.stringify(tagSuggestingArea), - dynamicFixes: function(context) { - - var fixes = []; - - var entity = context.entity(this.entityIds[0]); - var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph()); - - fixes.push(new validationIssueFix({ - title: _t('issues.fix.connect_endpoints.title'), - onClick: connectEndsOnClick - })); - - fixes.push(new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.remove_tag.title'), - onClick: function(context) { - var entityId = this.issue.entityIds[0]; - var entity = context.entity(entityId); - var tags = Object.assign({}, entity.tags); // shallow copy - for (var key in tagSuggestingArea) { - delete tags[key]; - } - context.perform( - actionChangeTags(entityId, tags), - _t('issues.fix.remove_tag.annotation') - ); - } - })); - - return fixes; - } - }); - - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.tag_suggests_area.reference')); - } - } - - function vertexTaggedAsPointIssue(entity, graph) { - // we only care about nodes - if (entity.type !== 'node') return null; - - // ignore tagless points - if (Object.keys(entity.tags).length === 0) return null; - - // address lines are special so just ignore them - if (entity.isOnAddressLine(graph)) return null; - - var geometry = entity.geometry(graph); - var allowedGeometries = osmNodeGeometriesForTags(entity.tags); - - if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) { - - return new validationIssue({ - type: type, - subtype: 'vertex_as_point', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.vertex_as_point.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.vertex_as_point.reference')); - }, - entityIds: [entity.id] - }); - - } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) { - - return new validationIssue({ - type: type, - subtype: 'point_as_vertex', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.point_as_vertex.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.point_as_vertex.reference')); - }, - entityIds: [entity.id], - dynamicFixes: function(context) { - - var entityId = this.entityIds[0]; - - var extractOnClick = null; - if (!context.hasHiddenConnections(entityId)) { - - extractOnClick = function(context) { - var entityId = this.issue.entityIds[0]; - var action = actionExtract(entityId); - context.perform( - action, - _t('operations.extract.annotation.single') - ); - // re-enter mode to trigger updates - context.enter(modeSelect(context, [action.getExtractedNodeID()])); - }; - } - - return [ - new validationIssueFix({ - icon: 'iD-operation-extract', - title: _t('issues.fix.extract_point.title'), - onClick: extractOnClick - }) - ]; - } - }); - } - - return null; - } - - function unclosedMultipolygonPartIssues(entity, graph) { - - if (entity.type !== 'relation' || - !entity.isMultipolygon() || - entity.isDegenerate() || - // cannot determine issues for incompletely-downloaded relations - !entity.isComplete(graph)) return null; - - var sequences = osmJoinWays(entity.members, graph); - - var issues = []; - - for (var i in sequences) { - var sequence = sequences[i]; - - if (!sequence.nodes) continue; - - var firstNode = sequence.nodes[0]; - var lastNode = sequence.nodes[sequence.nodes.length - 1]; - - // part is closed if the first and last nodes are the same - if (firstNode === lastNode) continue; - - var issue = new validationIssue({ - type: type, - subtype: 'unclosed_multipolygon_part', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.unclosed_multipolygon_part.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: showReference, - loc: sequence.nodes[0].loc, - entityIds: [entity.id], - hash: sequence.map(function(way) { - return way.id; - }).join() - }); - issues.push(issue); - } - - return issues; - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.unclosed_multipolygon_part.reference')); - } - } - - var validation = function checkMismatchedGeometry(entity, graph) { - var issues = [ - vertexTaggedAsPointIssue(entity, graph), - lineTaggedAsAreaIssue(entity) - ]; - issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph)); - return issues.filter(Boolean); - }; - - validation.type = type; - - return validation; - } - - function validationMissingRole() { - var type = 'missing_role'; - - var validation = function checkMissingRole(entity, graph) { - var issues = []; - if (entity.type === 'way') { - graph.parentRelations(entity).forEach(function(relation) { - if (!relation.isMultipolygon()) return; - - var member = relation.memberById(entity.id); - if (member && isMissingRole(member)) { - issues.push(makeIssue(entity, relation, member)); - } - }); - } else if (entity.type === 'relation' && entity.isMultipolygon()) { - entity.indexedMembers().forEach(function(member) { - var way = graph.hasEntity(member.id); - if (way && isMissingRole(member)) { - issues.push(makeIssue(way, entity, member)); - } - }); - } - - return issues; - }; - - - function isMissingRole(member) { - return !member.role || !member.role.trim().length; - } - - - function makeIssue(way, relation, member) { - return new validationIssue({ - type: type, - severity: 'warning', - message: function(context) { - var member = context.hasEntity(this.entityIds[1]), - relation = context.hasEntity(this.entityIds[0]); - return (member && relation) ? _t('issues.missing_role.message', { - member: utilDisplayLabel(member, context.graph()), - relation: utilDisplayLabel(relation, context.graph()) - }) : ''; - }, - reference: showReference, - entityIds: [relation.id, way.id], - data: { - member: member - }, - hash: member.index.toString(), - dynamicFixes: function() { - return [ - makeAddRoleFix('inner'), - makeAddRoleFix('outer'), - new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.remove_from_relation.title'), - onClick: function(context) { - context.perform( - actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), - _t('operations.delete_member.annotation') - ); - } - }) - ]; - } - }); - - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.missing_role.multipolygon.reference')); - } - } - - - function makeAddRoleFix(role) { - return new validationIssueFix({ - title: _t('issues.fix.set_as_' + role + '.title'), - onClick: function(context) { - var oldMember = this.issue.data.member; - var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role }; - context.perform( - actionChangeMember(this.issue.entityIds[0], member, oldMember.index), - _t('operations.change_role.annotation') - ); - } - }); - } - - validation.type = type; - - return validation; - } - - function validationMissingTag(context) { - var type = 'missing_tag'; - - function hasDescriptiveTags(entity, graph) { - var keys = Object.keys(entity.tags) - .filter(function(k) { - if (k === 'area' || k === 'name') { - return false; - } else { - return osmIsInterestingTag(k); - } - }); - - if (entity.type === 'relation' && - keys.length === 1 && - entity.tags.type === 'multipolygon') { - // this relation's only interesting tag just says its a multipolygon, - // which is not descriptive enough - - // It's okay for a simple multipolygon to have no descriptive tags - // if its outer way has them (old model, see `outdated_tags.js`) - return osmOldMultipolygonOuterMemberOfRelation(entity, graph); - } - - return keys.length > 0; - } - - function isUnknownRoad(entity) { - return entity.type === 'way' && entity.tags.highway === 'road'; - } - - function isUntypedRelation(entity) { - return entity.type === 'relation' && !entity.tags.type; - } - - var validation = function checkMissingTag(entity, graph) { - - var subtype; - - var osm = context.connection(); - var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc); - - // we can't know if the node is a vertex if the tile is undownloaded - if (!isUnloadedNode && - // allow untagged nodes that are part of ways - entity.geometry(graph) !== 'vertex' && - // allow untagged entities that are part of relations - !entity.hasParentRelations(graph)) { - - if (Object.keys(entity.tags).length === 0) { - subtype = 'any'; - } else if (!hasDescriptiveTags(entity, graph)) { - subtype = 'descriptive'; - } else if (isUntypedRelation(entity)) { - subtype = 'relation_type'; - } - } - - // flag an unknown road even if it's a member of a relation - if (!subtype && isUnknownRoad(entity)) { - subtype = 'highway_classification'; - } - - if (!subtype) return []; - - var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype; - var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; - - // can always delete if the user created it in the first place.. - var canDelete = (entity.version === undefined || entity.v !== undefined); - var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning'; - - return [new validationIssue({ - type: type, - subtype: subtype, - severity: severity, - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.' + messageID + '.message', { - feature: utilDisplayLabel(entity, context.graph()) - }) : ''; - }, - reference: showReference, - entityIds: [entity.id], - dynamicFixes: function(context) { - - var fixes = []; - - var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset'; - - fixes.push(new validationIssueFix({ - icon: 'iD-icon-search', - title: _t('issues.fix.' + selectFixType + '.title'), - onClick: function(context) { - context.ui().sidebar.showPresetList(); - } - })); - - var deleteOnClick; - - var id = this.entityIds[0]; - var operation = operationDelete(context, [id]); - var disabledReasonID = operation.disabled(); - if (!disabledReasonID) { - deleteOnClick = function(context) { - var id = this.issue.entityIds[0]; - var operation = operationDelete(context, [id]); - if (!operation.disabled()) { - operation(); - } - }; - } - - fixes.push( - new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.delete_feature.title'), - disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined, - onClick: deleteOnClick - }) - ); - - return fixes; - } - })]; - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.' + referenceID + '.reference')); - } - }; - - validation.type = type; - - return validation; - } - - // remove spaces, punctuation, diacritics - var simplify = (str) => { - return diacritics.remove( - str - .replace(/&/g, 'and') - .replace(/[\s\-=_!"#%'*{},.\/:;?\(\)\[\]@\\$\^*+<>~`’\u00a1\u00a7\u00b6\u00b7\u00bf\u037e\u0387\u055a-\u055f\u0589\u05c0\u05c3\u05c6\u05f3\u05f4\u0609\u060a\u060c\u060d\u061b\u061e\u061f\u066a-\u066d\u06d4\u0700-\u070d\u07f7-\u07f9\u0830-\u083e\u085e\u0964\u0965\u0970\u0af0\u0df4\u0e4f\u0e5a\u0e5b\u0f04-\u0f12\u0f14\u0f85\u0fd0-\u0fd4\u0fd9\u0fda\u104a-\u104f\u10fb\u1360-\u1368\u166d\u166e\u16eb-\u16ed\u1735\u1736\u17d4-\u17d6\u17d8-\u17da\u1800-\u1805\u1807-\u180a\u1944\u1945\u1a1e\u1a1f\u1aa0-\u1aa6\u1aa8-\u1aad\u1b5a-\u1b60\u1bfc-\u1bff\u1c3b-\u1c3f\u1c7e\u1c7f\u1cc0-\u1cc7\u1cd3\u2016\u2017\u2020-\u2027\u2030-\u2038\u203b-\u203e\u2041-\u2043\u2047-\u2051\u2053\u2055-\u205e\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00\u2e01\u2e06-\u2e08\u2e0b\u2e0e-\u2e16\u2e18\u2e19\u2e1b\u2e1e\u2e1f\u2e2a-\u2e2e\u2e30-\u2e39\u3001-\u3003\u303d\u30fb\ua4fe\ua4ff\ua60d-\ua60f\ua673\ua67e\ua6f2-\ua6f7\ua874-\ua877\ua8ce\ua8cf\ua8f8-\ua8fa\ua92e\ua92f\ua95f\ua9c1-\ua9cd\ua9de\ua9df\uaa5c-\uaa5f\uaade\uaadf\uaaf0\uaaf1\uabeb\ufe10-\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49-\ufe4c\ufe50-\ufe52\ufe54-\ufe57\ufe5f-\ufe61\ufe68\ufe6a\ufe6b\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g,'') - .toLowerCase() - ); - }; - - // toParts - split a name-suggestion-index key into parts - // { - // kvnd: "amenity/fast_food|Thaï Express~(North America)", - // kvn: "amenity/fast_food|Thaï Express", - // kv: "amenity/fast_food", - // k: "amenity", - // v: "fast_food", - // n: "Thaï Express", - // d: "(North America)", - // nsimple: "thaiexpress", - // kvnnsimple: "amenity/fast_food|thaiexpress" - // } - var to_parts = (kvnd) => { - const parts = {}; - parts.kvnd = kvnd; - - const kvndparts = kvnd.split('~', 2); - if (kvndparts.length > 1) parts.d = kvndparts[1]; - - parts.kvn = kvndparts[0]; - const kvnparts = parts.kvn.split('|', 2); - if (kvnparts.length > 1) parts.n = kvnparts[1]; - - parts.kv = kvnparts[0]; - const kvparts = parts.kv.split('/', 2); - parts.k = kvparts[0]; - parts.v = kvparts[1]; - - parts.nsimple = simplify(parts.n); - parts.kvnsimple = parts.kv + '|' + parts.nsimple; - return parts; - }; - - var matchGroups = {adult_gaming_centre:["amenity/casino","amenity/gambling","leisure/adult_gaming_centre"],beauty:["shop/beauty","shop/hairdresser_supply"],bed:["shop/bed","shop/furniture"],beverages:["shop/alcohol","shop/beverages"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fashion:["shop/accessories","shop/bag","shop/botique","shop/clothes","shop/department_store","shop/fashion","shop/fashion_accessories","shop/sports","shop/shoes"],financial:["amenity/bank","office/accountant","office/financial","office/financial_advisor","office/tax_advisor","shop/tax"],fitness:["leisure/fitness_centre","leisure/fitness_center","leisure/sports_centre","leisure/sports_center"],food:["amenity/cafe","amenity/fast_food","amenity/ice_cream","amenity/restaurant","shop/bakery","shop/ice_cream","shop/pastry","shop/tea","shop/coffee"],fuel:["amenity/fuel","shop/gas","shop/convenience;gas","shop/gas;convenience"],gift:["shop/gift","shop/card","shop/cards","shop/stationery"],hardware:["shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],houseware:["shop/houseware","shop/interior_decoration"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],outdoor:["shop/outdoor","shop/sports"],rental:["amenity/bicycle_rental","amenity/boat_rental","amenity/car_rental","amenity/truck_rental","amenity/vehicle_rental","shop/rental"],school:["amenity/childcare","amenity/college","amenity/kindergarten","amenity/language_school","amenity/prep_school","amenity/school","amenity/university"],supermarket:["shop/food","shop/frozen_food","shop/greengrocer","shop/grocery","shop/supermarket","shop/wholesale"],variety_store:["shop/variety_store","shop/supermarket","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]}; - var match_groups = { - matchGroups: matchGroups - }; - - var match_groups$1 = /*#__PURE__*/Object.freeze({ - __proto__: null, - matchGroups: matchGroups, - 'default': match_groups - }); - - var require$$0 = getCjsExportFromNamespace(match_groups$1); - - const matchGroups$1 = require$$0.matchGroups; - - - var matcher$1 = () => { - let _warnings = []; // array of match conflict pairs - let _ambiguous = {}; - let _matchIndex = {}; - let matcher = {}; - - - // Create an index of all the keys/simplenames for fast matching - matcher.buildMatchIndex = (brands) => { - // two passes - once for primary names, once for secondary/alternate names - Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'primary')); - Object.keys(brands).forEach(kvnd => insertNames(kvnd, 'secondary')); - - - function insertNames(kvnd, which) { - const obj = brands[kvnd]; - const parts = to_parts(kvnd); - - // Exit early for ambiguous names in the second pass. - // They were collected in the first pass and we don't gather alt names for them. - if (which === 'secondary' && parts.d) return; - - - if (obj.countryCodes) { - parts.countryCodes = obj.countryCodes.slice(); // copy - } - - const nomatches = (obj.nomatch || []); - if (nomatches.some(s => s === kvnd)) { - console.log(`WARNING match/nomatch conflict for ${kvnd}`); - return; - } - - const match_kv = [parts.kv] - .concat(obj.matchTags || []) - .concat([`${parts.k}/yes`, `building/yes`]) // #3454 - match some generic tags - .map(s => s.toLowerCase()); - - let match_nsimple = []; - if (which === 'primary') { - match_nsimple = [parts.n] - .concat(obj.matchNames || []) - .concat(obj.tags.official_name || []) // #2732 - match alternate names - .map(simplify); - - } else if (which === 'secondary') { - match_nsimple = [] - .concat(obj.tags.alt_name || []) // #2732 - match alternate names - .concat(obj.tags.short_name || []) // #2732 - match alternate names - .map(simplify); - } - - if (!match_nsimple.length) return; // nothing to do - - match_kv.forEach(kv => { - match_nsimple.forEach(nsimple => { - if (parts.d) { - // Known ambiguous names with disambiguation string ~(USA) / ~(Canada) - // FIXME: Name collisions will overwrite the initial entry (ok for now) - if (!_ambiguous[kv]) _ambiguous[kv] = {}; - _ambiguous[kv][nsimple] = parts; - - } else { - // Names we mostly expect to be unique.. - if (!_matchIndex[kv]) _matchIndex[kv] = {}; - - const m = _matchIndex[kv][nsimple]; - if (m) { // There already is a match for this name, skip it - // Warn if we detect collisions in a primary name. - // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454 - if (which === 'primary' && !/\/yes$/.test(kv)) { - _warnings.push([m.kvnd, `${kvnd} (${kv}/${nsimple})`]); - } - } else { - _matchIndex[kv][nsimple] = parts; // insert - } - } - }); - }); - - } - }; - - - // pass a `key`, `value`, `name` and return the best match, - // `countryCode` optional (if supplied, must match that too) - matcher.matchKVN = (key, value, name, countryCode) => { - return matcher.matchParts(to_parts(`${key}/${value}|${name}`), countryCode); - }; - - - // pass a parts object and return the best match, - // `countryCode` optional (if supplied, must match that too) - matcher.matchParts = (parts, countryCode) => { - let match = null; - let inGroup = false; - - // fixme: we currently return a single match for ambiguous - match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple]; - if (match && matchesCountryCode(match)) return match; - - // try to return an exact match - match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple]; - if (match && matchesCountryCode(match)) return match; - - // look in match groups - for (let mg in matchGroups$1) { - const matchGroup = matchGroups$1[mg]; - match = null; - inGroup = false; - - for (let i = 0; i < matchGroup.length; i++) { - const otherkv = matchGroup[i].toLowerCase(); - if (!inGroup) { - inGroup = otherkv === parts.kv; - } - if (!match) { - // fixme: we currently return a single match for ambiguous - match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple]; - } - if (!match) { - match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple]; - } - - if (match && !matchesCountryCode(match)) { - match = null; - } - - if (inGroup && match) { - return match; - } - } - } - - return null; - - function matchesCountryCode(match) { - if (!countryCode) return true; - if (!match.countryCodes) return true; - return match.countryCodes.indexOf(countryCode) !== -1; - } - }; - - - matcher.getWarnings = () => _warnings; - - return matcher; - }; - - var quickselect$1 = createCommonjsModule(function (module, exports) { - (function (global, factory) { - module.exports = factory() ; - }(commonjsGlobal, (function () { - function quickselect(arr, k, left, right, compare) { - quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare); - } - - function quickselectStep(arr, k, left, right, compare) { - - while (right > left) { - if (right - left > 600) { - var n = right - left + 1; - var m = k - left + 1; - var z = Math.log(n); - var s = 0.5 * Math.exp(2 * z / 3); - var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); - var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); - var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); - quickselectStep(arr, k, newLeft, newRight, compare); - } - - var t = arr[k]; - var i = left; - var j = right; - - swap(arr, left, k); - if (compare(arr[right], t) > 0) swap(arr, left, right); - - while (i < j) { - swap(arr, i, j); - i++; - j--; - while (compare(arr[i], t) < 0) i++; - while (compare(arr[j], t) > 0) j--; - } - - if (compare(arr[left], t) === 0) swap(arr, left, j); - else { - j++; - swap(arr, j, right); - } - - if (j <= k) left = j + 1; - if (k <= j) right = j - 1; - } - } - - function swap(arr, i, j) { - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; - } - - function defaultCompare(a, b) { - return a < b ? -1 : a > b ? 1 : 0; - } - - return quickselect; - - }))); - }); - - var rbush_1 = rbush; - var _default$2 = rbush; - - - - function rbush(maxEntries, format) { - if (!(this instanceof rbush)) return new rbush(maxEntries, format); - - // max entries in a node is 9 by default; min node fill is 40% for best performance - this._maxEntries = Math.max(4, maxEntries || 9); - this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); - - if (format) { - this._initFormat(format); - } - - this.clear(); - } - - rbush.prototype = { - - all: function () { - return this._all(this.data, []); - }, - - search: function (bbox) { - - var node = this.data, - result = [], - toBBox = this.toBBox; - - if (!intersects$1(bbox, node)) return result; - - var nodesToSearch = [], - i, len, child, childBBox; - - while (node) { - for (i = 0, len = node.children.length; i < len; i++) { - - child = node.children[i]; - childBBox = node.leaf ? toBBox(child) : child; - - if (intersects$1(bbox, childBBox)) { - if (node.leaf) result.push(child); - else if (contains$2(bbox, childBBox)) this._all(child, result); - else nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - - return result; - }, - - collides: function (bbox) { - - var node = this.data, - toBBox = this.toBBox; - - if (!intersects$1(bbox, node)) return false; - - var nodesToSearch = [], - i, len, child, childBBox; - - while (node) { - for (i = 0, len = node.children.length; i < len; i++) { - - child = node.children[i]; - childBBox = node.leaf ? toBBox(child) : child; - - if (intersects$1(bbox, childBBox)) { - if (node.leaf || contains$2(bbox, childBBox)) return true; - nodesToSearch.push(child); - } - } - node = nodesToSearch.pop(); - } - - return false; - }, - - load: function (data) { - if (!(data && data.length)) return this; - - if (data.length < this._minEntries) { - for (var i = 0, len = data.length; i < len; i++) { - this.insert(data[i]); - } - return this; - } - - // recursively build the tree with the given data from scratch using OMT algorithm - var node = this._build(data.slice(), 0, data.length - 1, 0); - - if (!this.data.children.length) { - // save as is if tree is empty - this.data = node; - - } else if (this.data.height === node.height) { - // split root if trees have the same height - this._splitRoot(this.data, node); - - } else { - if (this.data.height < node.height) { - // swap trees if inserted one is bigger - var tmpNode = this.data; - this.data = node; - node = tmpNode; - } - - // insert the small tree into the large tree at appropriate level - this._insert(node, this.data.height - node.height - 1, true); - } - - return this; - }, - - insert: function (item) { - if (item) this._insert(item, this.data.height - 1); - return this; - }, - - clear: function () { - this.data = createNode$1([]); - return this; - }, - - remove: function (item, equalsFn) { - if (!item) return this; - - var node = this.data, - bbox = this.toBBox(item), - path = [], - indexes = [], - i, parent, index, goingUp; - - // depth-first iterative tree traversal - while (node || path.length) { - - if (!node) { // go up - node = path.pop(); - parent = path[path.length - 1]; - i = indexes.pop(); - goingUp = true; - } - - if (node.leaf) { // check current node - index = findItem$1(item, node.children, equalsFn); - - if (index !== -1) { - // item found, remove the item and condense tree upwards - node.children.splice(index, 1); - path.push(node); - this._condense(path); - return this; - } - } - - if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down - path.push(node); - indexes.push(i); - i = 0; - parent = node; - node = node.children[0]; - - } else if (parent) { // go right - i++; - node = parent.children[i]; - goingUp = false; - - } else node = null; // nothing found - } - - return this; - }, - - toBBox: function (item) { return item; }, - - compareMinX: compareNodeMinX$1, - compareMinY: compareNodeMinY$1, - - toJSON: function () { return this.data; }, - - fromJSON: function (data) { - this.data = data; - return this; - }, - - _all: function (node, result) { - var nodesToSearch = []; - while (node) { - if (node.leaf) result.push.apply(result, node.children); - else nodesToSearch.push.apply(nodesToSearch, node.children); - - node = nodesToSearch.pop(); - } - return result; - }, - - _build: function (items, left, right, height) { - - var N = right - left + 1, - M = this._maxEntries, - node; - - if (N <= M) { - // reached leaf level; return leaf - node = createNode$1(items.slice(left, right + 1)); - calcBBox$1(node, this.toBBox); - return node; - } - - if (!height) { - // target height of the bulk-loaded tree - height = Math.ceil(Math.log(N) / Math.log(M)); - - // target number of root entries to maximize storage utilization - M = Math.ceil(N / Math.pow(M, height - 1)); - } - - node = createNode$1([]); - node.leaf = false; - node.height = height; - - // split the items into M mostly square tiles - - var N2 = Math.ceil(N / M), - N1 = N2 * Math.ceil(Math.sqrt(M)), - i, j, right2, right3; - - multiSelect$1(items, left, right, N1, this.compareMinX); - - for (i = left; i <= right; i += N1) { - - right2 = Math.min(i + N1 - 1, right); - - multiSelect$1(items, i, right2, N2, this.compareMinY); - - for (j = i; j <= right2; j += N2) { - - right3 = Math.min(j + N2 - 1, right2); - - // pack each entry recursively - node.children.push(this._build(items, j, right3, height - 1)); - } - } - - calcBBox$1(node, this.toBBox); - - return node; - }, - - _chooseSubtree: function (bbox, node, level, path) { - - var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; - - while (true) { - path.push(node); - - if (node.leaf || path.length - 1 === level) break; - - minArea = minEnlargement = Infinity; - - for (i = 0, len = node.children.length; i < len; i++) { - child = node.children[i]; - area = bboxArea$1(child); - enlargement = enlargedArea$1(bbox, child) - area; - - // choose entry with the least area enlargement - if (enlargement < minEnlargement) { - minEnlargement = enlargement; - minArea = area < minArea ? area : minArea; - targetNode = child; - - } else if (enlargement === minEnlargement) { - // otherwise choose one with the smallest area - if (area < minArea) { - minArea = area; - targetNode = child; - } - } - } - - node = targetNode || node.children[0]; - } - - return node; - }, - - _insert: function (item, level, isNode) { - - var toBBox = this.toBBox, - bbox = isNode ? item : toBBox(item), - insertPath = []; - - // find the best node for accommodating the item, saving all nodes along the path too - var node = this._chooseSubtree(bbox, this.data, level, insertPath); - - // put the item into the node - node.children.push(item); - extend$3(node, bbox); - - // split on node overflow; propagate upwards if necessary - while (level >= 0) { - if (insertPath[level].children.length > this._maxEntries) { - this._split(insertPath, level); - level--; - } else break; - } - - // adjust bboxes along the insertion path - this._adjustParentBBoxes(bbox, insertPath, level); - }, - - // split overflowed node into two - _split: function (insertPath, level) { - - var node = insertPath[level], - M = node.children.length, - m = this._minEntries; - - this._chooseSplitAxis(node, m, M); - - var splitIndex = this._chooseSplitIndex(node, m, M); - - var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex)); - newNode.height = node.height; - newNode.leaf = node.leaf; - - calcBBox$1(node, this.toBBox); - calcBBox$1(newNode, this.toBBox); - - if (level) insertPath[level - 1].children.push(newNode); - else this._splitRoot(node, newNode); - }, - - _splitRoot: function (node, newNode) { - // split root node - this.data = createNode$1([node, newNode]); - this.data.height = node.height + 1; - this.data.leaf = false; - calcBBox$1(this.data, this.toBBox); - }, - - _chooseSplitIndex: function (node, m, M) { - - var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; - - minOverlap = minArea = Infinity; - - for (i = m; i <= M - m; i++) { - bbox1 = distBBox$1(node, 0, i, this.toBBox); - bbox2 = distBBox$1(node, i, M, this.toBBox); - - overlap = intersectionArea$1(bbox1, bbox2); - area = bboxArea$1(bbox1) + bboxArea$1(bbox2); - - // choose distribution with minimum overlap - if (overlap < minOverlap) { - minOverlap = overlap; - index = i; - - minArea = area < minArea ? area : minArea; - - } else if (overlap === minOverlap) { - // otherwise choose distribution with minimum area - if (area < minArea) { - minArea = area; - index = i; - } - } - } - - return index; - }, - - // sorts node children by the best axis for split - _chooseSplitAxis: function (node, m, M) { - - var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1, - compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1, - xMargin = this._allDistMargin(node, m, M, compareMinX), - yMargin = this._allDistMargin(node, m, M, compareMinY); - - // if total distributions margin value is minimal for x, sort by minX, - // otherwise it's already sorted by minY - if (xMargin < yMargin) node.children.sort(compareMinX); - }, - - // total margin of all possible split distributions where each node is at least m full - _allDistMargin: function (node, m, M, compare) { - - node.children.sort(compare); - - var toBBox = this.toBBox, - leftBBox = distBBox$1(node, 0, m, toBBox), - rightBBox = distBBox$1(node, M - m, M, toBBox), - margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox), - i, child; - - for (i = m; i < M - m; i++) { - child = node.children[i]; - extend$3(leftBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin$1(leftBBox); - } - - for (i = M - m - 1; i >= m; i--) { - child = node.children[i]; - extend$3(rightBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin$1(rightBBox); - } - - return margin; - }, - - _adjustParentBBoxes: function (bbox, path, level) { - // adjust bboxes along the given tree path - for (var i = level; i >= 0; i--) { - extend$3(path[i], bbox); - } - }, - - _condense: function (path) { - // go through the path, removing empty nodes and updating bboxes - for (var i = path.length - 1, siblings; i >= 0; i--) { - if (path[i].children.length === 0) { - if (i > 0) { - siblings = path[i - 1].children; - siblings.splice(siblings.indexOf(path[i]), 1); - - } else this.clear(); - - } else calcBBox$1(path[i], this.toBBox); - } - }, - - _initFormat: function (format) { - // data format (minX, minY, maxX, maxY accessors) - - // uses eval-type function compilation instead of just accepting a toBBox function - // because the algorithms are very sensitive to sorting functions performance, - // so they should be dead simple and without inner calls - - var compareArr = ['return a', ' - b', ';']; - - this.compareMinX = new Function('a', 'b', compareArr.join(format[0])); - this.compareMinY = new Function('a', 'b', compareArr.join(format[1])); - - this.toBBox = new Function('a', - 'return {minX: a' + format[0] + - ', minY: a' + format[1] + - ', maxX: a' + format[2] + - ', maxY: a' + format[3] + '};'); - } - }; - - function findItem$1(item, items, equalsFn) { - if (!equalsFn) return items.indexOf(item); - - for (var i = 0; i < items.length; i++) { - if (equalsFn(item, items[i])) return i; - } - return -1; - } - - // calculate node's bbox from bboxes of its children - function calcBBox$1(node, toBBox) { - distBBox$1(node, 0, node.children.length, toBBox, node); - } - - // min bounding rectangle of node children from k to p-1 - function distBBox$1(node, k, p, toBBox, destNode) { - if (!destNode) destNode = createNode$1(null); - destNode.minX = Infinity; - destNode.minY = Infinity; - destNode.maxX = -Infinity; - destNode.maxY = -Infinity; - - for (var i = k, child; i < p; i++) { - child = node.children[i]; - extend$3(destNode, node.leaf ? toBBox(child) : child); - } - - return destNode; - } - - function extend$3(a, b) { - a.minX = Math.min(a.minX, b.minX); - a.minY = Math.min(a.minY, b.minY); - a.maxX = Math.max(a.maxX, b.maxX); - a.maxY = Math.max(a.maxY, b.maxY); - return a; - } - - function compareNodeMinX$1(a, b) { return a.minX - b.minX; } - function compareNodeMinY$1(a, b) { return a.minY - b.minY; } - - function bboxArea$1(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } - function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } - - function enlargedArea$1(a, b) { - return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * - (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); - } - - function intersectionArea$1(a, b) { - var minX = Math.max(a.minX, b.minX), - minY = Math.max(a.minY, b.minY), - maxX = Math.min(a.maxX, b.maxX), - maxY = Math.min(a.maxY, b.maxY); - - return Math.max(0, maxX - minX) * - Math.max(0, maxY - minY); - } - - function contains$2(a, b) { - return a.minX <= b.minX && - a.minY <= b.minY && - b.maxX <= a.maxX && - b.maxY <= a.maxY; - } - - function intersects$1(a, b) { - return b.minX <= a.maxX && - b.minY <= a.maxY && - b.maxX >= a.minX && - b.maxY >= a.minY; - } - - function createNode$1(children) { - return { - children: children, - height: 1, - leaf: true, - minX: Infinity, - minY: Infinity, - maxX: -Infinity, - maxY: -Infinity - }; - } - - // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; - // combines selection algorithm with binary divide & conquer approach - - function multiSelect$1(arr, left, right, n, compare) { - var stack = [left, right], - mid; - - while (stack.length) { - right = stack.pop(); - left = stack.pop(); - - if (right - left <= n) continue; - - mid = left + Math.ceil((right - left) / n / 2) * n; - quickselect$1(arr, mid, left, right, compare); - - stack.push(left, mid, mid, right); - } - } - rbush_1.default = _default$2; - - var lineclip_1$1 = lineclip$1; - - lineclip$1.polyline = lineclip$1; - lineclip$1.polygon = polygonclip$1; - - - // Cohen-Sutherland line clippign algorithm, adapted to efficiently - // handle polylines rather than just segments - - function lineclip$1(points, bbox, result) { - - var len = points.length, - codeA = bitCode$1(points[0], bbox), - part = [], - i, a, b, codeB, lastCode; - - if (!result) result = []; - - for (i = 1; i < len; i++) { - a = points[i - 1]; - b = points[i]; - codeB = lastCode = bitCode$1(b, bbox); - - while (true) { - - if (!(codeA | codeB)) { // accept - part.push(a); - - if (codeB !== lastCode) { // segment went outside - part.push(b); - - if (i < len - 1) { // start a new line - result.push(part); - part = []; - } - } else if (i === len - 1) { - part.push(b); - } - break; - - } else if (codeA & codeB) { // trivial reject - break; - - } else if (codeA) { // a outside, intersect with clip edge - a = intersect$1(a, b, codeA, bbox); - codeA = bitCode$1(a, bbox); - - } else { // b outside - b = intersect$1(a, b, codeB, bbox); - codeB = bitCode$1(b, bbox); - } - } - - codeA = lastCode; - } - - if (part.length) result.push(part); - - return result; - } - - // Sutherland-Hodgeman polygon clipping algorithm - - function polygonclip$1(points, bbox) { - - var result, edge, prev, prevInside, i, p, inside; - - // clip against each side of the clip rectangle - for (edge = 1; edge <= 8; edge *= 2) { - result = []; - prev = points[points.length - 1]; - prevInside = !(bitCode$1(prev, bbox) & edge); - - for (i = 0; i < points.length; i++) { - p = points[i]; - inside = !(bitCode$1(p, bbox) & edge); - - // if segment goes through the clip window, add an intersection - if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox)); - - if (inside) result.push(p); // add a point if it's inside - - prev = p; - prevInside = inside; - } - - points = result; - - if (!points.length) break; - } - - return result; - } - - // intersect a segment against one of the 4 lines that make up the bbox - - function intersect$1(a, b, edge, bbox) { - return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top - edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom - edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right - edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left - null; - } - - // bit code reflects the point position relative to the bbox: - - // left mid right - // top 1001 1000 1010 - // mid 0001 0000 0010 - // bottom 0101 0100 0110 - - function bitCode$1(p, bbox) { - var code = 0; - - if (p[0] < bbox[0]) code |= 1; // left - else if (p[0] > bbox[2]) code |= 2; // right - - if (p[1] < bbox[1]) code |= 4; // bottom - else if (p[1] > bbox[3]) code |= 8; // top - - return code; - } - - var whichPolygon_1 = whichPolygon; - - function whichPolygon(data) { - var bboxes = []; - for (var i = 0; i < data.features.length; i++) { - var feature = data.features[i]; - var coords = feature.geometry.coordinates; - - if (feature.geometry.type === 'Polygon') { - bboxes.push(treeItem(coords, feature.properties)); - - } else if (feature.geometry.type === 'MultiPolygon') { - for (var j = 0; j < coords.length; j++) { - bboxes.push(treeItem(coords[j], feature.properties)); - } - } - } - - var tree = rbush_1().load(bboxes); - - function query(p, multi) { - var output = [], - result = tree.search({ - minX: p[0], - minY: p[1], - maxX: p[0], - maxY: p[1] - }); - for (var i = 0; i < result.length; i++) { - if (insidePolygon(result[i].coords, p)) { - if (multi) - output.push(result[i].props); - else - return result[i].props; - } - } - return multi && output.length ? output : null; - } - - query.tree = tree; - query.bbox = function queryBBox(bbox) { - var output = []; - var result = tree.search({ - minX: bbox[0], - minY: bbox[1], - maxX: bbox[2], - maxY: bbox[3] - }); - for (var i = 0; i < result.length; i++) { - if (polygonIntersectsBBox(result[i].coords, bbox)) { - output.push(result[i].props); - } - } - return output; - }; - - return query; - } - - function polygonIntersectsBBox(polygon, bbox) { - var bboxCenter = [ - (bbox[0] + bbox[2]) / 2, - (bbox[1] + bbox[3]) / 2 - ]; - if (insidePolygon(polygon, bboxCenter)) return true; - for (var i = 0; i < polygon.length; i++) { - if (lineclip_1$1(polygon[i], bbox).length > 0) return true; - } - return false; - } - - // ray casting algorithm for detecting if point is in polygon - function insidePolygon(rings, p) { - var inside = false; - for (var i = 0, len = rings.length; i < len; i++) { - var ring = rings[i]; - for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) { - if (rayIntersect(p, ring[j], ring[k])) inside = !inside; - } - } - return inside; - } - - function rayIntersect(p, p1, p2) { - return ((p1[1] > p[1]) !== (p2[1] > p[1])) && (p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]); - } - - function treeItem(coords, props) { - var item = { - minX: Infinity, - minY: Infinity, - maxX: -Infinity, - maxY: -Infinity, - coords: coords, - props: props - }; - - for (var i = 0; i < coords[0].length; i++) { - var p = coords[0][i]; - item.minX = Math.min(item.minX, p[0]); - item.minY = Math.min(item.minY, p[1]); - item.maxX = Math.max(item.maxX, p[0]); - item.maxY = Math.max(item.maxY, p[1]); - } - return item; - } - - var type = "FeatureCollection"; - var features = [{type:"Feature",properties:{m49:"680",wikidata:"Q3405693",nameEn:"Sark",country:"GB",groups:["GG","830","154","150"],level:"subterritory",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.36485,49.48223],[-2.65349,49.15373],[-2.09454,49.46288],[-2.36485,49.48223]]]]}},{type:"Feature",properties:{m49:"001",wikidata:"Q2",nameEn:"World",aliases:["Earth","Planet"],level:"world"},geometry:null},{type:"Feature",properties:{m49:"142",wikidata:"Q48",nameEn:"Asia",level:"region"},geometry:null},{type:"Feature",properties:{m49:"143",wikidata:"Q27275",nameEn:"Central Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"145",wikidata:"Q27293",nameEn:"Western Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"150",wikidata:"Q46",nameEn:"Europe",level:"region"},geometry:null},{type:"Feature",properties:{m49:"151",wikidata:"Q27468",nameEn:"Eastern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"154",wikidata:"Q27479",nameEn:"Northern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"155",wikidata:"Q27496",nameEn:"Western Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"202",wikidata:"Q132959",nameEn:"Sub-Saharan Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"419",wikidata:"Q72829598",nameEn:"Latin America and the Caribbean",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"830",wikidata:"Q42314",nameEn:"Channel Islands",groups:["150","154"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"019",wikidata:"Q828",nameEn:"Americas",level:"region"},geometry:null},{type:"Feature",properties:{m49:"029",wikidata:"Q664609",nameEn:"Caribbean",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"034",wikidata:"Q771405",nameEn:"Southern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"002",wikidata:"Q15",nameEn:"Africa",level:"region"},geometry:null},{type:"Feature",properties:{m49:"003",wikidata:"Q49",nameEn:"North America",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"017",wikidata:"Q27433",nameEn:"Middle Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"039",wikidata:"Q27449",nameEn:"Southern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"005",wikidata:"Q18",nameEn:"South America",groups:["419","019"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"009",wikidata:"Q538",nameEn:"Oceania",level:"region"},geometry:null},{type:"Feature",properties:{m49:"061",wikidata:"Q35942",nameEn:"Polynesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"014",wikidata:"Q27407",nameEn:"Eastern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"053",wikidata:"Q45256",nameEn:"Australia and New Zealand",aliases:["Australasia"],groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"011",wikidata:"Q4412",nameEn:"Western Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"013",wikidata:"Q27611",nameEn:"Central America",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"021",wikidata:"Q2017699",nameEn:"Northern America",groups:["019","003"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"035",wikidata:"Q11708",nameEn:"South-eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"018",wikidata:"Q27394",nameEn:"Southern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"030",wikidata:"Q27231",nameEn:"Eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"015",wikidata:"Q27381",nameEn:"Northern Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"054",wikidata:"Q37394",nameEn:"Melanesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"057",wikidata:"Q3359409",nameEn:"Micronesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{iso1A2:"AC",iso1A3:"ASC",wikidata:"Q46197",nameEn:"Ascension Island",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["247"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.33271,-8.07391],[-14.91926,-6.63386],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"AD",iso1A3:"AND",iso1N3:"020",wikidata:"Q228",nameEn:"Andorra",groups:["039","150"],callingCodes:["376"]},geometry:{type:"MultiPolygon",coordinates:[[[[1.72515,42.50338],[1.73683,42.55492],[1.7858,42.57698],[1.72588,42.59098],[1.73452,42.61515],[1.68267,42.62533],[1.6625,42.61982],[1.63485,42.62957],[1.60085,42.62703],[1.55418,42.65669],[1.50867,42.64483],[1.48043,42.65203],[1.46718,42.63296],[1.47986,42.61346],[1.44197,42.60217],[1.42512,42.58292],[1.44529,42.56722],[1.4234,42.55959],[1.41245,42.53539],[1.44759,42.54431],[1.46661,42.50949],[1.41648,42.48315],[1.43838,42.47848],[1.44529,42.43724],[1.5127,42.42959],[1.55073,42.43299],[1.55937,42.45808],[1.57953,42.44957],[1.58933,42.46275],[1.65674,42.47125],[1.66826,42.50779],[1.70571,42.48867],[1.72515,42.50338]]]]}},{type:"Feature",properties:{iso1A2:"AE",iso1A3:"ARE",iso1N3:"784",wikidata:"Q878",nameEn:"United Arab Emirates",groups:["145","142"],callingCodes:["971"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.26534,25.62825],[56.25341,25.61443],[56.26636,25.60643],[56.25365,25.60211],[56.20473,25.61119],[56.18363,25.65508],[56.14826,25.66351],[56.13579,25.73524],[56.17416,25.77239],[56.13963,25.82765],[56.19334,25.9795],[56.15498,26.06828],[56.08666,26.05038],[55.81777,26.18798],[55.14145,25.62624],[53.97892,24.64436],[52.82259,25.51697],[52.35509,25.00368],[52.02277,24.75635],[51.83108,24.71675],[51.58834,24.66608],[51.41644,24.39615],[51.58871,24.27256],[51.59617,24.12041],[52.56622,22.94341],[55.13599,22.63334],[55.2137,22.71065],[55.22634,23.10378],[55.57358,23.669],[55.48677,23.94946],[55.73301,24.05994],[55.8308,24.01633],[56.01799,24.07426],[55.95472,24.2172],[55.83367,24.20193],[55.77658,24.23476],[55.76558,24.23227],[55.75257,24.23466],[55.75382,24.2466],[55.75939,24.26114],[55.76781,24.26209],[55.79145,24.27914],[55.80747,24.31069],[55.83395,24.32776],[55.83271,24.41521],[55.76461,24.5287],[55.83271,24.68567],[55.83408,24.77858],[55.81348,24.80102],[55.81116,24.9116],[55.85094,24.96858],[55.90849,24.96771],[55.96316,25.00857],[56.05715,24.95727],[56.05106,24.87461],[55.97467,24.89639],[55.97836,24.87673],[56.03535,24.81161],[56.06128,24.74457],[56.13684,24.73699],[56.20062,24.78565],[56.20568,24.85063],[56.30269,24.88334],[56.34873,24.93205],[56.3227,24.97284],[56.86325,25.03856],[56.82555,25.7713],[56.26534,25.62825]],[[56.26062,25.33108],[56.3005,25.31815],[56.3111,25.30107],[56.35172,25.30681],[56.34438,25.26653],[56.27628,25.23404],[56.24341,25.22867],[56.20872,25.24104],[56.20838,25.25668],[56.24465,25.27505],[56.25008,25.28843],[56.23362,25.31253],[56.26062,25.33108]]],[[[56.28423,25.26344],[56.29379,25.2754],[56.28102,25.28486],[56.2716,25.27916],[56.27086,25.26128],[56.28423,25.26344]]]]}},{type:"Feature",properties:{iso1A2:"AF",iso1A3:"AFG",iso1N3:"004",wikidata:"Q889",nameEn:"Afghanistan",groups:["034","142"],callingCodes:["93"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.61526,38.34774],[70.60407,38.28046],[70.54673,38.24541],[70.4898,38.12546],[70.17206,37.93276],[70.1863,37.84296],[70.27694,37.81258],[70.28243,37.66706],[70.15015,37.52519],[69.95971,37.5659],[69.93362,37.61378],[69.84435,37.60616],[69.80041,37.5746],[69.51888,37.5844],[69.44954,37.4869],[69.36645,37.40462],[69.45022,37.23315],[69.39529,37.16752],[69.25152,37.09426],[69.03274,37.25174],[68.96407,37.32603],[68.88168,37.33368],[68.91189,37.26704],[68.80889,37.32494],[68.81438,37.23862],[68.6798,37.27906],[68.61851,37.19815],[68.41888,37.13906],[68.41201,37.10402],[68.29253,37.10621],[68.27605,37.00977],[68.18542,37.02074],[68.02194,36.91923],[67.87917,37.0591],[67.7803,37.08978],[67.78329,37.1834],[67.51868,37.26102],[67.2581,37.17216],[67.2224,37.24545],[67.13039,37.27168],[67.08232,37.35469],[66.95598,37.40162],[66.64699,37.32958],[66.55743,37.35409],[66.30993,37.32409],[65.72274,37.55438],[65.64137,37.45061],[65.64263,37.34388],[65.51778,37.23881],[64.97945,37.21913],[64.61141,36.6351],[64.62514,36.44311],[64.57295,36.34362],[64.43288,36.24401],[64.05385,36.10433],[63.98519,36.03773],[63.56496,35.95106],[63.53475,35.90881],[63.29579,35.85985],[63.12276,35.86208],[63.10318,35.81782],[63.23262,35.67487],[63.10079,35.63024],[63.12276,35.53196],[63.0898,35.43131],[62.90853,35.37086],[62.74098,35.25432],[62.62288,35.22067],[62.48006,35.28796],[62.29878,35.13312],[62.29191,35.25964],[62.15871,35.33278],[62.05709,35.43803],[61.97743,35.4604],[61.77693,35.41341],[61.58742,35.43803],[61.27371,35.61482],[61.18187,35.30249],[61.0991,35.27845],[61.12831,35.09938],[61.06926,34.82139],[61.00197,34.70631],[60.99922,34.63064],[60.72316,34.52857],[60.91321,34.30411],[60.66502,34.31539],[60.50209,34.13992],[60.5838,33.80793],[60.5485,33.73422],[60.57762,33.59772],[60.69573,33.56054],[60.91133,33.55596],[60.88908,33.50219],[60.56485,33.12944],[60.86191,32.22565],[60.84541,31.49561],[61.70929,31.37391],[61.80569,31.16167],[61.80957,31.12576],[61.83257,31.0452],[61.8335,30.97669],[61.78268,30.92724],[61.80829,30.84224],[60.87231,29.86514],[62.47751,29.40782],[63.5876,29.50456],[64.12966,29.39157],[64.19796,29.50407],[64.62116,29.58903],[65.04005,29.53957],[66.24175,29.85181],[66.36042,29.9583],[66.23609,30.06321],[66.34869,30.404],[66.28413,30.57001],[66.39194,30.9408],[66.42645,30.95309],[66.58175,30.97532],[66.68166,31.07597],[66.72561,31.20526],[66.83273,31.26867],[67.04147,31.31561],[67.03323,31.24519],[67.29964,31.19586],[67.78854,31.33203],[67.7748,31.4188],[67.62374,31.40473],[67.58323,31.52772],[67.72056,31.52304],[67.86887,31.63536],[68.00071,31.6564],[68.1655,31.82691],[68.25614,31.80357],[68.27605,31.75863],[68.44222,31.76446],[68.57475,31.83158],[68.6956,31.75687],[68.79997,31.61665],[68.91078,31.59687],[68.95995,31.64822],[69.00939,31.62249],[69.11514,31.70782],[69.20577,31.85957],[69.3225,31.93186],[69.27032,32.14141],[69.27932,32.29119],[69.23599,32.45946],[69.2868,32.53938],[69.38155,32.56601],[69.44747,32.6678],[69.43649,32.7302],[69.38018,32.76601],[69.47082,32.85834],[69.5436,32.8768],[69.49854,32.88843],[69.49004,33.01509],[69.57656,33.09911],[69.71526,33.09911],[69.79766,33.13247],[69.85259,33.09451],[70.02563,33.14282],[70.07369,33.22557],[70.13686,33.21064],[70.32775,33.34496],[70.17062,33.53535],[70.20141,33.64387],[70.14785,33.6553],[70.14236,33.71701],[70.00503,33.73528],[69.85671,33.93719],[69.87307,33.9689],[69.90203,34.04194],[70.54336,33.9463],[70.88119,33.97933],[71.07345,34.06242],[71.06933,34.10564],[71.09307,34.11961],[71.09453,34.13524],[71.13078,34.16503],[71.12815,34.26619],[71.17662,34.36769],[71.02401,34.44835],[71.0089,34.54568],[71.11602,34.63047],[71.08718,34.69034],[71.28356,34.80882],[71.29472,34.87728],[71.50329,34.97328],[71.49917,35.00478],[71.55273,35.02615],[71.52938,35.09023],[71.67495,35.21262],[71.5541,35.28776],[71.54294,35.31037],[71.65435,35.4479],[71.49917,35.6267],[71.55273,35.71483],[71.37969,35.95865],[71.19505,36.04134],[71.60491,36.39429],[71.80267,36.49924],[72.18135,36.71838],[72.6323,36.84601],[73.82685,36.91421],[74.04856,36.82648],[74.43389,37.00977],[74.53739,36.96224],[74.56453,37.03023],[74.49981,37.24518],[74.80605,37.21565],[74.88887,37.23275],[74.8294,37.3435],[74.68383,37.3948],[74.56161,37.37734],[74.41055,37.3948],[74.23339,37.41116],[74.20308,37.34208],[73.8564,37.26158],[73.82552,37.22659],[73.64974,37.23643],[73.61129,37.27469],[73.76647,37.33913],[73.77197,37.4417],[73.29633,37.46495],[73.06884,37.31729],[72.79693,37.22222],[72.66381,37.02014],[72.54095,37.00007],[72.31676,36.98115],[71.83229,36.68084],[71.67083,36.67346],[71.57195,36.74943],[71.51502,36.89128],[71.48481,36.93218],[71.46923,36.99925],[71.45578,37.03094],[71.43097,37.05855],[71.44127,37.11856],[71.4494,37.18137],[71.4555,37.21418],[71.47386,37.2269],[71.48339,37.23937],[71.4824,37.24921],[71.48536,37.26017],[71.50674,37.31502],[71.49821,37.31975],[71.4862,37.33405],[71.47685,37.40281],[71.49612,37.4279],[71.5256,37.47971],[71.50616,37.50733],[71.49693,37.53527],[71.5065,37.60912],[71.51972,37.61945],[71.54186,37.69691],[71.55234,37.73209],[71.53053,37.76534],[71.54324,37.77104],[71.55752,37.78677],[71.59255,37.79956],[71.58843,37.92425],[71.51565,37.95349],[71.32871,37.88564],[71.296,37.93403],[71.2809,37.91995],[71.24969,37.93031],[71.27278,37.96496],[71.27622,37.99946],[71.28922,38.01272],[71.29878,38.04429],[71.36444,38.15358],[71.37803,38.25641],[71.33869,38.27335],[71.33114,38.30339],[71.21291,38.32797],[71.1451,38.40106],[71.10957,38.40671],[71.10592,38.42077],[71.09542,38.42517],[71.0556,38.40176],[71.03545,38.44779],[70.98693,38.48862],[70.92728,38.43021],[70.88719,38.46826],[70.84376,38.44688],[70.82538,38.45394],[70.81697,38.44507],[70.80521,38.44447],[70.79766,38.44944],[70.78702,38.45031],[70.78581,38.45502],[70.77132,38.45548],[70.75455,38.4252],[70.72485,38.4131],[70.69807,38.41861],[70.67438,38.40597],[70.6761,38.39144],[70.69189,38.37031],[70.64966,38.34999],[70.61526,38.34774]]]]}},{type:"Feature",properties:{iso1A2:"AG",iso1A3:"ATG",iso1N3:"028",wikidata:"Q781",nameEn:"Antigua and Barbuda",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 268"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.12601,17.9235],[-62.27053,17.22145],[-62.62949,16.82364],[-62.52079,16.69392],[-62.14123,17.02632],[-61.83929,16.66647],[-61.44461,16.81958],[-61.45764,17.9187],[-62.12601,17.9235]]]]}},{type:"Feature",properties:{iso1A2:"AI",iso1A3:"AIA",iso1N3:"660",wikidata:"Q25228",nameEn:"Anguilla",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 264"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.83866,18.82518],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.46233,19.00569],[-63.83866,18.82518]]]]}},{type:"Feature",properties:{iso1A2:"AL",iso1A3:"ALB",iso1N3:"008",wikidata:"Q222",nameEn:"Albania",groups:["039","150"],callingCodes:["355"]},geometry:{type:"MultiPolygon",coordinates:[[[[20.07761,42.55582],[20.01834,42.54622],[20.00842,42.5109],[19.9324,42.51699],[19.82333,42.46581],[19.76549,42.50237],[19.74731,42.57422],[19.77375,42.58517],[19.73244,42.66299],[19.65972,42.62774],[19.4836,42.40831],[19.42352,42.36546],[19.42,42.33019],[19.28623,42.17745],[19.40687,42.10024],[19.37548,42.06835],[19.36867,42.02564],[19.37691,41.96977],[19.34601,41.95675],[19.33812,41.90669],[19.37451,41.8842],[19.37597,41.84849],[19.26406,41.74971],[19.0384,40.35325],[19.95905,39.82857],[19.97622,39.78684],[19.92466,39.69533],[19.98042,39.6504],[20.00957,39.69227],[20.05189,39.69112],[20.12956,39.65805],[20.15988,39.652],[20.22376,39.64532],[20.22707,39.67459],[20.27412,39.69884],[20.31961,39.72799],[20.29152,39.80421],[20.30804,39.81563],[20.38572,39.78516],[20.41475,39.81437],[20.41546,39.82832],[20.31135,39.99438],[20.37911,39.99058],[20.42373,40.06777],[20.48487,40.06271],[20.51297,40.08168],[20.55593,40.06524],[20.61081,40.07866],[20.62566,40.0897],[20.67162,40.09433],[20.71789,40.27739],[20.78234,40.35803],[20.7906,40.42726],[20.83688,40.47882],[20.94925,40.46625],[20.96908,40.51526],[21.03932,40.56299],[21.05833,40.66586],[20.98134,40.76046],[20.95752,40.76982],[20.98396,40.79109],[20.97887,40.85475],[20.97693,40.90103],[20.94305,40.92399],[20.83671,40.92752],[20.81567,40.89662],[20.73504,40.9081],[20.71634,40.91781],[20.65558,41.08009],[20.63454,41.0889],[20.59832,41.09066],[20.58546,41.11179],[20.59715,41.13644],[20.51068,41.2323],[20.49432,41.33679],[20.52119,41.34381],[20.55976,41.4087],[20.51301,41.442],[20.49039,41.49277],[20.45331,41.51436],[20.45809,41.5549],[20.52103,41.56473],[20.55508,41.58113],[20.51769,41.65975],[20.52937,41.69292],[20.51301,41.72433],[20.53405,41.78099],[20.57144,41.7897],[20.55976,41.87068],[20.59524,41.8818],[20.57946,41.91593],[20.63069,41.94913],[20.59434,42.03879],[20.55633,42.08173],[20.56955,42.12097],[20.48857,42.25444],[20.3819,42.3029],[20.34479,42.32656],[20.24399,42.32168],[20.21797,42.41237],[20.17127,42.50469],[20.07761,42.55582]]]]}},{type:"Feature",properties:{iso1A2:"AM",iso1A3:"ARM",iso1N3:"051",wikidata:"Q399",nameEn:"Armenia",groups:["145","142"],callingCodes:["374"]},geometry:{type:"MultiPolygon",coordinates:[[[[45.0133,41.29747],[44.93493,41.25685],[44.81437,41.30371],[44.80053,41.25949],[44.81749,41.23488],[44.84358,41.23088],[44.89911,41.21366],[44.87887,41.20195],[44.82084,41.21513],[44.72814,41.20338],[44.61462,41.24018],[44.59322,41.1933],[44.46791,41.18204],[44.34417,41.2382],[44.34337,41.20312],[44.32139,41.2079],[44.18148,41.24644],[44.16591,41.19141],[43.84835,41.16329],[43.74717,41.1117],[43.67712,41.13398],[43.4717,41.12611],[43.44984,41.0988],[43.47319,41.02251],[43.58683,40.98961],[43.67712,40.93084],[43.67712,40.84846],[43.74872,40.7365],[43.7425,40.66805],[43.63664,40.54159],[43.54791,40.47413],[43.60862,40.43267],[43.59928,40.34019],[43.71136,40.16673],[43.65221,40.14889],[43.65688,40.11199],[43.92307,40.01787],[44.1057,40.03555],[44.1778,40.02845],[44.26973,40.04866],[44.46635,39.97733],[44.61845,39.8281],[44.75779,39.7148],[44.88354,39.74432],[44.92869,39.72157],[45.06604,39.79277],[45.18554,39.67846],[45.17464,39.58614],[45.21784,39.58074],[45.23535,39.61373],[45.30385,39.61373],[45.29606,39.57654],[45.46992,39.49888],[45.70547,39.60174],[45.80804,39.56716],[45.83,39.46487],[45.79225,39.3695],[45.99774,39.28931],[46.02303,39.09978],[46.06973,39.0744],[46.14785,38.84206],[46.20601,38.85262],[46.34059,38.92076],[46.53497,38.86548],[46.51805,38.94982],[46.54296,39.07078],[46.44022,39.19636],[46.52584,39.18912],[46.54141,39.15895],[46.58032,39.21204],[46.63481,39.23013],[46.56476,39.24942],[46.50093,39.33736],[46.43244,39.35181],[46.37795,39.42039],[46.4013,39.45405],[46.53051,39.47809],[46.51027,39.52373],[46.57721,39.54414],[46.57098,39.56694],[46.52117,39.58734],[46.42465,39.57534],[46.40286,39.63651],[46.18493,39.60533],[45.96543,39.78859],[45.82533,39.82925],[45.7833,39.9475],[45.60895,39.97733],[45.59806,40.0131],[45.78642,40.03218],[45.83779,39.98925],[45.97944,40.181],[45.95609,40.27846],[45.65098,40.37696],[45.42994,40.53804],[45.45484,40.57707],[45.35366,40.65979],[45.4206,40.7424],[45.55914,40.78366],[45.60584,40.87436],[45.40814,40.97904],[45.44083,41.01663],[45.39725,41.02603],[45.35677,40.99784],[45.28859,41.03757],[45.26162,41.0228],[45.25897,41.0027],[45.1994,41.04518],[45.16493,41.05068],[45.1634,41.08082],[45.1313,41.09369],[45.12923,41.06059],[45.06784,41.05379],[45.08028,41.10917],[45.19942,41.13299],[45.1969,41.168],[45.11811,41.19967],[45.05201,41.19211],[45.02932,41.2101],[45.05497,41.2464],[45.0133,41.29747]],[[45.21324,40.9817],[45.21219,40.99001],[45.20518,40.99348],[45.19312,40.98998],[45.18382,41.0066],[45.20625,41.01484],[45.23487,41.00226],[45.23095,40.97828],[45.21324,40.9817]],[[45.00864,41.03411],[44.9903,41.05657],[44.96031,41.06345],[44.95383,41.07553],[44.97169,41.09176],[45.00864,41.09407],[45.03406,41.07931],[45.04517,41.06653],[45.03792,41.03938],[45.00864,41.03411]]],[[[45.50279,40.58424],[45.56071,40.64765],[45.51825,40.67382],[45.47927,40.65023],[45.50279,40.58424]]]]}},{type:"Feature",properties:{iso1A2:"AO",iso1A3:"AGO",iso1N3:"024",wikidata:"Q916",nameEn:"Angola",groups:["017","202","002"],callingCodes:["244"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.55507,-5.85631],[13.04371,-5.87078],[12.42245,-6.07585],[11.95767,-5.94705],[12.20376,-5.76338],[12.26557,-5.74031],[12.52318,-5.74353],[12.52301,-5.17481],[12.53599,-5.1618],[12.53586,-5.14658],[12.51589,-5.1332],[12.49815,-5.14058],[12.46297,-5.09408],[12.60251,-5.01715],[12.63465,-4.94632],[12.70868,-4.95505],[12.8733,-4.74346],[13.11195,-4.67745],[13.09648,-4.63739],[12.91489,-4.47907],[12.87096,-4.40315],[12.76844,-4.38709],[12.64835,-4.55937],[12.40964,-4.60609],[12.32324,-4.78415],[12.25587,-4.79437],[12.20901,-4.75642],[12.16068,-4.90089],[12.00924,-5.02627],[11.50888,-5.33417],[10.5065,-17.25284],[11.75063,-17.25013],[12.07076,-17.15165],[12.52111,-17.24495],[12.97145,-16.98567],[13.36212,-16.98048],[13.95896,-17.43141],[14.28743,-17.38814],[18.39229,-17.38927],[18.84226,-17.80375],[21.14283,-17.94318],[21.42741,-18.02787],[23.47474,-17.62877],[23.20038,-17.47563],[22.17217,-16.50269],[22.00323,-16.18028],[21.97988,-13.00148],[24.03339,-12.99091],[23.90937,-12.844],[24.06672,-12.29058],[23.98804,-12.13149],[24.02603,-11.15368],[24.00027,-10.89356],[23.86868,-11.02856],[23.45631,-10.946],[23.16602,-11.10577],[22.54205,-11.05784],[22.25951,-11.24911],[22.17954,-10.85884],[22.32604,-10.76291],[22.19039,-9.94628],[21.84856,-9.59871],[21.79824,-7.29628],[20.56263,-7.28566],[20.61689,-6.90876],[20.31846,-6.91953],[20.30218,-6.98955],[19.5469,-7.00195],[19.33698,-7.99743],[18.33635,-8.00126],[17.5828,-8.13784],[16.96282,-7.21787],[16.55507,-5.85631]]]]}},{type:"Feature",properties:{iso1A2:"AQ",iso1A3:"ATA",iso1N3:"010",wikidata:"Q51",nameEn:"Antarctica",level:"region",callingCodes:["672"]},geometry:{type:"MultiPolygon",coordinates:[[[[180,-60],[-180,-60],[-180,-90],[180,-90],[180,-60]]]]}},{type:"Feature",properties:{iso1A2:"AR",iso1A3:"ARG",iso1N3:"032",wikidata:"Q414",nameEn:"Argentina",aliases:["RA"],groups:["005","419","019"],callingCodes:["54"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.31343,-50.58411],[-72.33873,-51.59954],[-71.99889,-51.98018],[-69.97824,-52.00845],[-68.41683,-52.33516],[-68.60702,-52.65781],[-68.60733,-54.9125],[-68.01394,-54.8753],[-67.46182,-54.92205],[-67.11046,-54.94199],[-66.07313,-55.19618],[-63.67376,-55.11859],[-54.78916,-36.21945],[-57.83001,-34.69099],[-58.34425,-34.15035],[-58.44442,-33.84033],[-58.40475,-33.11777],[-58.1224,-32.98842],[-58.22362,-32.52416],[-58.10036,-32.25338],[-58.20252,-31.86966],[-58.00076,-31.65016],[-58.0023,-31.53084],[-58.07569,-31.44916],[-57.98127,-31.3872],[-57.9908,-31.34924],[-57.86729,-31.06352],[-57.89476,-30.95994],[-57.8024,-30.77193],[-57.89115,-30.49572],[-57.64859,-30.35095],[-57.61478,-30.25165],[-57.65132,-30.19229],[-57.09386,-29.74211],[-56.81251,-29.48154],[-56.62789,-29.18073],[-56.57295,-29.11357],[-56.54171,-29.11447],[-56.05265,-28.62651],[-56.00458,-28.60421],[-56.01729,-28.51223],[-55.65418,-28.18304],[-55.6262,-28.17124],[-55.33303,-27.94661],[-55.16872,-27.86224],[-55.1349,-27.89759],[-54.90805,-27.73149],[-54.90159,-27.63132],[-54.67657,-27.57214],[-54.50416,-27.48232],[-54.41888,-27.40882],[-54.19268,-27.30751],[-54.19062,-27.27639],[-54.15978,-27.2889],[-53.80144,-27.09844],[-53.73372,-26.6131],[-53.68269,-26.33359],[-53.64505,-26.28089],[-53.64186,-26.25976],[-53.64632,-26.24798],[-53.63881,-26.25075],[-53.63739,-26.2496],[-53.65237,-26.23289],[-53.65018,-26.19501],[-53.73968,-26.10012],[-53.73391,-26.07006],[-53.7264,-26.0664],[-53.73086,-26.05842],[-53.73511,-26.04211],[-53.83691,-25.94849],[-53.90831,-25.55513],[-54.52926,-25.62846],[-54.5502,-25.58915],[-54.59398,-25.59224],[-54.62063,-25.91213],[-54.60664,-25.9691],[-54.67359,-25.98607],[-54.69333,-26.37705],[-54.70732,-26.45099],[-54.80868,-26.55669],[-55.00584,-26.78754],[-55.06351,-26.80195],[-55.16948,-26.96068],[-55.25243,-26.93808],[-55.39611,-26.97679],[-55.62322,-27.1941],[-55.59094,-27.32444],[-55.74475,-27.44485],[-55.89195,-27.3467],[-56.18313,-27.29851],[-56.85337,-27.5165],[-58.04205,-27.2387],[-58.59549,-27.29973],[-58.65321,-27.14028],[-58.3198,-26.83443],[-58.1188,-26.16704],[-57.87176,-25.93604],[-57.57431,-25.47269],[-57.80821,-25.13863],[-58.25492,-24.92528],[-58.33055,-24.97099],[-59.33886,-24.49935],[-59.45482,-24.34787],[-60.03367,-24.00701],[-60.28163,-24.04436],[-60.99754,-23.80934],[-61.0782,-23.62932],[-61.9756,-23.0507],[-62.22768,-22.55807],[-62.51761,-22.37684],[-62.64455,-22.25091],[-62.8078,-22.12534],[-62.81124,-21.9987],[-63.66482,-21.99918],[-63.68113,-22.0544],[-63.70963,-21.99934],[-63.93287,-21.99934],[-64.22918,-22.55807],[-64.31489,-22.88824],[-64.35108,-22.73282],[-64.4176,-22.67692],[-64.58888,-22.25035],[-64.67174,-22.18957],[-64.90014,-22.12136],[-64.99524,-22.08255],[-65.47435,-22.08908],[-65.57743,-22.07675],[-65.58694,-22.09794],[-65.61166,-22.09504],[-65.7467,-22.10105],[-65.9261,-21.93335],[-66.04832,-21.9187],[-66.03836,-21.84829],[-66.24077,-21.77837],[-66.29714,-22.08741],[-66.7298,-22.23644],[-67.18382,-22.81525],[-66.99632,-22.99839],[-67.33563,-24.04237],[-68.24825,-24.42596],[-68.56909,-24.69831],[-68.38372,-25.08636],[-68.57622,-25.32505],[-68.38372,-26.15353],[-68.56909,-26.28146],[-68.59048,-26.49861],[-68.27677,-26.90626],[-68.43363,-27.08414],[-68.77586,-27.16029],[-69.22504,-27.95042],[-69.66709,-28.44055],[-69.80969,-29.07185],[-69.99507,-29.28351],[-69.8596,-30.26131],[-70.14479,-30.36595],[-70.55832,-31.51559],[-69.88099,-33.34489],[-69.87386,-34.13344],[-70.49416,-35.24145],[-70.38008,-36.02375],[-70.95047,-36.4321],[-71.24279,-37.20264],[-70.89532,-38.6923],[-71.37826,-38.91474],[-71.92726,-40.72714],[-71.74901,-42.11711],[-72.15541,-42.15941],[-72.14828,-42.85321],[-71.64206,-43.64774],[-71.81318,-44.38097],[-71.16436,-44.46244],[-71.26418,-44.75684],[-72.06985,-44.81756],[-71.35687,-45.22075],[-71.75614,-45.61611],[-71.68577,-46.55385],[-71.94152,-47.13595],[-72.50478,-47.80586],[-72.27662,-48.28727],[-72.54042,-48.52392],[-72.56894,-48.81116],[-73.09655,-49.14342],[-73.45156,-49.79461],[-73.55259,-49.92488],[-73.15765,-50.78337],[-72.31343,-50.58411]]]]}},{type:"Feature",properties:{iso1A2:"AS",iso1A3:"ASM",iso1N3:"016",wikidata:"Q16641",nameEn:"American Samoa",country:"US",groups:["061","009"],roadSpeedUnit:"mph",callingCodes:["1 684"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]]]}},{type:"Feature",properties:{iso1A2:"AT",iso1A3:"AUT",iso1N3:"040",wikidata:"Q40",nameEn:"Austria",groups:["EU","155","150"],callingCodes:["43"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.34823,48.98444],[15.28305,48.98831],[15.26177,48.95766],[15.16358,48.94278],[15.15534,48.99056],[14.99878,49.01444],[14.97612,48.96983],[14.98917,48.90082],[14.95072,48.79101],[14.98032,48.77959],[14.9782,48.7766],[14.98112,48.77524],[14.9758,48.76857],[14.95641,48.75915],[14.94773,48.76268],[14.81545,48.7874],[14.80821,48.77711],[14.80584,48.73489],[14.72756,48.69502],[14.71794,48.59794],[14.66762,48.58215],[14.60808,48.62881],[14.56139,48.60429],[14.4587,48.64695],[14.43076,48.58855],[14.33909,48.55852],[14.20691,48.5898],[14.09104,48.5943],[14.01482,48.63788],[14.06151,48.66873],[13.84023,48.76988],[13.82266,48.75544],[13.81863,48.73257],[13.79337,48.71375],[13.81791,48.69832],[13.81283,48.68426],[13.81901,48.6761],[13.82609,48.62345],[13.80038,48.59487],[13.80519,48.58026],[13.76921,48.55324],[13.7513,48.5624],[13.74816,48.53058],[13.72802,48.51208],[13.66113,48.53558],[13.65186,48.55092],[13.62508,48.55501],[13.59705,48.57013],[13.57535,48.55912],[13.51291,48.59023],[13.50131,48.58091],[13.50663,48.57506],[13.46967,48.55157],[13.45214,48.56472],[13.43695,48.55776],[13.45727,48.51092],[13.42527,48.45711],[13.43929,48.43386],[13.40709,48.37292],[13.30897,48.31575],[13.26039,48.29422],[13.18093,48.29577],[13.126,48.27867],[13.0851,48.27711],[13.02083,48.25689],[12.95306,48.20629],[12.87126,48.20318],[12.84475,48.16556],[12.836,48.1647],[12.8362,48.15876],[12.82673,48.15245],[12.80676,48.14979],[12.78595,48.12445],[12.7617,48.12796],[12.74973,48.10885],[12.76141,48.07373],[12.8549,48.01122],[12.87476,47.96195],[12.91683,47.95647],[12.9211,47.95135],[12.91985,47.94069],[12.92668,47.93879],[12.93419,47.94063],[12.93642,47.94436],[12.93886,47.94046],[12.94163,47.92927],[13.00588,47.84374],[12.98543,47.82896],[12.96311,47.79957],[12.93202,47.77302],[12.94371,47.76281],[12.9353,47.74788],[12.91711,47.74026],[12.90274,47.72513],[12.91333,47.7178],[12.92969,47.71094],[12.98578,47.7078],[13.01382,47.72116],[13.07692,47.68814],[13.09562,47.63304],[13.06407,47.60075],[13.06641,47.58577],[13.04537,47.58183],[13.05355,47.56291],[13.03252,47.53373],[13.04537,47.49426],[12.9998,47.46267],[12.98344,47.48716],[12.9624,47.47452],[12.85256,47.52741],[12.84672,47.54556],[12.80699,47.54477],[12.77427,47.58025],[12.82101,47.61493],[12.76492,47.64485],[12.77777,47.66689],[12.7357,47.6787],[12.6071,47.6741],[12.57438,47.63238],[12.53816,47.63553],[12.50076,47.62293],[12.44117,47.6741],[12.43883,47.6977],[12.37222,47.68433],[12.336,47.69534],[12.27991,47.68827],[12.26004,47.67725],[12.24017,47.69534],[12.26238,47.73544],[12.2542,47.7433],[12.22571,47.71776],[12.18303,47.70065],[12.16217,47.70105],[12.16769,47.68167],[12.18347,47.66663],[12.18507,47.65984],[12.19895,47.64085],[12.20801,47.61082],[12.20398,47.60667],[12.18568,47.6049],[12.17737,47.60121],[12.18145,47.61019],[12.17824,47.61506],[12.13734,47.60639],[12.05788,47.61742],[12.02282,47.61033],[12.0088,47.62451],[11.85572,47.60166],[11.84052,47.58354],[11.63934,47.59202],[11.60681,47.57881],[11.58811,47.55515],[11.58578,47.52281],[11.52618,47.50939],[11.4362,47.51413],[11.38128,47.47465],[11.4175,47.44621],[11.33804,47.44937],[11.29597,47.42566],[11.27844,47.39956],[11.22002,47.3964],[11.25157,47.43277],[11.20482,47.43198],[11.12536,47.41222],[11.11835,47.39719],[10.97111,47.39561],[10.97111,47.41617],[10.98513,47.42882],[10.92437,47.46991],[10.93839,47.48018],[10.90918,47.48571],[10.87061,47.4786],[10.86945,47.5015],[10.91268,47.51334],[10.88814,47.53701],[10.77596,47.51729],[10.7596,47.53228],[10.6965,47.54253],[10.68832,47.55752],[10.63456,47.5591],[10.60337,47.56755],[10.56912,47.53584],[10.48849,47.54057],[10.47329,47.58552],[10.43473,47.58394],[10.44992,47.5524],[10.4324,47.50111],[10.44291,47.48453],[10.46278,47.47901],[10.47446,47.43318],[10.4359,47.41183],[10.4324,47.38494],[10.39851,47.37623],[10.33424,47.30813],[10.23257,47.27088],[10.17531,47.27167],[10.17648,47.29149],[10.2147,47.31014],[10.19998,47.32832],[10.23757,47.37609],[10.22774,47.38904],[10.2127,47.38019],[10.17648,47.38889],[10.16362,47.36674],[10.11805,47.37228],[10.09819,47.35724],[10.06897,47.40709],[10.1052,47.4316],[10.09001,47.46005],[10.07131,47.45531],[10.03859,47.48927],[10.00003,47.48216],[9.96029,47.53899],[9.92407,47.53111],[9.87733,47.54688],[9.87499,47.52953],[9.8189,47.54688],[9.82591,47.58158],[9.80254,47.59419],[9.76748,47.5934],[9.72736,47.53457],[9.55125,47.53629],[9.56312,47.49495],[9.58208,47.48344],[9.59482,47.46305],[9.60205,47.46165],[9.60484,47.46358],[9.60841,47.47178],[9.62158,47.45858],[9.62475,47.45685],[9.6423,47.45599],[9.65728,47.45383],[9.65863,47.44847],[9.64483,47.43842],[9.6446,47.43233],[9.65043,47.41937],[9.65136,47.40504],[9.6629,47.39591],[9.67334,47.39191],[9.67445,47.38429],[9.6711,47.37824],[9.66243,47.37136],[9.65427,47.36824],[9.62476,47.36639],[9.59978,47.34671],[9.58513,47.31334],[9.55857,47.29919],[9.54773,47.2809],[9.53116,47.27029],[9.56766,47.24281],[9.55176,47.22585],[9.56981,47.21926],[9.58264,47.20673],[9.56539,47.17124],[9.62623,47.14685],[9.63395,47.08443],[9.61216,47.07732],[9.60717,47.06091],[9.87935,47.01337],[9.88266,46.93343],[9.98058,46.91434],[10.10715,46.84296],[10.22675,46.86942],[10.24128,46.93147],[10.30031,46.92093],[10.36933,47.00212],[10.48376,46.93891],[10.47197,46.85698],[10.54783,46.84505],[10.66405,46.87614],[10.75753,46.82258],[10.72974,46.78972],[11.00764,46.76896],[11.10618,46.92966],[11.33355,46.99862],[11.50739,47.00644],[11.74789,46.98484],[12.19254,47.09331],[12.21781,47.03996],[12.11675,47.01241],[12.2006,46.88854],[12.27591,46.88651],[12.38708,46.71529],[12.59992,46.6595],[12.94445,46.60401],[13.27627,46.56059],[13.64088,46.53438],[13.7148,46.5222],[13.89837,46.52331],[14.00422,46.48474],[14.04002,46.49117],[14.12097,46.47724],[14.15989,46.43327],[14.28326,46.44315],[14.314,46.43327],[14.42608,46.44614],[14.45877,46.41717],[14.52176,46.42617],[14.56463,46.37208],[14.5942,46.43434],[14.66892,46.44936],[14.72185,46.49974],[14.81836,46.51046],[14.83549,46.56614],[14.86419,46.59411],[14.87129,46.61],[14.92283,46.60848],[14.96002,46.63459],[14.98024,46.6009],[15.01451,46.641],[15.14215,46.66131],[15.23711,46.63994],[15.41235,46.65556],[15.45514,46.63697],[15.46906,46.61321],[15.54431,46.6312],[15.55333,46.64988],[15.54533,46.66985],[15.59826,46.68908],[15.62317,46.67947],[15.63255,46.68069],[15.6365,46.6894],[15.6543,46.69228],[15.6543,46.70616],[15.67411,46.70735],[15.69523,46.69823],[15.72279,46.69548],[15.73823,46.70011],[15.76771,46.69863],[15.78518,46.70712],[15.8162,46.71897],[15.87691,46.7211],[15.94864,46.68769],[15.98512,46.68463],[15.99988,46.67947],[16.04036,46.6549],[16.04347,46.68694],[16.02808,46.71094],[15.99769,46.7266],[15.98432,46.74991],[15.99126,46.78199],[15.99054,46.82772],[16.05786,46.83927],[16.10983,46.867],[16.19904,46.94134],[16.22403,46.939],[16.27594,46.9643],[16.28202,47.00159],[16.51369,47.00084],[16.43936,47.03548],[16.52176,47.05747],[16.46134,47.09395],[16.52863,47.13974],[16.44932,47.14418],[16.46442,47.16845],[16.4523,47.18812],[16.42801,47.18422],[16.41739,47.20649],[16.43663,47.21127],[16.44142,47.25079],[16.47782,47.25918],[16.45104,47.41181],[16.49908,47.39416],[16.52414,47.41007],[16.57152,47.40868],[16.6718,47.46139],[16.64821,47.50155],[16.71059,47.52692],[16.64193,47.63114],[16.58699,47.61772],[16.4222,47.66537],[16.55129,47.72268],[16.53514,47.73837],[16.54779,47.75074],[16.61183,47.76171],[16.65679,47.74197],[16.72089,47.73469],[16.7511,47.67878],[16.82938,47.68432],[16.86509,47.72268],[16.87538,47.68895],[17.08893,47.70928],[17.05048,47.79377],[17.07039,47.81129],[17.00997,47.86245],[17.08275,47.87719],[17.11022,47.92461],[17.09786,47.97336],[17.16001,48.00636],[17.07039,48.0317],[17.09168,48.09366],[17.05735,48.14179],[17.02919,48.13996],[16.97701,48.17385],[16.89461,48.31332],[16.90903,48.32519],[16.84243,48.35258],[16.83317,48.38138],[16.83588,48.3844],[16.8497,48.38321],[16.85204,48.44968],[16.94611,48.53614],[16.93955,48.60371],[16.90354,48.71541],[16.79779,48.70998],[16.71883,48.73806],[16.68518,48.7281],[16.67008,48.77699],[16.46134,48.80865],[16.40915,48.74576],[16.37345,48.729],[16.06034,48.75436],[15.84404,48.86921],[15.78087,48.87644],[15.75341,48.8516],[15.6921,48.85973],[15.61622,48.89541],[15.51357,48.91549],[15.48027,48.94481],[15.34823,48.98444]]]]}},{type:"Feature",properties:{iso1A2:"AU",iso1A3:"AUS",iso1N3:"036",wikidata:"Q408",nameEn:"Australia",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[156.55918,-21.85134],[158.60851,-15.7108],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[127.55165,-9.05052],[96.7091,-25.20343],[159.69067,-56.28945],[165.46901,-28.32101],[156.55918,-21.85134]]]]}},{type:"Feature",properties:{iso1A2:"AW",iso1A3:"ABW",iso1N3:"533",wikidata:"Q21203",nameEn:"Aruba",country:"NL",groups:["029","003","419","019"],callingCodes:["297"]},geometry:{type:"MultiPolygon",coordinates:[[[[-70.00823,12.98375],[-70.35625,12.58277],[-69.60231,12.17],[-70.00823,12.98375]]]]}},{type:"Feature",properties:{iso1A2:"AX",iso1A3:"ALA",iso1N3:"248",wikidata:"Q5689",nameEn:"Åland Islands",country:"FI",groups:["EU","154","150"],callingCodes:["358 18","358 457"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.08191,60.19152],[20.5104,59.15546],[21.35468,59.67511],[21.02509,60.12142],[21.08159,60.20167],[21.15143,60.54555],[20.96741,60.71528],[19.23413,60.61414],[19.08191,60.19152]]]]}},{type:"Feature",properties:{iso1A2:"AZ",iso1A3:"AZE",iso1N3:"031",wikidata:"Q227",nameEn:"Azerbaijan",groups:["145","142"],callingCodes:["994"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[46.3984,41.84399],[46.30863,41.79133],[46.23962,41.75811],[46.20538,41.77205],[46.17891,41.72094],[46.19759,41.62327],[46.24429,41.59883],[46.26531,41.63339],[46.28182,41.60089],[46.3253,41.60912],[46.34039,41.5947],[46.34126,41.57454],[46.29794,41.5724],[46.33925,41.4963],[46.40307,41.48464],[46.4669,41.43331],[46.63658,41.37727],[46.72375,41.28609],[46.66148,41.20533],[46.63969,41.09515],[46.55096,41.1104],[46.48558,41.0576],[46.456,41.09984],[46.37661,41.10805],[46.27698,41.19011],[46.13221,41.19479],[45.95786,41.17956],[45.80842,41.2229],[45.69946,41.29545],[45.75705,41.35157],[45.71035,41.36208],[45.68389,41.3539],[45.45973,41.45898],[45.4006,41.42402],[45.31352,41.47168],[45.26285,41.46433],[45.1797,41.42231],[45.09867,41.34065],[45.0133,41.29747],[45.05497,41.2464],[45.02932,41.2101],[45.05201,41.19211],[45.11811,41.19967],[45.1969,41.168],[45.19942,41.13299],[45.08028,41.10917],[45.06784,41.05379],[45.12923,41.06059],[45.1313,41.09369],[45.1634,41.08082],[45.16493,41.05068],[45.1994,41.04518],[45.25897,41.0027],[45.26162,41.0228],[45.28859,41.03757],[45.35677,40.99784],[45.39725,41.02603],[45.44083,41.01663],[45.40814,40.97904],[45.60584,40.87436],[45.55914,40.78366],[45.4206,40.7424],[45.35366,40.65979],[45.45484,40.57707],[45.42994,40.53804],[45.65098,40.37696],[45.95609,40.27846],[45.97944,40.181],[45.83779,39.98925],[45.78642,40.03218],[45.59806,40.0131],[45.60895,39.97733],[45.7833,39.9475],[45.82533,39.82925],[45.96543,39.78859],[46.18493,39.60533],[46.40286,39.63651],[46.42465,39.57534],[46.52117,39.58734],[46.57098,39.56694],[46.57721,39.54414],[46.51027,39.52373],[46.53051,39.47809],[46.4013,39.45405],[46.37795,39.42039],[46.43244,39.35181],[46.50093,39.33736],[46.56476,39.24942],[46.63481,39.23013],[46.58032,39.21204],[46.54141,39.15895],[46.52584,39.18912],[46.44022,39.19636],[46.54296,39.07078],[46.51805,38.94982],[46.53497,38.86548],[46.75752,39.03231],[46.83822,39.13143],[46.92539,39.16644],[46.95341,39.13505],[47.05771,39.20143],[47.05927,39.24846],[47.31301,39.37492],[47.38978,39.45999],[47.50099,39.49615],[47.84774,39.66285],[47.98977,39.70999],[48.34264,39.42935],[48.37385,39.37584],[48.15984,39.30028],[48.12404,39.25208],[48.15361,39.19419],[48.31239,39.09278],[48.33884,39.03022],[48.28437,38.97186],[48.08627,38.94434],[48.07734,38.91616],[48.01409,38.90333],[48.02581,38.82705],[48.24773,38.71883],[48.3146,38.59958],[48.45084,38.61013],[48.58793,38.45076],[48.62217,38.40198],[48.70001,38.40564],[48.78979,38.45026],[48.81072,38.44853],[48.84969,38.45015],[48.88288,38.43975],[52.39847,39.43556],[48.80971,41.95365],[48.5867,41.84306],[48.55078,41.77917],[48.42301,41.65444],[48.40277,41.60441],[48.2878,41.56221],[48.22064,41.51472],[48.07587,41.49957],[47.87973,41.21798],[47.75831,41.19455],[47.62288,41.22969],[47.54504,41.20275],[47.49004,41.26366],[47.34579,41.27884],[47.10762,41.59044],[47.03757,41.55434],[46.99554,41.59743],[47.00955,41.63583],[46.8134,41.76252],[46.75269,41.8623],[46.58924,41.80547],[46.5332,41.87389],[46.42738,41.91323]],[[45.50279,40.58424],[45.47927,40.65023],[45.51825,40.67382],[45.56071,40.64765],[45.50279,40.58424]]],[[[45.00864,41.03411],[45.03792,41.03938],[45.04517,41.06653],[45.03406,41.07931],[45.00864,41.09407],[44.97169,41.09176],[44.95383,41.07553],[44.96031,41.06345],[44.9903,41.05657],[45.00864,41.03411]]],[[[45.21324,40.9817],[45.23095,40.97828],[45.23487,41.00226],[45.20625,41.01484],[45.18382,41.0066],[45.19312,40.98998],[45.20518,40.99348],[45.21219,40.99001],[45.21324,40.9817]]],[[[45.46992,39.49888],[45.29606,39.57654],[45.30385,39.61373],[45.23535,39.61373],[45.21784,39.58074],[45.17464,39.58614],[45.18554,39.67846],[45.06604,39.79277],[44.92869,39.72157],[44.88354,39.74432],[44.75779,39.7148],[44.80977,39.65768],[44.81043,39.62677],[44.88916,39.59653],[44.96746,39.42998],[45.05932,39.36435],[45.08751,39.35052],[45.16168,39.21952],[45.30489,39.18333],[45.40148,39.09007],[45.40452,39.07224],[45.44811,39.04927],[45.44966,38.99243],[45.6131,38.964],[45.6155,38.94304],[45.65172,38.95199],[45.83883,38.90768],[45.90266,38.87739],[45.94624,38.89072],[46.00228,38.87376],[46.06766,38.87861],[46.14785,38.84206],[46.06973,39.0744],[46.02303,39.09978],[45.99774,39.28931],[45.79225,39.3695],[45.83,39.46487],[45.80804,39.56716],[45.70547,39.60174],[45.46992,39.49888]]]]}},{type:"Feature",properties:{iso1A2:"BA",iso1A3:"BIH",iso1N3:"070",wikidata:"Q225",nameEn:"Bosnia and Herzegovina",groups:["039","150"],callingCodes:["387"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.84826,45.04489],[17.66571,45.13408],[17.59104,45.10816],[17.51469,45.10791],[17.47589,45.12656],[17.45615,45.12523],[17.4498,45.16119],[17.41229,45.13335],[17.33573,45.14521],[17.32092,45.16246],[17.26815,45.18444],[17.25131,45.14957],[17.24325,45.146],[17.18438,45.14764],[17.0415,45.20759],[16.9385,45.22742],[16.92405,45.27607],[16.83804,45.18951],[16.81137,45.18434],[16.78219,45.19002],[16.74845,45.20393],[16.64962,45.20714],[16.60194,45.23042],[16.56559,45.22307],[16.5501,45.2212],[16.52982,45.22713],[16.49155,45.21153],[16.4634,45.14522],[16.40023,45.1147],[16.38309,45.05955],[16.38219,45.05139],[16.3749,45.05206],[16.35863,45.03529],[16.35404,45.00241],[16.29036,44.99732],[16.12153,45.09616],[15.98412,45.23088],[15.83512,45.22459],[15.76371,45.16508],[15.78842,45.11519],[15.74585,45.0638],[15.78568,44.97401],[15.74723,44.96818],[15.76096,44.87045],[15.79472,44.8455],[15.72584,44.82334],[15.8255,44.71501],[15.89348,44.74964],[16.05828,44.61538],[16.00884,44.58605],[16.03012,44.55572],[16.10566,44.52586],[16.16814,44.40679],[16.12969,44.38275],[16.21346,44.35231],[16.18688,44.27012],[16.36864,44.08263],[16.43662,44.07523],[16.43629,44.02826],[16.50528,44.0244],[16.55472,43.95326],[16.70922,43.84887],[16.75316,43.77157],[16.80736,43.76011],[17.00585,43.58037],[17.15828,43.49376],[17.24411,43.49376],[17.29699,43.44542],[17.25579,43.40353],[17.286,43.33065],[17.46986,43.16559],[17.64268,43.08595],[17.70879,42.97223],[17.5392,42.92787],[17.6444,42.88641],[17.68151,42.92725],[17.7948,42.89556],[17.80854,42.9182],[17.88201,42.83668],[18.24318,42.6112],[18.36197,42.61423],[18.43735,42.55921],[18.49778,42.58409],[18.53751,42.57376],[18.55504,42.58409],[18.52232,42.62279],[18.57373,42.64429],[18.54841,42.68328],[18.54603,42.69171],[18.55221,42.69045],[18.56789,42.72074],[18.47324,42.74992],[18.45921,42.81682],[18.47633,42.85829],[18.4935,42.86433],[18.49661,42.89306],[18.49076,42.95553],[18.52232,43.01451],[18.66254,43.03928],[18.64735,43.14766],[18.66605,43.2056],[18.71747,43.2286],[18.6976,43.25243],[18.76538,43.29838],[18.85342,43.32426],[18.84794,43.33735],[18.83912,43.34795],[18.90911,43.36383],[18.95819,43.32899],[18.95001,43.29327],[19.00844,43.24988],[19.04233,43.30008],[19.08206,43.29668],[19.08673,43.31453],[19.04071,43.397],[19.01078,43.43854],[18.96053,43.45042],[18.95469,43.49367],[18.91379,43.50299],[19.01078,43.55806],[19.04934,43.50384],[19.13933,43.5282],[19.15685,43.53943],[19.22807,43.5264],[19.24774,43.53061],[19.2553,43.5938],[19.33426,43.58833],[19.36653,43.60921],[19.41941,43.54056],[19.42696,43.57987],[19.50455,43.58385],[19.5176,43.71403],[19.3986,43.79668],[19.23465,43.98764],[19.24363,44.01502],[19.38439,43.96611],[19.52515,43.95573],[19.56498,43.99922],[19.61836,44.01464],[19.61991,44.05254],[19.57467,44.04716],[19.55999,44.06894],[19.51167,44.08158],[19.47321,44.1193],[19.48386,44.14332],[19.47338,44.15034],[19.43905,44.13088],[19.40927,44.16722],[19.3588,44.18353],[19.34773,44.23244],[19.32464,44.27185],[19.26945,44.26957],[19.23306,44.26097],[19.20508,44.2917],[19.18328,44.28383],[19.16741,44.28648],[19.13332,44.31492],[19.13556,44.338],[19.11547,44.34218],[19.1083,44.3558],[19.11865,44.36712],[19.10298,44.36924],[19.10365,44.37795],[19.10704,44.38249],[19.10749,44.39421],[19.11785,44.40313],[19.14681,44.41463],[19.14837,44.45253],[19.12278,44.50132],[19.13369,44.52521],[19.16699,44.52197],[19.26388,44.65412],[19.32543,44.74058],[19.36722,44.88164],[19.18183,44.92055],[19.01994,44.85493],[18.8704,44.85097],[18.76347,44.90669],[18.76369,44.93707],[18.80661,44.93561],[18.78357,44.97741],[18.65723,45.07544],[18.47939,45.05871],[18.41896,45.11083],[18.32077,45.1021],[18.24387,45.13699],[18.1624,45.07654],[18.03121,45.12632],[18.01594,45.15163],[17.99479,45.14958],[17.97834,45.13831],[17.97336,45.12245],[17.93706,45.08016],[17.87148,45.04645],[17.84826,45.04489]]]]}},{type:"Feature",properties:{iso1A2:"BB",iso1A3:"BRB",iso1N3:"052",wikidata:"Q244",nameEn:"Barbados",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 246"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.56442,13.24471],[-59.80731,13.87556],[-60.19227,12.37597],[-58.56442,13.24471]]]]}},{type:"Feature",properties:{iso1A2:"BD",iso1A3:"BGD",iso1N3:"050",wikidata:"Q902",nameEn:"Bangladesh",groups:["034","142"],driveSide:"left",callingCodes:["880"]},geometry:{type:"MultiPolygon",coordinates:[[[[89.15869,26.13708],[89.08899,26.38845],[88.95612,26.4564],[88.92357,26.40711],[88.91321,26.37984],[89.05328,26.2469],[88.85004,26.23211],[88.78961,26.31093],[88.67837,26.26291],[88.69485,26.38353],[88.62144,26.46783],[88.4298,26.54489],[88.41196,26.63837],[88.33093,26.48929],[88.35153,26.45241],[88.36938,26.48683],[88.48749,26.45855],[88.51649,26.35923],[88.35153,26.29123],[88.34757,26.22216],[88.1844,26.14417],[88.16581,26.0238],[88.08804,25.91334],[88.13138,25.78773],[88.242,25.80811],[88.45103,25.66245],[88.4559,25.59227],[88.677,25.46959],[88.81296,25.51546],[88.85278,25.34679],[89.01105,25.30303],[89.00463,25.26583],[88.94067,25.18534],[88.44766,25.20149],[88.46277,25.07468],[88.33917,24.86803],[88.27325,24.88796],[88.21832,24.96642],[88.14004,24.93529],[88.15515,24.85806],[88.00683,24.66477],[88.08786,24.63232],[88.12296,24.51301],[88.50934,24.32474],[88.68801,24.31464],[88.74841,24.1959],[88.6976,24.14703],[88.73743,23.91751],[88.66189,23.87607],[88.58087,23.87105],[88.56507,23.64044],[88.74841,23.47361],[88.79351,23.50535],[88.79254,23.46028],[88.71133,23.2492],[88.99148,23.21134],[88.86377,23.08759],[88.88327,23.03885],[88.87063,22.95235],[88.96713,22.83346],[88.9151,22.75228],[88.94614,22.66941],[88.9367,22.58527],[89.07114,22.15335],[89.03553,21.77397],[89.13927,21.60785],[89.13606,21.42955],[92.39837,20.38919],[92.4302,20.5688],[92.31348,20.57137],[92.28464,20.63179],[92.37665,20.72172],[92.26071,21.05697],[92.17752,21.17445],[92.20087,21.337],[92.37939,21.47764],[92.43158,21.37025],[92.55105,21.3856],[92.60187,21.24615],[92.68152,21.28454],[92.59775,21.6092],[92.62187,21.87037],[92.60949,21.97638],[92.56616,22.13554],[92.60029,22.1522],[92.5181,22.71441],[92.37665,22.9435],[92.38214,23.28705],[92.26541,23.70392],[92.15417,23.73409],[92.04706,23.64229],[91.95093,23.73284],[91.95642,23.47361],[91.84789,23.42235],[91.76417,23.26619],[91.81634,23.08001],[91.7324,23.00043],[91.61571,22.93929],[91.54993,23.01051],[91.46615,23.2328],[91.4035,23.27522],[91.40848,23.07117],[91.36453,23.06612],[91.28293,23.37538],[91.15579,23.6599],[91.25192,23.83463],[91.22308,23.89616],[91.29587,24.0041],[91.35741,23.99072],[91.37414,24.10693],[91.55542,24.08687],[91.63782,24.1132],[91.65292,24.22095],[91.73257,24.14703],[91.76004,24.23848],[91.82596,24.22345],[91.89258,24.14674],[91.96603,24.3799],[92.11662,24.38997],[92.15796,24.54435],[92.25854,24.9191],[92.38626,24.86055],[92.49887,24.88796],[92.39147,25.01471],[92.33957,25.07593],[92.0316,25.1834],[91.63648,25.12846],[91.25517,25.20677],[90.87427,25.15799],[90.65042,25.17788],[90.40034,25.1534],[90.1155,25.22686],[89.90478,25.31038],[89.87629,25.28337],[89.83371,25.29548],[89.84086,25.31854],[89.81208,25.37244],[89.86129,25.61714],[89.84388,25.70042],[89.80585,25.82489],[89.86592,25.93115],[89.77728,26.04254],[89.77865,26.08387],[89.73581,26.15818],[89.70201,26.15138],[89.63968,26.22595],[89.57101,25.9682],[89.53515,26.00382],[89.35953,26.0077],[89.15869,26.13708]]]]}},{type:"Feature",properties:{iso1A2:"BE",iso1A3:"BEL",iso1N3:"056",wikidata:"Q31",nameEn:"Belgium",groups:["EU","155","150"],callingCodes:["32"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.93295,51.44945],[4.93909,51.44632],[4.9524,51.45014],[4.95244,51.45207],[4.93295,51.44945]]],[[[4.91493,51.4353],[4.92652,51.43329],[4.92952,51.42984],[4.93986,51.43064],[4.94265,51.44003],[4.93471,51.43861],[4.93416,51.44185],[4.94025,51.44193],[4.93544,51.44634],[4.92879,51.44161],[4.92815,51.43856],[4.92566,51.44273],[4.92811,51.4437],[4.92287,51.44741],[4.91811,51.44621],[4.92227,51.44252],[4.91935,51.43634],[4.91493,51.4353]]],[[[4.82946,51.4213],[4.82409,51.44736],[4.84139,51.4799],[4.78803,51.50284],[4.77321,51.50529],[4.74578,51.48937],[4.72935,51.48424],[4.65442,51.42352],[4.57489,51.4324],[4.53521,51.4243],[4.52846,51.45002],[4.54675,51.47265],[4.5388,51.48184],[4.47736,51.4778],[4.38122,51.44905],[4.39747,51.43316],[4.38064,51.41965],[4.43777,51.36989],[4.39292,51.35547],[4.34086,51.35738],[4.33265,51.37687],[4.21923,51.37443],[4.24024,51.35371],[4.16721,51.29348],[4.05165,51.24171],[4.01957,51.24504],[3.97889,51.22537],[3.90125,51.20371],[3.78783,51.2151],[3.78999,51.25766],[3.58939,51.30064],[3.51502,51.28697],[3.52698,51.2458],[3.43488,51.24135],[3.41704,51.25933],[3.38289,51.27331],[3.35847,51.31572],[3.38696,51.33436],[3.36263,51.37112],[2.56575,51.85301],[2.18458,51.52087],[2.55904,51.07014],[2.57551,51.00326],[2.63074,50.94746],[2.59093,50.91751],[2.63331,50.81457],[2.71165,50.81295],[2.81056,50.71773],[2.8483,50.72276],[2.86985,50.7033],[2.87937,50.70298],[2.88504,50.70656],[2.90069,50.69263],[2.91036,50.6939],[2.90873,50.702],[2.95019,50.75138],[2.96778,50.75242],[3.00537,50.76588],[3.04314,50.77674],[3.09163,50.77717],[3.10614,50.78303],[3.11206,50.79416],[3.11987,50.79188],[3.1257,50.78603],[3.15017,50.79031],[3.16476,50.76843],[3.18339,50.74981],[3.18811,50.74025],[3.20064,50.73547],[3.19017,50.72569],[3.20845,50.71662],[3.22042,50.71019],[3.24593,50.71389],[3.26063,50.70086],[3.26141,50.69151],[3.2536,50.68977],[3.264,50.67668],[3.23951,50.6585],[3.2729,50.60718],[3.28575,50.52724],[3.37693,50.49538],[3.44629,50.51009],[3.47385,50.53397],[3.51564,50.5256],[3.49509,50.48885],[3.5683,50.50192],[3.58361,50.49049],[3.61014,50.49568],[3.64426,50.46275],[3.66153,50.45165],[3.67494,50.40239],[3.67262,50.38663],[3.65709,50.36873],[3.66976,50.34563],[3.71009,50.30305],[3.70987,50.3191],[3.73911,50.34809],[3.84314,50.35219],[3.90781,50.32814],[3.96771,50.34989],[4.0268,50.35793],[4.0689,50.3254],[4.10237,50.31247],[4.10957,50.30234],[4.11954,50.30425],[4.13665,50.25609],[4.16808,50.25786],[4.15524,50.2833],[4.17347,50.28838],[4.17861,50.27443],[4.20651,50.27333],[4.21945,50.25539],[4.15524,50.21103],[4.16014,50.19239],[4.13561,50.13078],[4.20147,50.13535],[4.23101,50.06945],[4.16294,50.04719],[4.13508,50.01976],[4.14239,49.98034],[4.20532,49.95803],[4.31963,49.97043],[4.35051,49.95315],[4.43488,49.94122],[4.51098,49.94659],[4.5414,49.96911],[4.68695,49.99685],[4.70064,50.09384],[4.75237,50.11314],[4.82438,50.16878],[4.83279,50.15331],[4.88602,50.15182],[4.8382,50.06738],[4.78827,49.95609],[4.88529,49.9236],[4.85134,49.86457],[4.86965,49.82271],[4.85464,49.78995],[4.96714,49.79872],[5.09249,49.76193],[5.14545,49.70287],[5.26232,49.69456],[5.31465,49.66846],[5.33039,49.6555],[5.30214,49.63055],[5.3137,49.61225],[5.33851,49.61599],[5.34837,49.62889],[5.3974,49.61596],[5.43713,49.5707],[5.46734,49.52648],[5.46541,49.49825],[5.55001,49.52729],[5.60909,49.51228],[5.64505,49.55146],[5.75649,49.54321],[5.7577,49.55915],[5.77435,49.56298],[5.79195,49.55228],[5.81838,49.54777],[5.84143,49.5533],[5.84692,49.55663],[5.8424,49.56082],[5.87256,49.57539],[5.86986,49.58756],[5.84971,49.58674],[5.84826,49.5969],[5.8762,49.60898],[5.87609,49.62047],[5.88393,49.62802],[5.88552,49.63507],[5.90599,49.63853],[5.90164,49.6511],[5.9069,49.66377],[5.86175,49.67862],[5.86527,49.69291],[5.88677,49.70951],[5.86503,49.72739],[5.84193,49.72161],[5.82562,49.72395],[5.83149,49.74729],[5.82245,49.75048],[5.78871,49.7962],[5.75409,49.79239],[5.74953,49.81428],[5.74364,49.82058],[5.74844,49.82435],[5.7404,49.83452],[5.74076,49.83823],[5.74975,49.83933],[5.74953,49.84709],[5.75884,49.84811],[5.74567,49.85368],[5.75861,49.85631],[5.75269,49.8711],[5.78415,49.87922],[5.73621,49.89796],[5.77314,49.93646],[5.77291,49.96056],[5.80833,49.96451],[5.81163,49.97142],[5.83467,49.97823],[5.83968,49.9892],[5.82331,49.99662],[5.81866,50.01286],[5.8551,50.02683],[5.86904,50.04614],[5.85474,50.06342],[5.8857,50.07824],[5.89488,50.11476],[5.95929,50.13295],[5.96453,50.17259],[6.02488,50.18283],[6.03093,50.16362],[6.06406,50.15344],[6.08577,50.17246],[6.12028,50.16374],[6.1137,50.13668],[6.1379,50.12964],[6.15298,50.14126],[6.14132,50.14971],[6.14588,50.17106],[6.18739,50.1822],[6.18364,50.20815],[6.16853,50.2234],[6.208,50.25179],[6.28797,50.27458],[6.29949,50.30887],[6.32488,50.32333],[6.35701,50.31139],[6.40641,50.32425],[6.40785,50.33557],[6.3688,50.35898],[6.34406,50.37994],[6.36852,50.40776],[6.37219,50.45397],[6.34005,50.46083],[6.3465,50.48833],[6.30809,50.50058],[6.26637,50.50272],[6.22335,50.49578],[6.20599,50.52089],[6.19193,50.5212],[6.18716,50.52653],[6.19579,50.5313],[6.19735,50.53576],[6.17802,50.54179],[6.17739,50.55875],[6.20281,50.56952],[6.22581,50.5907],[6.24005,50.58732],[6.24888,50.59869],[6.2476,50.60392],[6.26957,50.62444],[6.17852,50.6245],[6.11707,50.72231],[6.04428,50.72861],[6.0406,50.71848],[6.0326,50.72647],[6.03889,50.74618],[6.01976,50.75398],[5.97545,50.75441],[5.95942,50.7622],[5.89132,50.75124],[5.89129,50.75125],[5.88734,50.77092],[5.84888,50.75448],[5.84548,50.76542],[5.80673,50.7558],[5.77513,50.78308],[5.76533,50.78159],[5.74356,50.7691],[5.73904,50.75674],[5.72216,50.76398],[5.69469,50.75529],[5.68091,50.75804],[5.70107,50.7827],[5.68995,50.79641],[5.70118,50.80764],[5.65259,50.82309],[5.64009,50.84742],[5.64504,50.87107],[5.67886,50.88142],[5.69858,50.91046],[5.71626,50.90796],[5.72644,50.91167],[5.72545,50.92312],[5.74644,50.94723],[5.75927,50.95601],[5.74752,50.96202],[5.72875,50.95428],[5.71864,50.96092],[5.76242,50.99703],[5.77688,51.02483],[5.75961,51.03113],[5.77258,51.06196],[5.79835,51.05834],[5.79903,51.09371],[5.82921,51.09328],[5.83226,51.10585],[5.8109,51.10861],[5.80798,51.11661],[5.85508,51.14445],[5.82564,51.16753],[5.77697,51.1522],[5.77735,51.17845],[5.74617,51.18928],[5.70344,51.1829],[5.65528,51.18736],[5.65145,51.19788],[5.5603,51.22249],[5.5569,51.26544],[5.515,51.29462],[5.48476,51.30053],[5.46519,51.2849],[5.4407,51.28169],[5.41672,51.26248],[5.347,51.27502],[5.33886,51.26314],[5.29716,51.26104],[5.26461,51.26693],[5.23814,51.26064],[5.22542,51.26888],[5.24244,51.30495],[5.2002,51.32243],[5.16222,51.31035],[5.13377,51.31592],[5.13105,51.34791],[5.07102,51.39469],[5.10456,51.43163],[5.07891,51.4715],[5.04774,51.47022],[5.03281,51.48679],[5.0106,51.47167],[5.00393,51.44406],[4.92152,51.39487],[4.90016,51.41404],[4.84988,51.41502],[4.78941,51.41102],[4.77229,51.41337],[4.76577,51.43046],[4.78314,51.43319],[4.82946,51.4213]]]]}},{type:"Feature",properties:{iso1A2:"BF",iso1A3:"BFA",iso1N3:"854",wikidata:"Q965",nameEn:"Burkina Faso",groups:["011","202","002"],callingCodes:["226"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.23859,15.00135],[0.06588,14.96961],[-0.24673,15.07805],[-0.72004,15.08655],[-1.05875,14.7921],[-1.32166,14.72774],[-1.68083,14.50023],[-1.97945,14.47709],[-1.9992,14.19011],[-2.10223,14.14878],[-2.47587,14.29671],[-2.66175,14.14713],[-2.84667,14.05532],[-2.90831,13.81174],[-2.88189,13.64921],[-3.26407,13.70699],[-3.28396,13.5422],[-3.23599,13.29035],[-3.43507,13.27272],[-3.4313,13.1588],[-3.54454,13.1781],[-3.7911,13.36665],[-3.96282,13.38164],[-3.90558,13.44375],[-3.96501,13.49778],[-4.34477,13.12927],[-4.21819,12.95722],[-4.238,12.71467],[-4.47356,12.71252],[-4.41412,12.31922],[-4.57703,12.19875],[-4.54841,12.1385],[-4.62546,12.13204],[-4.62987,12.06531],[-4.70692,12.06746],[-4.72893,12.01579],[-5.07897,11.97918],[-5.26389,11.84778],[-5.40258,11.8327],[-5.26389,11.75728],[-5.29251,11.61715],[-5.22867,11.60421],[-5.20665,11.43811],[-5.25509,11.36905],[-5.25949,11.24816],[-5.32553,11.21578],[-5.32994,11.13371],[-5.49284,11.07538],[-5.41579,10.84628],[-5.47083,10.75329],[-5.46643,10.56074],[-5.51058,10.43177],[-5.39602,10.2929],[-5.12465,10.29788],[-4.96453,9.99923],[-4.96621,9.89132],[-4.6426,9.70696],[-4.31392,9.60062],[-4.25999,9.76012],[-3.69703,9.94279],[-3.31779,9.91125],[-3.27228,9.84981],[-3.19306,9.93781],[-3.16609,9.85147],[-3.00765,9.74019],[-2.93012,9.57403],[-2.76494,9.40778],[-2.68802,9.49343],[-2.76534,9.56589],[-2.74174,9.83172],[-2.83108,10.40252],[-2.94232,10.64281],[-2.83373,11.0067],[-0.67143,10.99811],[-0.61937,10.91305],[-0.44298,11.04292],[-0.42391,11.11661],[-0.38219,11.12596],[-0.35955,11.07801],[-0.28566,11.12713],[-0.27374,11.17157],[-0.13493,11.14075],[0.50388,11.01011],[0.48852,10.98561],[0.50521,10.98035],[0.4958,10.93269],[0.66104,10.99964],[0.91245,10.99597],[0.9813,11.08876],[1.03409,11.04719],[1.42823,11.46822],[2.00988,11.42227],[2.29983,11.68254],[2.39723,11.89473],[2.05785,12.35539],[2.26349,12.41915],[0.99167,13.10727],[0.99253,13.37515],[1.18873,13.31771],[1.21217,13.37853],[1.24516,13.33968],[1.28509,13.35488],[1.24429,13.39373],[1.20088,13.38951],[1.02813,13.46635],[0.99514,13.5668],[0.77637,13.64442],[0.77377,13.6866],[0.61924,13.68491],[0.38051,14.05575],[0.16936,14.51654],[0.23859,15.00135]]]]}},{type:"Feature",properties:{iso1A2:"BG",iso1A3:"BGR",iso1N3:"100",wikidata:"Q219",nameEn:"Bulgaria",groups:["EU","151","150"],callingCodes:["359"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.05288,43.79494],[22.85314,43.84452],[22.83753,43.88055],[22.87873,43.9844],[23.01674,44.01946],[23.04988,44.07694],[22.67173,44.21564],[22.61711,44.16938],[22.61688,44.06534],[22.41449,44.00514],[22.35558,43.81281],[22.41043,43.69566],[22.47582,43.6558],[22.53397,43.47225],[22.82036,43.33665],[22.89727,43.22417],[23.00806,43.19279],[22.98104,43.11199],[22.89521,43.03625],[22.78397,42.98253],[22.74826,42.88701],[22.54302,42.87774],[22.43309,42.82057],[22.4997,42.74144],[22.43983,42.56851],[22.55669,42.50144],[22.51961,42.3991],[22.47498,42.3915],[22.45919,42.33822],[22.34773,42.31725],[22.38136,42.30339],[22.47251,42.20393],[22.50289,42.19527],[22.51224,42.15457],[22.67701,42.06614],[22.86749,42.02275],[22.90254,41.87587],[22.96682,41.77137],[23.01239,41.76527],[23.03342,41.71034],[22.95513,41.63265],[22.96331,41.35782],[22.93334,41.34104],[23.1833,41.31755],[23.21953,41.33773],[23.22771,41.37106],[23.31301,41.40525],[23.33639,41.36317],[23.40416,41.39999],[23.52453,41.40262],[23.63203,41.37632],[23.67644,41.41139],[23.76525,41.40175],[23.80148,41.43943],[23.89613,41.45257],[23.91483,41.47971],[23.96975,41.44118],[24.06908,41.46132],[24.06323,41.53222],[24.10063,41.54796],[24.18126,41.51735],[24.27124,41.57682],[24.30513,41.51297],[24.52599,41.56808],[24.61129,41.42278],[24.71529,41.41928],[24.8041,41.34913],[24.82514,41.4035],[24.86136,41.39298],[24.90928,41.40876],[24.942,41.38685],[25.11611,41.34212],[25.28322,41.23411],[25.48187,41.28506],[25.52394,41.2798],[25.55082,41.31667],[25.61042,41.30614],[25.66183,41.31316],[25.70507,41.29209],[25.8266,41.34563],[25.87919,41.30526],[26.12926,41.35878],[26.16548,41.42278],[26.20288,41.43943],[26.14796,41.47533],[26.176,41.50072],[26.17951,41.55409],[26.14328,41.55496],[26.15146,41.60828],[26.07083,41.64584],[26.06148,41.70345],[26.16841,41.74858],[26.21325,41.73223],[26.22888,41.74139],[26.2654,41.71544],[26.30255,41.70925],[26.35957,41.71149],[26.32952,41.73637],[26.33589,41.76802],[26.36952,41.82265],[26.53968,41.82653],[26.57961,41.90024],[26.56051,41.92995],[26.62996,41.97644],[26.79143,41.97386],[26.95638,42.00741],[27.03277,42.0809],[27.08486,42.08735],[27.19251,42.06028],[27.22376,42.10152],[27.27411,42.10409],[27.45478,41.96591],[27.52379,41.93756],[27.55191,41.90928],[27.69949,41.97515],[27.81235,41.94803],[27.83492,41.99709],[27.91479,41.97902],[28.02971,41.98066],[28.32297,41.98371],[29.24336,43.70874],[28.23293,43.76],[27.99558,43.84193],[27.92008,44.00761],[27.73468,43.95326],[27.64542,44.04958],[27.60834,44.01206],[27.39757,44.0141],[27.26845,44.12602],[26.95141,44.13555],[26.62712,44.05698],[26.38764,44.04356],[26.10115,43.96908],[26.05584,43.90925],[25.94911,43.85745],[25.72792,43.69263],[25.39528,43.61866],[25.17144,43.70261],[25.10718,43.6831],[24.96682,43.72693],[24.73542,43.68523],[24.62281,43.74082],[24.50264,43.76314],[24.35364,43.70211],[24.18149,43.68218],[23.73978,43.80627],[23.61687,43.79289],[23.4507,43.84936],[23.26772,43.84843],[23.05288,43.79494]]]]}},{type:"Feature",properties:{iso1A2:"BH",iso1A3:"BHR",iso1N3:"048",wikidata:"Q398",nameEn:"Bahrain",groups:["145","142"],callingCodes:["973"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.93865,26.30758],[50.71771,26.73086],[50.38162,26.53976],[50.26923,26.08243],[50.302,25.87592],[50.57069,25.57887],[50.80824,25.54641],[50.7801,25.595],[50.86149,25.6965],[50.81266,25.88946],[50.93865,26.30758]]]]}},{type:"Feature",properties:{iso1A2:"BI",iso1A3:"BDI",iso1N3:"108",wikidata:"Q967",nameEn:"Burundi",groups:["014","202","002"],callingCodes:["257"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.54501,-2.41404],[30.42933,-2.31064],[30.14034,-2.43626],[29.95911,-2.33348],[29.88237,-2.75105],[29.36805,-2.82933],[29.32234,-2.6483],[29.0562,-2.58632],[29.04081,-2.7416],[29.00167,-2.78523],[29.00404,-2.81978],[29.0505,-2.81774],[29.09119,-2.87871],[29.09797,-2.91935],[29.16037,-2.95457],[29.17258,-2.99385],[29.25633,-3.05471],[29.21463,-3.3514],[29.23708,-3.75856],[29.43673,-4.44845],[29.63827,-4.44681],[29.75109,-4.45836],[29.77289,-4.41733],[29.82885,-4.36153],[29.88172,-4.35743],[30.03323,-4.26631],[30.22042,-4.01738],[30.45915,-3.56532],[30.84165,-3.25152],[30.83823,-2.97837],[30.6675,-2.98987],[30.57926,-2.89791],[30.4987,-2.9573],[30.40662,-2.86151],[30.52747,-2.65841],[30.41789,-2.66266],[30.54501,-2.41404]]]]}},{type:"Feature",properties:{iso1A2:"BJ",iso1A3:"BEN",iso1N3:"204",wikidata:"Q962",nameEn:"Benin",aliases:["DY"],groups:["011","202","002"],callingCodes:["229"]},geometry:{type:"MultiPolygon",coordinates:[[[[3.59375,11.70269],[3.48187,11.86092],[3.31613,11.88495],[3.25352,12.01467],[2.83978,12.40585],[2.6593,12.30631],[2.37783,12.24804],[2.39657,12.10952],[2.45824,11.98672],[2.39723,11.89473],[2.29983,11.68254],[2.00988,11.42227],[1.42823,11.46822],[1.03409,11.04719],[0.9813,11.08876],[0.91245,10.99597],[0.8804,10.803],[0.80358,10.71459],[0.77666,10.37665],[1.35507,9.99525],[1.36624,9.5951],[1.33675,9.54765],[1.41746,9.3226],[1.5649,9.16941],[1.61838,9.0527],[1.64249,6.99562],[1.55877,6.99737],[1.61812,6.74843],[1.58105,6.68619],[1.76906,6.43189],[1.79826,6.28221],[1.62913,6.24075],[1.67336,6.02702],[2.74181,6.13349],[2.70566,6.38038],[2.70464,6.50831],[2.74334,6.57291],[2.7325,6.64057],[2.78204,6.70514],[2.78823,6.76356],[2.73405,6.78508],[2.74024,6.92802],[2.71702,6.95722],[2.76965,7.13543],[2.74489,7.42565],[2.79442,7.43486],[2.78668,7.5116],[2.73405,7.5423],[2.73095,7.7755],[2.67523,7.87825],[2.77907,9.06924],[3.08017,9.10006],[3.14147,9.28375],[3.13928,9.47167],[3.25093,9.61632],[3.34726,9.70696],[3.32099,9.78032],[3.35383,9.83641],[3.54429,9.87739],[3.66908,10.18136],[3.57275,10.27185],[3.6844,10.46351],[3.78292,10.40538],[3.84243,10.59316],[3.71505,11.13015],[3.49175,11.29765],[3.59375,11.70269]]]]}},{type:"Feature",properties:{iso1A2:"BL",iso1A3:"BLM",iso1N3:"652",wikidata:"Q25362",nameEn:"Saint-Barthélemy",country:"FR",groups:["029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.75637,18.13489],[-62.93924,18.02904],[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489]]]]}},{type:"Feature",properties:{iso1A2:"BM",iso1A3:"BMU",iso1N3:"060",wikidata:"Q23635",nameEn:"Bermuda",country:"GB",groups:["021","003","019"],driveSide:"left",callingCodes:["1 441"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.20987,32.6953],[-65.31453,32.68437],[-65.63955,31.43417],[-63.20987,32.6953]]]]}},{type:"Feature",properties:{iso1A2:"BN",iso1A3:"BRN",iso1N3:"096",wikidata:"Q921",nameEn:"Brunei",groups:["035","142"],driveSide:"left",callingCodes:["673"]},geometry:{type:"MultiPolygon",coordinates:[[[[115.16236,5.01011],[115.02521,5.35005],[114.08532,4.64632],[114.07448,4.58441],[114.15813,4.57],[114.26876,4.49878],[114.32176,4.34942],[114.32176,4.2552],[114.4416,4.27588],[114.49922,4.13108],[114.64211,4.00694],[114.78539,4.12205],[114.88039,4.4257],[114.83189,4.42387],[114.77303,4.72871],[114.8266,4.75062],[114.88841,4.81905],[114.96982,4.81146],[114.99417,4.88201],[115.05038,4.90275],[115.02955,4.82087],[115.02278,4.74137],[115.04064,4.63706],[115.07737,4.53418],[115.09978,4.39123],[115.31275,4.30806],[115.36346,4.33563],[115.2851,4.42295],[115.27819,4.63661],[115.20737,4.8256],[115.15092,4.87604],[115.16236,5.01011]]]]}},{type:"Feature",properties:{iso1A2:"BO",iso1A3:"BOL",iso1N3:"068",wikidata:"Q750",nameEn:"Bolivia",groups:["005","419","019"],callingCodes:["591"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.90248,-12.52544],[-64.22539,-12.45267],[-64.30708,-12.46398],[-64.99778,-11.98604],[-65.30027,-11.48749],[-65.28141,-10.86289],[-65.35402,-10.78685],[-65.37923,-10.35141],[-65.29019,-9.86253],[-65.40615,-9.63894],[-65.56244,-9.84266],[-65.68343,-9.75323],[-67.17784,-10.34016],[-68.71533,-11.14749],[-68.7651,-11.0496],[-68.75179,-11.03688],[-68.75265,-11.02383],[-68.74802,-11.00891],[-69.42792,-10.93451],[-69.47839,-10.95254],[-69.57156,-10.94555],[-68.98115,-11.8979],[-68.65044,-12.50689],[-68.85615,-12.87769],[-68.8864,-13.40792],[-69.05265,-13.68546],[-68.88135,-14.18639],[-69.36254,-14.94634],[-69.14856,-15.23478],[-69.40336,-15.61358],[-69.20291,-16.16668],[-69.09986,-16.22693],[-68.96238,-16.194],[-68.79464,-16.33272],[-68.98358,-16.42165],[-69.04027,-16.57214],[-69.00853,-16.66769],[-69.16896,-16.72233],[-69.62883,-17.28142],[-69.46863,-17.37466],[-69.46897,-17.4988],[-69.46623,-17.60518],[-69.34126,-17.72753],[-69.28671,-17.94844],[-69.07496,-18.03715],[-69.14807,-18.16893],[-69.07432,-18.28259],[-68.94987,-18.93302],[-68.87082,-19.06003],[-68.80602,-19.08355],[-68.61989,-19.27584],[-68.41218,-19.40499],[-68.66761,-19.72118],[-68.54611,-19.84651],[-68.57132,-20.03134],[-68.74273,-20.08817],[-68.7276,-20.46178],[-68.44023,-20.62701],[-68.55383,-20.7355],[-68.53957,-20.91542],[-68.40403,-20.94562],[-68.18816,-21.28614],[-67.85114,-22.87076],[-67.54284,-22.89771],[-67.18382,-22.81525],[-66.7298,-22.23644],[-66.29714,-22.08741],[-66.24077,-21.77837],[-66.03836,-21.84829],[-66.04832,-21.9187],[-65.9261,-21.93335],[-65.7467,-22.10105],[-65.61166,-22.09504],[-65.58694,-22.09794],[-65.57743,-22.07675],[-65.47435,-22.08908],[-64.99524,-22.08255],[-64.90014,-22.12136],[-64.67174,-22.18957],[-64.58888,-22.25035],[-64.4176,-22.67692],[-64.35108,-22.73282],[-64.31489,-22.88824],[-64.22918,-22.55807],[-63.93287,-21.99934],[-63.70963,-21.99934],[-63.68113,-22.0544],[-63.66482,-21.99918],[-62.81124,-21.9987],[-62.8078,-22.12534],[-62.64455,-22.25091],[-62.2757,-21.06657],[-62.26883,-20.55311],[-61.93912,-20.10053],[-61.73723,-19.63958],[-60.00638,-19.2981],[-59.06965,-19.29148],[-58.23216,-19.80058],[-58.16225,-20.16193],[-57.8496,-19.98346],[-58.14215,-19.76276],[-57.78463,-19.03259],[-57.71113,-19.03161],[-57.69134,-19.00544],[-57.71995,-18.97546],[-57.71995,-18.89573],[-57.76764,-18.90087],[-57.56807,-18.25655],[-57.48237,-18.24219],[-57.69877,-17.8431],[-57.73949,-17.56095],[-57.90082,-17.44555],[-57.99661,-17.5273],[-58.32935,-17.28195],[-58.5058,-16.80958],[-58.30918,-16.3699],[-58.32431,-16.25861],[-58.41506,-16.32636],[-60.16069,-16.26479],[-60.23797,-15.50267],[-60.58224,-15.09887],[-60.23968,-15.09515],[-60.27887,-14.63021],[-60.46037,-14.22496],[-60.48053,-13.77981],[-61.05527,-13.50054],[-61.81151,-13.49564],[-63.76259,-12.42952],[-63.90248,-12.52544]]]]}},{type:"Feature",properties:{iso1A2:"BQ",iso1A3:"BES",iso1N3:"535",wikidata:"Q27561",nameEn:"Caribbean Netherlands",country:"NL",groups:["029","003","419","019"],callingCodes:["599 3","599 4","599 7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.07669,17.79659],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659]]],[[[-63.29212,17.90532],[-63.58819,17.61311],[-63.22932,17.32592],[-63.07669,17.79659],[-63.29212,17.90532]]],[[[-67.89186,12.4116],[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116]]]]}},{type:"Feature",properties:{iso1A2:"BR",iso1A3:"BRA",iso1N3:"076",wikidata:"Q155",nameEn:"Brazil",groups:["005","419","019"],callingCodes:["55"]},geometry:{type:"MultiPolygon",coordinates:[[[[-59.69361,4.34069],[-59.78878,4.45637],[-60.15953,4.53456],[-60.04189,4.69801],[-59.98129,5.07097],[-60.20944,5.28754],[-60.32352,5.21299],[-60.73204,5.20931],[-60.5802,4.94312],[-60.86539,4.70512],[-60.98303,4.54167],[-61.15703,4.49839],[-61.31457,4.54167],[-61.29675,4.44216],[-61.48569,4.43149],[-61.54629,4.2822],[-62.13094,4.08309],[-62.44822,4.18621],[-62.57656,4.04754],[-62.74411,4.03331],[-62.7655,3.73099],[-62.98296,3.59935],[-63.21111,3.96219],[-63.4464,3.9693],[-63.42233,3.89995],[-63.50611,3.83592],[-63.67099,4.01731],[-63.70218,3.91417],[-63.86082,3.94796],[-63.99183,3.90172],[-64.14512,4.12932],[-64.57648,4.12576],[-64.72977,4.28931],[-64.84028,4.24665],[-64.48379,3.7879],[-64.02908,2.79797],[-64.0257,2.48156],[-63.39114,2.4317],[-63.39827,2.16098],[-64.06135,1.94722],[-64.08274,1.64792],[-64.34654,1.35569],[-64.38932,1.5125],[-65.11657,1.12046],[-65.57288,0.62856],[-65.50158,0.92086],[-65.6727,1.01353],[-66.28507,0.74585],[-66.85795,1.22998],[-67.08222,1.17441],[-67.15784,1.80439],[-67.299,1.87494],[-67.40488,2.22258],[-67.9292,1.82455],[-68.18632,2.00091],[-68.26699,1.83463],[-68.18128,1.72881],[-69.38621,1.70865],[-69.53746,1.76408],[-69.83491,1.69353],[-69.82987,1.07864],[-69.26017,1.06856],[-69.14422,0.84172],[-69.20976,0.57958],[-69.47696,0.71065],[-70.04162,0.55437],[-70.03658,-0.19681],[-69.603,-0.51947],[-69.59796,-0.75136],[-69.4215,-1.01853],[-69.43395,-1.42219],[-69.94708,-4.2431],[-70.00888,-4.37833],[-70.11305,-4.27281],[-70.19582,-4.3607],[-70.33236,-4.15214],[-70.77601,-4.15717],[-70.96814,-4.36915],[-71.87003,-4.51661],[-72.64391,-5.0391],[-72.83973,-5.14765],[-73.24579,-6.05764],[-73.12983,-6.43852],[-73.73986,-6.87919],[-73.77011,-7.28944],[-73.96938,-7.58465],[-73.65485,-7.77897],[-73.76576,-7.89884],[-72.92886,-9.04074],[-73.21498,-9.40904],[-72.72216,-9.41397],[-72.31883,-9.5184],[-72.14742,-9.98049],[-71.23394,-9.9668],[-70.53373,-9.42628],[-70.58453,-9.58303],[-70.55429,-9.76692],[-70.62487,-9.80666],[-70.64134,-11.0108],[-70.51395,-10.92249],[-70.38791,-11.07096],[-69.90896,-10.92744],[-69.57835,-10.94051],[-69.57156,-10.94555],[-69.47839,-10.95254],[-69.42792,-10.93451],[-68.74802,-11.00891],[-68.75265,-11.02383],[-68.75179,-11.03688],[-68.7651,-11.0496],[-68.71533,-11.14749],[-67.17784,-10.34016],[-65.68343,-9.75323],[-65.56244,-9.84266],[-65.40615,-9.63894],[-65.29019,-9.86253],[-65.37923,-10.35141],[-65.35402,-10.78685],[-65.28141,-10.86289],[-65.30027,-11.48749],[-64.99778,-11.98604],[-64.30708,-12.46398],[-64.22539,-12.45267],[-63.90248,-12.52544],[-63.76259,-12.42952],[-61.81151,-13.49564],[-61.05527,-13.50054],[-60.48053,-13.77981],[-60.46037,-14.22496],[-60.27887,-14.63021],[-60.23968,-15.09515],[-60.58224,-15.09887],[-60.23797,-15.50267],[-60.16069,-16.26479],[-58.41506,-16.32636],[-58.32431,-16.25861],[-58.30918,-16.3699],[-58.5058,-16.80958],[-58.32935,-17.28195],[-57.99661,-17.5273],[-57.90082,-17.44555],[-57.73949,-17.56095],[-57.69877,-17.8431],[-57.48237,-18.24219],[-57.56807,-18.25655],[-57.76764,-18.90087],[-57.71995,-18.89573],[-57.71995,-18.97546],[-57.69134,-19.00544],[-57.71113,-19.03161],[-57.78463,-19.03259],[-58.14215,-19.76276],[-57.8496,-19.98346],[-58.16225,-20.16193],[-57.84536,-20.93155],[-57.93492,-21.65505],[-57.88239,-21.6868],[-57.94642,-21.73799],[-57.98625,-22.09157],[-56.6508,-22.28387],[-56.5212,-22.11556],[-56.45893,-22.08072],[-56.23206,-22.25347],[-55.8331,-22.29008],[-55.74941,-22.46436],[-55.741,-22.52018],[-55.72366,-22.5519],[-55.6986,-22.56268],[-55.68742,-22.58407],[-55.62493,-22.62765],[-55.63849,-22.95122],[-55.5446,-23.22811],[-55.52288,-23.2595],[-55.5555,-23.28237],[-55.43585,-23.87157],[-55.44117,-23.9185],[-55.41784,-23.9657],[-55.12292,-23.99669],[-55.0518,-23.98666],[-55.02691,-23.97317],[-54.6238,-23.83078],[-54.32807,-24.01865],[-54.28207,-24.07305],[-54.4423,-25.13381],[-54.62033,-25.46026],[-54.60196,-25.48397],[-54.59509,-25.53696],[-54.59398,-25.59224],[-54.5502,-25.58915],[-54.52926,-25.62846],[-53.90831,-25.55513],[-53.83691,-25.94849],[-53.73511,-26.04211],[-53.73086,-26.05842],[-53.7264,-26.0664],[-53.73391,-26.07006],[-53.73968,-26.10012],[-53.65018,-26.19501],[-53.65237,-26.23289],[-53.63739,-26.2496],[-53.63881,-26.25075],[-53.64632,-26.24798],[-53.64186,-26.25976],[-53.64505,-26.28089],[-53.68269,-26.33359],[-53.73372,-26.6131],[-53.80144,-27.09844],[-54.15978,-27.2889],[-54.19062,-27.27639],[-54.19268,-27.30751],[-54.41888,-27.40882],[-54.50416,-27.48232],[-54.67657,-27.57214],[-54.90159,-27.63132],[-54.90805,-27.73149],[-55.1349,-27.89759],[-55.16872,-27.86224],[-55.33303,-27.94661],[-55.6262,-28.17124],[-55.65418,-28.18304],[-56.01729,-28.51223],[-56.00458,-28.60421],[-56.05265,-28.62651],[-56.54171,-29.11447],[-56.57295,-29.11357],[-56.62789,-29.18073],[-56.81251,-29.48154],[-57.09386,-29.74211],[-57.65132,-30.19229],[-57.22502,-30.26121],[-56.90236,-30.02578],[-56.49267,-30.39471],[-56.4795,-30.3899],[-56.4619,-30.38457],[-55.87388,-31.05053],[-55.58866,-30.84117],[-55.5634,-30.8686],[-55.55373,-30.8732],[-55.55218,-30.88193],[-55.54572,-30.89051],[-55.53431,-30.89714],[-55.53276,-30.90218],[-55.52712,-30.89997],[-55.51862,-30.89828],[-55.50841,-30.9027],[-55.50821,-30.91349],[-54.17384,-31.86168],[-53.76024,-32.0751],[-53.39572,-32.58596],[-53.37671,-32.57005],[-53.1111,-32.71147],[-53.53459,-33.16843],[-53.52794,-33.68908],[-53.44031,-33.69344],[-53.39593,-33.75169],[-53.37138,-33.74313],[-52.83257,-34.01481],[-28.34015,-20.99094],[-28.99601,1.86593],[-51.35485,4.8383],[-51.63798,4.51394],[-51.61983,4.14596],[-51.79599,3.89336],[-51.82312,3.85825],[-51.85573,3.83427],[-52.31787,3.17896],[-52.6906,2.37298],[-52.96539,2.1881],[-53.78743,2.34412],[-54.16286,2.10779],[-54.6084,2.32856],[-55.01919,2.564],[-55.71493,2.40342],[-55.96292,2.53188],[-56.13054,2.27723],[-55.92159,2.05236],[-55.89863,1.89861],[-55.99278,1.83137],[-56.47045,1.95135],[-56.7659,1.89509],[-57.07092,1.95304],[-57.09109,2.01854],[-57.23981,1.95808],[-57.35073,1.98327],[-57.55743,1.69605],[-57.77281,1.73344],[-57.97336,1.64566],[-58.01873,1.51966],[-58.33887,1.58014],[-58.4858,1.48399],[-58.53571,1.29154],[-58.84229,1.17749],[-58.92072,1.31293],[-59.25583,1.40559],[-59.74066,1.87596],[-59.7264,2.27497],[-59.91177,2.36759],[-59.99733,2.92312],[-59.79769,3.37162],[-59.86899,3.57089],[-59.51963,3.91951],[-59.73353,4.20399],[-59.69361,4.34069]]]]}},{type:"Feature",properties:{iso1A2:"BS",iso1A3:"BHS",iso1N3:"044",wikidata:"Q778",nameEn:"The Bahamas",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 242"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.98446,20.4801],[-71.70065,25.7637],[-79.14818,27.83105],[-79.89631,24.6597],[-80.88924,23.80416],[-72.98446,20.4801]]]]}},{type:"Feature",properties:{iso1A2:"BT",iso1A3:"BTN",iso1N3:"064",wikidata:"Q917",nameEn:"Bhutan",groups:["034","142"],driveSide:"left",callingCodes:["975"]},geometry:{type:"MultiPolygon",coordinates:[[[[91.6469,27.76358],[91.5629,27.84823],[91.48973,27.93903],[91.46327,28.0064],[91.25779,28.07509],[91.20019,27.98715],[90.69894,28.07784],[90.58842,28.02838],[90.13387,28.19178],[89.79762,28.23979],[89.59525,28.16433],[89.12825,27.62502],[89.0582,27.60985],[88.97213,27.51671],[88.95355,27.4106],[89.00216,27.32532],[88.96947,27.30319],[88.93678,27.33777],[88.91901,27.32483],[88.74219,27.144],[88.86984,27.10937],[88.8714,26.97488],[88.92301,26.99286],[88.95807,26.92668],[89.09554,26.89089],[89.12825,26.81661],[89.1926,26.81329],[89.37913,26.86224],[89.38319,26.85963],[89.3901,26.84225],[89.42349,26.83727],[89.63369,26.74402],[89.86124,26.73307],[90.04535,26.72422],[90.30402,26.85098],[90.39271,26.90704],[90.48504,26.8594],[90.67715,26.77215],[91.50067,26.79223],[91.83181,26.87318],[92.05523,26.8692],[92.11863,26.893],[92.03457,27.07334],[92.04702,27.26861],[92.12019,27.27829],[92.01132,27.47352],[91.65007,27.48287],[91.55819,27.6144],[91.6469,27.76358]]]]}},{type:"Feature",properties:{iso1A2:"BV",iso1A3:"BVT",iso1N3:"074",wikidata:"Q23408",nameEn:"Bouvet Island",country:"NO",groups:["005","419","019"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.54042,-54.0949],[2.28941,-54.13089],[3.35353,-55.17558],[4.54042,-54.0949]]]]}},{type:"Feature",properties:{iso1A2:"BW",iso1A3:"BWA",iso1N3:"072",wikidata:"Q963",nameEn:"Botswana",groups:["018","202","002"],driveSide:"left",callingCodes:["267"]},geometry:{type:"MultiPolygon",coordinates:[[[[25.26433,-17.79571],[25.16882,-17.78253],[25.05895,-17.84452],[24.95586,-17.79674],[24.73364,-17.89338],[24.71887,-17.9218],[24.6303,-17.9863],[24.57485,-18.07151],[24.40577,-17.95726],[24.19416,-18.01919],[23.61088,-18.4881],[23.29618,-17.99855],[23.0996,-18.00075],[21.45556,-18.31795],[20.99904,-18.31743],[20.99751,-22.00026],[19.99912,-21.99991],[19.99817,-24.76768],[20.02809,-24.78725],[20.03678,-24.81004],[20.29826,-24.94869],[20.64795,-25.47827],[20.86081,-26.14892],[20.61754,-26.4692],[20.63275,-26.78181],[20.68596,-26.9039],[20.87031,-26.80047],[21.13353,-26.86661],[21.37869,-26.82083],[21.69322,-26.86152],[21.7854,-26.79199],[21.77114,-26.69015],[21.83291,-26.65959],[21.90703,-26.66808],[22.06192,-26.61882],[22.21206,-26.3773],[22.41921,-26.23078],[22.56365,-26.19668],[22.70808,-25.99186],[22.86012,-25.50572],[23.03497,-25.29971],[23.47588,-25.29971],[23.9244,-25.64286],[24.18287,-25.62916],[24.36531,-25.773],[24.44703,-25.73021],[24.67319,-25.81749],[24.8946,-25.80723],[25.01718,-25.72507],[25.12266,-25.75931],[25.33076,-25.76616],[25.58543,-25.6343],[25.6643,-25.4491],[25.69661,-25.29284],[25.72702,-25.25503],[25.88571,-24.87802],[25.84295,-24.78661],[25.8515,-24.75727],[26.39409,-24.63468],[26.46346,-24.60358],[26.51667,-24.47219],[26.84165,-24.24885],[26.99749,-23.65486],[27.33768,-23.40917],[27.52393,-23.37952],[27.6066,-23.21894],[27.74154,-23.2137],[27.93539,-23.04941],[27.93729,-22.96194],[28.04752,-22.90243],[28.04562,-22.8394],[28.34874,-22.5694],[28.63287,-22.55887],[28.91889,-22.44299],[29.0151,-22.22907],[29.10881,-22.21202],[29.15268,-22.21399],[29.18974,-22.18599],[29.21955,-22.17771],[29.37703,-22.19581],[29.3533,-22.18363],[29.24648,-22.05967],[29.1974,-22.07472],[29.14501,-22.07275],[29.08495,-22.04867],[29.04108,-22.00563],[29.02191,-21.95665],[29.02191,-21.90647],[29.04023,-21.85864],[29.07763,-21.81877],[28.58114,-21.63455],[28.49942,-21.66634],[28.29416,-21.59037],[28.01669,-21.57624],[27.91407,-21.31621],[27.69171,-21.08409],[27.72972,-20.51735],[27.69361,-20.48531],[27.28865,-20.49873],[27.29831,-20.28935],[27.21278,-20.08244],[26.72246,-19.92707],[26.17227,-19.53709],[25.96226,-19.08152],[25.99837,-19.02943],[25.94326,-18.90362],[25.82353,-18.82808],[25.79217,-18.6355],[25.68859,-18.56165],[25.53465,-18.39041],[25.39972,-18.12691],[25.31799,-18.07091],[25.23909,-17.90832],[25.26433,-17.79571]]]]}},{type:"Feature",properties:{iso1A2:"BY",iso1A3:"BLR",iso1N3:"112",wikidata:"Q184",nameEn:"Belarus",groups:["151","150"],callingCodes:["375"]},geometry:{type:"MultiPolygon",coordinates:[[[[28.15217,56.16964],[27.97865,56.11849],[27.63065,55.89687],[27.61683,55.78558],[27.3541,55.8089],[27.27804,55.78299],[27.1559,55.85032],[26.97153,55.8102],[26.87448,55.7172],[26.76872,55.67658],[26.71802,55.70645],[26.64888,55.70515],[26.63231,55.67968],[26.63167,55.57887],[26.55094,55.5093],[26.5522,55.40277],[26.44937,55.34832],[26.5709,55.32572],[26.6714,55.33902],[26.80929,55.31642],[26.83266,55.30444],[26.835,55.28182],[26.73017,55.24226],[26.72983,55.21788],[26.68075,55.19787],[26.69243,55.16718],[26.54753,55.14181],[26.51481,55.16051],[26.46249,55.12814],[26.35121,55.1525],[26.30628,55.12536],[26.23202,55.10439],[26.26941,55.08032],[26.20397,54.99729],[26.13386,54.98924],[26.05907,54.94631],[25.99129,54.95705],[25.89462,54.93438],[25.74122,54.80108],[25.75977,54.57252],[25.68045,54.5321],[25.64813,54.48704],[25.62203,54.4656],[25.63371,54.42075],[25.5376,54.33158],[25.55425,54.31591],[25.68513,54.31727],[25.78553,54.23327],[25.78563,54.15747],[25.71084,54.16704],[25.64875,54.1259],[25.54724,54.14925],[25.51452,54.17799],[25.56823,54.25212],[25.509,54.30267],[25.35559,54.26544],[25.22705,54.26271],[25.19199,54.219],[25.0728,54.13419],[24.991,54.14241],[24.96894,54.17589],[24.77131,54.11091],[24.85311,54.02862],[24.74279,53.96663],[24.69185,53.96543],[24.69652,54.01901],[24.62275,54.00217],[24.44411,53.90076],[24.34128,53.90076],[24.19638,53.96405],[23.98837,53.92554],[23.95098,53.9613],[23.81309,53.94205],[23.80543,53.89558],[23.71726,53.93379],[23.61677,53.92691],[23.51284,53.95052],[23.62004,53.60942],[23.81995,53.24131],[23.85657,53.22923],[23.91393,53.16469],[23.87548,53.0831],[23.92184,53.02079],[23.94689,52.95919],[23.91805,52.94016],[23.93763,52.71332],[23.73615,52.6149],[23.58296,52.59868],[23.45112,52.53774],[23.34141,52.44845],[23.18196,52.28812],[23.20071,52.22848],[23.47859,52.18215],[23.54314,52.12148],[23.61,52.11264],[23.64066,52.07626],[23.68733,51.9906],[23.61523,51.92066],[23.62691,51.78208],[23.53198,51.74298],[23.57053,51.55938],[23.56236,51.53673],[23.62751,51.50512],[23.6736,51.50255],[23.60906,51.62122],[23.7766,51.66809],[23.91118,51.63316],[23.8741,51.59734],[23.99907,51.58369],[24.13075,51.66979],[24.3163,51.75063],[24.29021,51.80841],[24.37123,51.88222],[24.98784,51.91273],[25.20228,51.97143],[25.46163,51.92205],[25.73673,51.91973],[25.80574,51.94556],[25.83217,51.92587],[26.00408,51.92967],[26.19084,51.86781],[26.39367,51.87315],[26.46962,51.80501],[26.69759,51.82284],[26.80043,51.75777],[26.9489,51.73788],[26.99422,51.76933],[27.20602,51.77291],[27.20948,51.66713],[27.26613,51.65957],[27.24828,51.60161],[27.47212,51.61184],[27.51058,51.5854],[27.55727,51.63486],[27.71932,51.60672],[27.67125,51.50854],[27.76052,51.47604],[27.85253,51.62293],[27.91844,51.61952],[27.95827,51.56065],[28.10658,51.57857],[28.23452,51.66988],[28.37592,51.54505],[28.47051,51.59734],[28.64429,51.5664],[28.69161,51.44695],[28.73143,51.46236],[28.75615,51.41442],[28.78224,51.45294],[28.76027,51.48802],[28.81795,51.55552],[28.95528,51.59222],[28.99098,51.56833],[29.1187,51.65872],[29.16402,51.64679],[29.20659,51.56918],[29.25603,51.57089],[29.25191,51.49828],[29.32881,51.37843],[29.42357,51.4187],[29.49773,51.39814],[29.54372,51.48372],[29.7408,51.53417],[29.77376,51.4461],[30.17888,51.51025],[30.34642,51.42555],[30.36153,51.33984],[30.56203,51.25655],[30.64992,51.35014],[30.51946,51.59649],[30.68804,51.82806],[30.76443,51.89739],[30.90897,52.00699],[30.95589,52.07775],[31.13332,52.1004],[31.25142,52.04131],[31.38326,52.12991],[31.7822,52.11406],[31.77877,52.18636],[31.6895,52.1973],[31.70735,52.26711],[31.57971,52.32146],[31.62084,52.33849],[31.61397,52.48843],[31.56316,52.51518],[31.63869,52.55361],[31.50406,52.69707],[31.57277,52.71613],[31.592,52.79011],[31.35667,52.97854],[31.24147,53.031],[31.32283,53.04101],[31.33519,53.08805],[31.3915,53.09712],[31.36403,53.13504],[31.40523,53.21406],[31.56316,53.19432],[31.62496,53.22886],[31.787,53.18033],[31.82373,53.10042],[32.15368,53.07594],[32.40773,53.18856],[32.51725,53.28431],[32.73257,53.33494],[32.74968,53.45597],[32.47777,53.5548],[32.40499,53.6656],[32.50112,53.68594],[32.45717,53.74039],[32.36663,53.7166],[32.12621,53.81586],[31.89137,53.78099],[31.77028,53.80015],[31.85019,53.91801],[31.88744,54.03653],[31.89599,54.0837],[31.57002,54.14535],[31.30791,54.25315],[31.3177,54.34067],[31.22945,54.46585],[31.08543,54.50361],[31.21399,54.63113],[31.19339,54.66947],[30.99187,54.67046],[30.98226,54.68872],[31.0262,54.70698],[30.97127,54.71967],[30.95479,54.74346],[30.75165,54.80699],[30.8264,54.90062],[30.81759,54.94064],[30.93144,54.9585],[30.95754,54.98609],[30.9081,55.02232],[30.94243,55.03964],[31.00972,55.02783],[31.02071,55.06167],[30.97369,55.17134],[30.87944,55.28223],[30.81946,55.27931],[30.8257,55.3313],[30.93144,55.3914],[30.90123,55.46621],[30.95204,55.50667],[30.93419,55.6185],[30.86003,55.63169],[30.7845,55.58514],[30.72957,55.66268],[30.67464,55.64176],[30.63344,55.73079],[30.51037,55.76568],[30.51346,55.78982],[30.48257,55.81066],[30.30987,55.83592],[30.27776,55.86819],[30.12136,55.8358],[29.97975,55.87281],[29.80672,55.79569],[29.61446,55.77716],[29.51283,55.70294],[29.3604,55.75862],[29.44692,55.95978],[29.21717,55.98971],[29.08299,56.03427],[28.73418,55.97131],[28.63668,56.07262],[28.68337,56.10173],[28.5529,56.11705],[28.43068,56.09407],[28.37987,56.11399],[28.36888,56.05805],[28.30571,56.06035],[28.15217,56.16964]]]]}},{type:"Feature",properties:{iso1A2:"BZ",iso1A3:"BLZ",iso1N3:"084",wikidata:"Q242",nameEn:"Belize",groups:["013","003","419","019"],roadSpeedUnit:"mph",callingCodes:["501"]},geometry:{type:"MultiPolygon",coordinates:[[[[-88.3268,18.49048],[-88.48242,18.49164],[-88.71505,18.0707],[-88.8716,17.89535],[-89.03839,18.0067],[-89.15105,17.95104],[-89.14985,17.81563],[-89.15025,17.04813],[-89.22683,15.88619],[-89.17418,15.90898],[-89.02415,15.9063],[-88.95358,15.88698],[-88.40779,16.09624],[-86.92368,17.61462],[-87.84815,18.18511],[-87.85693,18.18266],[-87.86657,18.19971],[-87.87604,18.18313],[-87.90671,18.15213],[-88.03165,18.16657],[-88.03238,18.41778],[-88.26593,18.47617],[-88.29909,18.47591],[-88.3268,18.49048]]]]}},{type:"Feature",properties:{iso1A2:"CA",iso1A3:"CAN",iso1N3:"124",wikidata:"Q16",nameEn:"Canada",groups:["021","003","019"],callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.20349,45.1722],[-67.19603,45.16771],[-67.15965,45.16179],[-67.11316,45.11176],[-67.0216,44.95333],[-66.96824,44.90965],[-66.98249,44.87071],[-66.96824,44.83078],[-66.93432,44.82597],[-67.16117,44.20069],[-61.98255,37.34815],[-56.27503,47.39728],[-53.12387,41.40385],[-46.37635,57.3249],[-76.75614,76.72014],[-68.21821,80.48551],[-45.47832,84.58738],[-140.97446,84.39275],[-141.00116,60.30648],[-140.5227,60.22077],[-140.45648,60.30919],[-139.98024,60.18027],[-139.68991,60.33693],[-139.05831,60.35205],[-139.20603,60.08896],[-139.05365,59.99655],[-138.71149,59.90728],[-138.62145,59.76431],[-137.60623,59.24465],[-137.4925,58.89415],[-136.82619,59.16198],[-136.52365,59.16752],[-136.47323,59.46617],[-136.33727,59.44466],[-136.22381,59.55526],[-136.31566,59.59083],[-135.48007,59.79937],[-135.03069,59.56208],[-135.00267,59.28745],[-134.7047,59.2458],[-134.55699,59.1297],[-134.48059,59.13231],[-134.27175,58.8634],[-133.84645,58.73543],[-133.38523,58.42773],[-131.8271,56.62247],[-130.77769,56.36185],[-130.33965,56.10849],[-130.10173,56.12178],[-130.00093,56.00325],[-130.00857,55.91344],[-130.15373,55.74895],[-129.97513,55.28029],[-130.08035,55.21556],[-130.18765,55.07744],[-130.27203,54.97174],[-130.44184,54.85377],[-130.64499,54.76912],[-130.61931,54.70835],[-133.92876,54.62289],[-133.36909,48.51151],[-125.03842,48.53282],[-123.50039,48.21223],[-123.15614,48.35395],[-123.26565,48.6959],[-123.0093,48.76586],[-123.0093,48.83186],[-123.32163,49.00419],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-97.24024,48.99952],[-95.15355,48.9996],[-95.15357,49.384],[-95.12903,49.37056],[-95.05825,49.35311],[-95.01419,49.35647],[-94.99532,49.36579],[-94.95681,49.37035],[-94.85381,49.32492],[-94.8159,49.32299],[-94.82487,49.29483],[-94.77355,49.11998],[-94.75017,49.09931],[-94.687,48.84077],[-94.70087,48.8339],[-94.70486,48.82365],[-94.69669,48.80918],[-94.69335,48.77883],[-94.58903,48.71803],[-94.54885,48.71543],[-94.53826,48.70216],[-94.44258,48.69223],[-94.4174,48.71049],[-94.27153,48.70232],[-94.25172,48.68404],[-94.25104,48.65729],[-94.23215,48.65202],[-93.85769,48.63284],[-93.83288,48.62745],[-93.80676,48.58232],[-93.80939,48.52439],[-93.79267,48.51631],[-93.66382,48.51845],[-93.47022,48.54357],[-93.44472,48.59147],[-93.40693,48.60948],[-93.39758,48.60364],[-93.3712,48.60599],[-93.33946,48.62787],[-93.25391,48.64266],[-92.94973,48.60866],[-92.7287,48.54005],[-92.6342,48.54133],[-92.62747,48.50278],[-92.69927,48.49573],[-92.71323,48.46081],[-92.65606,48.43471],[-92.50712,48.44921],[-92.45588,48.40624],[-92.48147,48.36609],[-92.37185,48.22259],[-92.27167,48.25046],[-92.30939,48.31251],[-92.26662,48.35651],[-92.202,48.35252],[-92.14732,48.36578],[-92.05339,48.35958],[-91.98929,48.25409],[-91.86125,48.21278],[-91.71231,48.19875],[-91.70451,48.11805],[-91.55649,48.10611],[-91.58025,48.04339],[-91.45829,48.07454],[-91.43248,48.04912],[-91.25025,48.08522],[-91.08016,48.18096],[-90.87588,48.2484],[-90.75045,48.09143],[-90.56444,48.12184],[-90.56312,48.09488],[-90.07418,48.11043],[-89.89974,47.98109],[-89.77248,48.02607],[-89.57972,48.00023],[-89.48837,48.01412],[-88.37033,48.30586],[-84.85871,46.88881],[-84.55635,46.45974],[-84.47607,46.45225],[-84.4481,46.48972],[-84.42101,46.49853],[-84.34174,46.50683],[-84.29893,46.49127],[-84.26351,46.49508],[-84.2264,46.53337],[-84.1945,46.54061],[-84.17723,46.52753],[-84.12885,46.53068],[-84.11196,46.50248],[-84.13451,46.39218],[-84.11254,46.32329],[-84.11615,46.2681],[-84.09756,46.25512],[-84.1096,46.23987],[-83.95399,46.05634],[-83.90453,46.05922],[-83.83329,46.12169],[-83.57017,46.105],[-83.43746,45.99749],[-83.59589,45.82131],[-82.48419,45.30225],[-82.42469,42.992],[-82.4146,42.97626],[-82.4253,42.95423],[-82.45331,42.93139],[-82.4826,42.8068],[-82.46613,42.76615],[-82.51063,42.66025],[-82.51858,42.611],[-82.57583,42.5718],[-82.58873,42.54984],[-82.64242,42.55594],[-82.82964,42.37355],[-83.02253,42.33045],[-83.07837,42.30978],[-83.09837,42.28877],[-83.12724,42.2376],[-83.14962,42.04089],[-83.11184,41.95671],[-82.67862,41.67615],[-78.93684,42.82887],[-78.90712,42.89733],[-78.90905,42.93022],[-78.93224,42.95229],[-78.96312,42.95509],[-78.98126,42.97],[-79.02074,42.98444],[-79.02424,43.01983],[-78.99941,43.05612],[-79.01055,43.06659],[-79.07486,43.07845],[-79.05671,43.10937],[-79.06881,43.12029],[-79.0427,43.13934],[-79.04652,43.16396],[-79.05384,43.17418],[-79.05002,43.20133],[-79.05544,43.21224],[-79.05512,43.25375],[-79.06921,43.26183],[-79.25796,43.54052],[-76.79706,43.63099],[-76.43859,44.09393],[-76.35324,44.13493],[-76.31222,44.19894],[-76.244,44.19643],[-76.1664,44.23051],[-76.16285,44.28262],[-76.00018,44.34896],[-75.95947,44.34463],[-75.8217,44.43176],[-75.76813,44.51537],[-75.41441,44.76614],[-75.2193,44.87821],[-75.01363,44.95608],[-74.99101,44.98051],[-74.8447,45.00606],[-74.66689,45.00646],[-74.32699,44.99029],[-73.35025,45.00942],[-71.50067,45.01357],[-71.48735,45.07784],[-71.42778,45.12624],[-71.40364,45.21382],[-71.44252,45.2361],[-71.37133,45.24624],[-71.29371,45.29996],[-71.22338,45.25184],[-71.19723,45.25438],[-71.14568,45.24128],[-71.08364,45.30623],[-71.01866,45.31573],[-71.0107,45.34819],[-70.95193,45.33895],[-70.91169,45.29849],[-70.89864,45.2398],[-70.84816,45.22698],[-70.80236,45.37444],[-70.82638,45.39828],[-70.78372,45.43269],[-70.65383,45.37592],[-70.62518,45.42286],[-70.72651,45.49771],[-70.68516,45.56964],[-70.54019,45.67291],[-70.38934,45.73215],[-70.41523,45.79497],[-70.25976,45.89675],[-70.24694,45.95138],[-70.31025,45.96424],[-70.23855,46.1453],[-70.29078,46.18832],[-70.18547,46.35357],[-70.05812,46.41768],[-69.99966,46.69543],[-69.22119,47.46461],[-69.05148,47.42012],[-69.05073,47.30076],[-69.05039,47.2456],[-68.89222,47.1807],[-68.70125,47.24399],[-68.60575,47.24659],[-68.57914,47.28431],[-68.38332,47.28723],[-68.37458,47.35851],[-68.23244,47.35712],[-67.94843,47.1925],[-67.87993,47.10377],[-67.78578,47.06473],[-67.78111,45.9392],[-67.75196,45.91814],[-67.80961,45.87531],[-67.75654,45.82324],[-67.80653,45.80022],[-67.80705,45.69528],[-67.6049,45.60725],[-67.43815,45.59162],[-67.42144,45.50584],[-67.50578,45.48971],[-67.42394,45.37969],[-67.48201,45.27351],[-67.34927,45.122],[-67.29754,45.14865],[-67.29748,45.18173],[-67.27039,45.1934],[-67.22751,45.16344],[-67.20349,45.1722]]]]}},{type:"Feature",properties:{iso1A2:"CC",iso1A3:"CCK",iso1N3:"166",wikidata:"Q36004",nameEn:"Cocos (Keeling) Islands",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[96.61846,-10.82438],[96.02343,-12.68334],[97.93979,-12.33309],[96.61846,-10.82438]]]]}},{type:"Feature",properties:{iso1A2:"CD",iso1A3:"COD",iso1N3:"180",wikidata:"Q974",nameEn:"Democratic Republic of the Congo",aliases:["ZR"],groups:["017","202","002"],callingCodes:["243"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.44012,5.07349],[27.09575,5.22305],[26.93064,5.13535],[26.85579,5.03887],[26.74572,5.10685],[26.48595,5.04984],[26.13371,5.25594],[25.86073,5.19455],[25.53271,5.37431],[25.34558,5.29101],[25.31256,5.03668],[24.71816,4.90509],[24.46719,5.0915],[23.38847,4.60013],[22.94817,4.82392],[22.89094,4.79321],[22.84691,4.69887],[22.78526,4.71423],[22.6928,4.47285],[22.60915,4.48821],[22.5431,4.22041],[22.45504,4.13039],[22.27682,4.11347],[22.10721,4.20723],[21.6405,4.317],[21.55904,4.25553],[21.25744,4.33676],[21.21341,4.29285],[21.11214,4.33895],[21.08793,4.39603],[20.90383,4.44877],[20.60184,4.42394],[18.62755,3.47564],[18.63857,3.19342],[18.10683,2.26876],[18.08034,1.58553],[17.85887,1.04327],[17.86989,0.58873],[17.95255,0.48128],[17.93877,0.32424],[17.81204,0.23884],[17.66051,-0.26535],[17.72112,-0.52707],[17.32438,-0.99265],[16.97999,-1.12762],[16.70724,-1.45815],[16.50336,-1.8795],[16.16173,-2.16586],[16.22785,-2.59528],[16.1755,-3.25014],[16.21407,-3.2969],[15.89448,-3.9513],[15.53081,-4.042],[15.48121,-4.22062],[15.41785,-4.28381],[15.32693,-4.27282],[15.25411,-4.31121],[15.1978,-4.32388],[14.83101,-4.80838],[14.67948,-4.92093],[14.5059,-4.84956],[14.41499,-4.8825],[14.37366,-4.56125],[14.47284,-4.42941],[14.3957,-4.36623],[14.40672,-4.28381],[13.9108,-4.50906],[13.81162,-4.41842],[13.71794,-4.44864],[13.70417,-4.72601],[13.50305,-4.77818],[13.41764,-4.89897],[13.11182,-4.5942],[13.09648,-4.63739],[13.11195,-4.67745],[12.8733,-4.74346],[12.70868,-4.95505],[12.63465,-4.94632],[12.60251,-5.01715],[12.46297,-5.09408],[12.49815,-5.14058],[12.51589,-5.1332],[12.53586,-5.14658],[12.53599,-5.1618],[12.52301,-5.17481],[12.52318,-5.74353],[12.26557,-5.74031],[12.20376,-5.76338],[11.95767,-5.94705],[12.42245,-6.07585],[13.04371,-5.87078],[16.55507,-5.85631],[16.96282,-7.21787],[17.5828,-8.13784],[18.33635,-8.00126],[19.33698,-7.99743],[19.5469,-7.00195],[20.30218,-6.98955],[20.31846,-6.91953],[20.61689,-6.90876],[20.56263,-7.28566],[21.79824,-7.29628],[21.84856,-9.59871],[22.19039,-9.94628],[22.32604,-10.76291],[22.17954,-10.85884],[22.25951,-11.24911],[22.54205,-11.05784],[23.16602,-11.10577],[23.45631,-10.946],[23.86868,-11.02856],[24.00027,-10.89356],[24.34528,-11.06816],[24.42612,-11.44975],[25.34069,-11.19707],[25.33058,-11.65767],[26.01777,-11.91488],[26.88687,-12.01868],[27.04351,-11.61312],[27.22541,-11.60323],[27.21025,-11.76157],[27.59932,-12.22123],[28.33199,-12.41375],[29.01918,-13.41353],[29.60531,-13.21685],[29.65078,-13.41844],[29.81551,-13.44683],[29.8139,-12.14898],[29.48404,-12.23604],[29.4992,-12.43843],[29.18592,-12.37921],[28.48357,-11.87532],[28.37241,-11.57848],[28.65032,-10.65133],[28.62795,-9.92942],[28.68532,-9.78],[28.56208,-9.49122],[28.51627,-9.44726],[28.52636,-9.35379],[28.36562,-9.30091],[28.38526,-9.23393],[28.9711,-8.66935],[28.88917,-8.4831],[30.79243,-8.27382],[30.2567,-7.14121],[29.52552,-6.2731],[29.43673,-4.44845],[29.23708,-3.75856],[29.21463,-3.3514],[29.25633,-3.05471],[29.17258,-2.99385],[29.16037,-2.95457],[29.09797,-2.91935],[29.09119,-2.87871],[29.0505,-2.81774],[29.00404,-2.81978],[29.00167,-2.78523],[29.04081,-2.7416],[29.00357,-2.70596],[28.94346,-2.69124],[28.89793,-2.66111],[28.90226,-2.62385],[28.89288,-2.55848],[28.87943,-2.55165],[28.86193,-2.53185],[28.86209,-2.5231],[28.87497,-2.50887],[28.88846,-2.50493],[28.89342,-2.49017],[28.89132,-2.47557],[28.86846,-2.44866],[28.86826,-2.41888],[28.89601,-2.37321],[28.95642,-2.37321],[29.00051,-2.29001],[29.105,-2.27043],[29.17562,-2.12278],[29.11847,-1.90576],[29.24458,-1.69663],[29.24323,-1.66826],[29.36322,-1.50887],[29.45038,-1.5054],[29.53062,-1.40499],[29.59061,-1.39016],[29.58388,-0.89821],[29.63006,-0.8997],[29.62708,-0.71055],[29.67176,-0.55714],[29.67474,-0.47969],[29.65091,-0.46777],[29.72687,-0.08051],[29.7224,0.07291],[29.77454,0.16675],[29.81922,0.16824],[29.87284,0.39166],[29.97413,0.52124],[29.95477,0.64486],[29.98307,0.84295],[30.1484,0.89805],[30.22139,0.99635],[30.24671,1.14974],[30.48503,1.21675],[31.30127,2.11006],[31.28042,2.17853],[31.20148,2.2217],[31.1985,2.29462],[31.12104,2.27676],[31.07934,2.30207],[31.06593,2.35862],[30.96911,2.41071],[30.91102,2.33332],[30.83059,2.42559],[30.74271,2.43601],[30.75612,2.5863],[30.8857,2.83923],[30.8574,2.9508],[30.77101,3.04897],[30.84251,3.26908],[30.93486,3.40737],[30.94081,3.50847],[30.85153,3.48867],[30.85997,3.5743],[30.80713,3.60506],[30.78512,3.67097],[30.56277,3.62703],[30.57378,3.74567],[30.55396,3.84451],[30.47691,3.83353],[30.27658,3.95653],[30.22374,3.93896],[30.1621,4.10586],[30.06964,4.13221],[29.79666,4.37809],[29.82087,4.56246],[29.49726,4.7007],[29.43341,4.50101],[29.22207,4.34297],[29.03054,4.48784],[28.8126,4.48784],[28.6651,4.42638],[28.20719,4.35614],[27.79551,4.59976],[27.76469,4.79284],[27.65462,4.89375],[27.56656,4.89375],[27.44012,5.07349]]]]}},{type:"Feature",properties:{iso1A2:"CF",iso1A3:"CAF",iso1N3:"140",wikidata:"Q929",nameEn:"Central African Republic",groups:["017","202","002"],callingCodes:["236"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.87758,10.91915],[22.45889,11.00246],[21.72139,10.64136],[21.71479,10.29932],[21.63553,10.217],[21.52766,10.2105],[21.34934,9.95907],[21.26348,9.97642],[20.82979,9.44696],[20.36748,9.11019],[19.06421,9.00367],[18.86388,8.87971],[19.11044,8.68172],[18.79783,8.25929],[18.67455,8.22226],[18.62612,8.14163],[18.64153,8.08714],[18.6085,8.05009],[18.02731,8.01085],[17.93926,7.95853],[17.67288,7.98905],[16.8143,7.53971],[16.6668,7.67281],[16.658,7.75353],[16.59415,7.76444],[16.58315,7.88657],[16.41583,7.77971],[16.40703,7.68809],[15.79942,7.44149],[15.73118,7.52006],[15.49743,7.52179],[15.23397,7.25135],[15.04717,6.77085],[14.96311,6.75693],[14.79966,6.39043],[14.80122,6.34866],[14.74206,6.26356],[14.56149,6.18928],[14.43073,6.08867],[14.42917,6.00508],[14.49455,5.91683],[14.60974,5.91838],[14.62375,5.70466],[14.58951,5.59777],[14.62531,5.51411],[14.52724,5.28319],[14.57083,5.23979],[14.65489,5.21343],[14.73383,4.6135],[15.00825,4.41458],[15.08609,4.30282],[15.10644,4.1362],[15.17482,4.05131],[15.07686,4.01805],[15.73522,3.24348],[15.77725,3.26835],[16.05449,3.02306],[16.08252,2.45708],[16.19357,2.21537],[16.50126,2.84739],[16.46701,2.92512],[16.57598,3.47999],[16.68283,3.54257],[17.01746,3.55136],[17.35649,3.63045],[17.46876,3.70515],[17.60966,3.63705],[17.83421,3.61068],[17.85842,3.53378],[18.05656,3.56893],[18.14902,3.54476],[18.17323,3.47665],[18.24148,3.50302],[18.2723,3.57992],[18.39558,3.58212],[18.49245,3.63924],[18.58711,3.49423],[18.62755,3.47564],[20.60184,4.42394],[20.90383,4.44877],[21.08793,4.39603],[21.11214,4.33895],[21.21341,4.29285],[21.25744,4.33676],[21.55904,4.25553],[21.6405,4.317],[22.10721,4.20723],[22.27682,4.11347],[22.45504,4.13039],[22.5431,4.22041],[22.60915,4.48821],[22.6928,4.47285],[22.78526,4.71423],[22.84691,4.69887],[22.89094,4.79321],[22.94817,4.82392],[23.38847,4.60013],[24.46719,5.0915],[24.71816,4.90509],[25.31256,5.03668],[25.34558,5.29101],[25.53271,5.37431],[25.86073,5.19455],[26.13371,5.25594],[26.48595,5.04984],[26.74572,5.10685],[26.85579,5.03887],[26.93064,5.13535],[27.09575,5.22305],[27.44012,5.07349],[27.26886,5.25876],[27.23017,5.37167],[27.28621,5.56382],[27.22705,5.62889],[27.22705,5.71254],[26.51721,6.09655],[26.58259,6.1987],[26.32729,6.36272],[26.38022,6.63493],[25.90076,7.09549],[25.37461,7.33024],[25.35281,7.42595],[25.20337,7.50312],[25.20649,7.61115],[25.29214,7.66675],[25.25319,7.8487],[24.98855,7.96588],[24.85156,8.16933],[24.35965,8.26177],[24.13238,8.36959],[24.25691,8.69288],[23.51905,8.71749],[23.59065,8.99743],[23.44744,8.99128],[23.4848,9.16959],[23.56263,9.19418],[23.64358,9.28637],[23.64981,9.44303],[23.62179,9.53823],[23.69155,9.67566],[23.67164,9.86923],[23.3128,10.45214],[23.02221,10.69235],[22.87758,10.91915]]]]}},{type:"Feature",properties:{iso1A2:"CG",iso1A3:"COG",iso1N3:"178",wikidata:"Q971",nameEn:"Republic of the Congo",groups:["017","202","002"],callingCodes:["242"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.62755,3.47564],[18.58711,3.49423],[18.49245,3.63924],[18.39558,3.58212],[18.2723,3.57992],[18.24148,3.50302],[18.17323,3.47665],[18.14902,3.54476],[18.05656,3.56893],[17.85842,3.53378],[17.83421,3.61068],[17.60966,3.63705],[17.46876,3.70515],[17.35649,3.63045],[17.01746,3.55136],[16.68283,3.54257],[16.57598,3.47999],[16.46701,2.92512],[16.50126,2.84739],[16.19357,2.21537],[16.15568,2.18955],[16.08563,2.19733],[16.05294,1.9811],[16.14634,1.70259],[16.02647,1.65591],[16.02959,1.76483],[15.48942,1.98265],[15.34776,1.91264],[15.22634,2.03243],[15.00996,1.98887],[14.61145,2.17866],[13.29457,2.16106],[13.13461,1.57238],[13.25447,1.32339],[13.15519,1.23368],[13.89582,1.4261],[14.25186,1.39842],[14.48179,0.9152],[14.26066,0.57255],[14.10909,0.58563],[13.88648,0.26652],[13.90632,-0.2287],[14.06862,-0.20826],[14.2165,-0.38261],[14.41887,-0.44799],[14.52569,-0.57818],[14.41838,-1.89412],[14.25932,-1.97624],[14.23518,-2.15671],[14.16202,-2.23916],[14.23829,-2.33715],[14.10442,-2.49268],[13.85846,-2.46935],[13.92073,-2.35581],[13.75884,-2.09293],[13.47977,-2.43224],[13.02759,-2.33098],[12.82172,-1.91091],[12.61312,-1.8129],[12.44656,-1.92025],[12.47925,-2.32626],[12.04895,-2.41704],[11.96866,-2.33559],[11.74605,-2.39936],[11.57637,-2.33379],[11.64487,-2.61865],[11.5359,-2.85654],[11.64798,-2.81146],[11.80365,-3.00424],[11.70558,-3.0773],[11.70227,-3.17465],[11.96554,-3.30267],[11.8318,-3.5812],[11.92719,-3.62768],[11.87083,-3.71571],[11.68608,-3.68942],[11.57949,-3.52798],[11.48764,-3.51089],[11.22301,-3.69888],[11.12647,-3.94169],[10.75913,-4.39519],[11.50888,-5.33417],[12.00924,-5.02627],[12.16068,-4.90089],[12.20901,-4.75642],[12.25587,-4.79437],[12.32324,-4.78415],[12.40964,-4.60609],[12.64835,-4.55937],[12.76844,-4.38709],[12.87096,-4.40315],[12.91489,-4.47907],[13.09648,-4.63739],[13.11182,-4.5942],[13.41764,-4.89897],[13.50305,-4.77818],[13.70417,-4.72601],[13.71794,-4.44864],[13.81162,-4.41842],[13.9108,-4.50906],[14.40672,-4.28381],[14.3957,-4.36623],[14.47284,-4.42941],[14.37366,-4.56125],[14.41499,-4.8825],[14.5059,-4.84956],[14.67948,-4.92093],[14.83101,-4.80838],[15.1978,-4.32388],[15.25411,-4.31121],[15.32693,-4.27282],[15.41785,-4.28381],[15.48121,-4.22062],[15.53081,-4.042],[15.89448,-3.9513],[16.21407,-3.2969],[16.1755,-3.25014],[16.22785,-2.59528],[16.16173,-2.16586],[16.50336,-1.8795],[16.70724,-1.45815],[16.97999,-1.12762],[17.32438,-0.99265],[17.72112,-0.52707],[17.66051,-0.26535],[17.81204,0.23884],[17.93877,0.32424],[17.95255,0.48128],[17.86989,0.58873],[17.85887,1.04327],[18.08034,1.58553],[18.10683,2.26876],[18.63857,3.19342],[18.62755,3.47564]]]]}},{type:"Feature",properties:{iso1A2:"CH",iso1A3:"CHE",iso1N3:"756",wikidata:"Q39",nameEn:"Switzerland",groups:["155","150"],callingCodes:["41"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.72809,47.69282],[8.72617,47.69651],[8.73671,47.7169],[8.70543,47.73121],[8.74251,47.75168],[8.71778,47.76571],[8.68985,47.75686],[8.68022,47.78599],[8.65292,47.80066],[8.64425,47.76398],[8.62408,47.7626],[8.61657,47.79998],[8.56415,47.80633],[8.56814,47.78001],[8.48868,47.77215],[8.45771,47.7493],[8.44807,47.72426],[8.40569,47.69855],[8.4211,47.68407],[8.40473,47.67499],[8.41346,47.66676],[8.42264,47.66667],[8.44711,47.65379],[8.4667,47.65747],[8.46605,47.64103],[8.49656,47.64709],[8.5322,47.64687],[8.52801,47.66059],[8.56141,47.67088],[8.57683,47.66158],[8.6052,47.67258],[8.61113,47.66332],[8.62884,47.65098],[8.62049,47.63757],[8.60412,47.63735],[8.61471,47.64514],[8.60701,47.65271],[8.59545,47.64298],[8.60348,47.61204],[8.57586,47.59537],[8.55756,47.62394],[8.51686,47.63476],[8.50747,47.61897],[8.45578,47.60121],[8.46637,47.58389],[8.48949,47.588],[8.49431,47.58107],[8.43235,47.56617],[8.39477,47.57826],[8.38273,47.56608],[8.32735,47.57133],[8.30277,47.58607],[8.29524,47.5919],[8.29722,47.60603],[8.2824,47.61225],[8.26313,47.6103],[8.25863,47.61571],[8.23809,47.61204],[8.22577,47.60385],[8.22011,47.6181],[8.20617,47.62141],[8.19378,47.61636],[8.1652,47.5945],[8.14947,47.59558],[8.13823,47.59147],[8.13662,47.58432],[8.11543,47.5841],[8.10395,47.57918],[8.10002,47.56504],[8.08557,47.55768],[8.06663,47.56374],[8.04383,47.55443],[8.02136,47.55096],[8.00113,47.55616],[7.97581,47.55493],[7.95682,47.55789],[7.94494,47.54511],[7.91251,47.55031],[7.90673,47.57674],[7.88664,47.58854],[7.84412,47.5841],[7.81901,47.58798],[7.79486,47.55691],[7.75261,47.54599],[7.71961,47.54219],[7.69642,47.53297],[7.68101,47.53232],[7.6656,47.53752],[7.66174,47.54554],[7.65083,47.54662],[7.63338,47.56256],[7.67655,47.56435],[7.68904,47.57133],[7.67115,47.5871],[7.68486,47.59601],[7.69385,47.60099],[7.68229,47.59905],[7.67395,47.59212],[7.64599,47.59695],[7.64213,47.5944],[7.64309,47.59151],[7.61929,47.57683],[7.60459,47.57869],[7.60523,47.58519],[7.58945,47.59017],[7.58386,47.57536],[7.56684,47.57785],[7.56548,47.57617],[7.55689,47.57232],[7.55652,47.56779],[7.53634,47.55553],[7.52831,47.55347],[7.51723,47.54578],[7.50873,47.54546],[7.49691,47.53821],[7.50588,47.52856],[7.51904,47.53515],[7.53199,47.5284],[7.5229,47.51644],[7.49804,47.51798],[7.51076,47.49651],[7.47534,47.47932],[7.43356,47.49712],[7.42923,47.48628],[7.4583,47.47216],[7.4462,47.46264],[7.43088,47.45846],[7.40308,47.43638],[7.35603,47.43432],[7.33526,47.44186],[7.24669,47.4205],[7.17026,47.44312],[7.19583,47.49455],[7.16249,47.49025],[7.12781,47.50371],[7.07425,47.48863],[7.0231,47.50522],[6.98425,47.49432],[7.0024,47.45264],[6.93953,47.43388],[6.93744,47.40714],[6.88542,47.37262],[6.87959,47.35335],[7.03125,47.36996],[7.0564,47.35134],[7.05305,47.33304],[6.94316,47.28747],[6.95108,47.26428],[6.9508,47.24338],[6.8489,47.15933],[6.76788,47.1208],[6.68823,47.06616],[6.71531,47.0494],[6.43341,46.92703],[6.46456,46.88865],[6.43216,46.80336],[6.45209,46.77502],[6.38351,46.73171],[6.27135,46.68251],[6.11084,46.57649],[6.1567,46.54402],[6.07269,46.46244],[6.08427,46.44305],[6.06407,46.41676],[6.09926,46.40768],[6.15016,46.3778],[6.15985,46.37721],[6.16987,46.36759],[6.15738,46.3491],[6.13876,46.33844],[6.1198,46.31157],[6.11697,46.29547],[6.1013,46.28512],[6.11926,46.2634],[6.12446,46.25059],[6.10071,46.23772],[6.08563,46.24651],[6.07072,46.24085],[6.0633,46.24583],[6.05029,46.23518],[6.04602,46.23127],[6.03342,46.2383],[6.02461,46.23313],[5.97542,46.21525],[5.96515,46.19638],[5.99573,46.18587],[5.98846,46.17046],[5.98188,46.17392],[5.97508,46.15863],[5.9641,46.14412],[5.95781,46.12925],[5.97893,46.13303],[5.9871,46.14499],[6.01791,46.14228],[6.03614,46.13712],[6.04564,46.14031],[6.05203,46.15191],[6.07491,46.14879],[6.09199,46.15191],[6.09926,46.14373],[6.13397,46.1406],[6.15305,46.15194],[6.18116,46.16187],[6.18871,46.16644],[6.18707,46.17999],[6.19552,46.18401],[6.19807,46.18369],[6.20539,46.19163],[6.21114,46.1927],[6.21273,46.19409],[6.21603,46.19507],[6.21844,46.19837],[6.22222,46.19888],[6.22175,46.20045],[6.23544,46.20714],[6.23913,46.20511],[6.24821,46.20531],[6.26007,46.21165],[6.27694,46.21566],[6.29663,46.22688],[6.31041,46.24417],[6.29474,46.26221],[6.26749,46.24745],[6.24952,46.26255],[6.23775,46.27822],[6.25137,46.29014],[6.24826,46.30175],[6.21981,46.31304],[6.25432,46.3632],[6.53358,46.45431],[6.82312,46.42661],[6.8024,46.39171],[6.77152,46.34784],[6.86052,46.28512],[6.78968,46.14058],[6.89321,46.12548],[6.87868,46.03855],[6.93862,46.06502],[7.00946,45.9944],[7.04151,45.92435],[7.10685,45.85653],[7.56343,45.97421],[7.85949,45.91485],[7.9049,45.99945],[7.98881,45.99867],[8.02906,46.10331],[8.11383,46.11577],[8.16866,46.17817],[8.08814,46.26692],[8.31162,46.38044],[8.30648,46.41587],[8.42464,46.46367],[8.46317,46.43712],[8.45032,46.26869],[8.62242,46.12112],[8.75697,46.10395],[8.80778,46.10085],[8.85617,46.0748],[8.79414,46.00913],[8.78585,45.98973],[8.79362,45.99207],[8.8319,45.9879],[8.85121,45.97239],[8.86688,45.96135],[8.88904,45.95465],[8.93649,45.86775],[8.94372,45.86587],[8.93504,45.86245],[8.91129,45.8388],[8.94737,45.84285],[8.9621,45.83707],[8.99663,45.83466],[9.00324,45.82055],[9.0298,45.82127],[9.03279,45.82865],[9.03793,45.83548],[9.03505,45.83976],[9.04059,45.8464],[9.04546,45.84968],[9.06642,45.8761],[9.09065,45.89906],[8.99257,45.9698],[9.01618,46.04928],[9.24503,46.23616],[9.29226,46.32717],[9.25502,46.43743],[9.28136,46.49685],[9.36128,46.5081],[9.40487,46.46621],[9.45936,46.50873],[9.46117,46.37481],[9.57015,46.2958],[9.71273,46.29266],[9.73086,46.35071],[9.95249,46.38045],[10.07055,46.21668],[10.14439,46.22992],[10.17862,46.25626],[10.10506,46.3372],[10.165,46.41051],[10.03715,46.44479],[10.10307,46.61003],[10.23674,46.63484],[10.25309,46.57432],[10.46136,46.53164],[10.49375,46.62049],[10.44686,46.64162],[10.40475,46.63671],[10.38659,46.67847],[10.47197,46.85698],[10.48376,46.93891],[10.36933,47.00212],[10.30031,46.92093],[10.24128,46.93147],[10.22675,46.86942],[10.10715,46.84296],[9.98058,46.91434],[9.88266,46.93343],[9.87935,47.01337],[9.60717,47.06091],[9.55721,47.04762],[9.54041,47.06495],[9.47548,47.05257],[9.47139,47.06402],[9.51362,47.08505],[9.52089,47.10019],[9.51044,47.13727],[9.48774,47.17402],[9.4891,47.19346],[9.50318,47.22153],[9.52406,47.24959],[9.53116,47.27029],[9.54773,47.2809],[9.55857,47.29919],[9.58513,47.31334],[9.59978,47.34671],[9.62476,47.36639],[9.65427,47.36824],[9.66243,47.37136],[9.6711,47.37824],[9.67445,47.38429],[9.67334,47.39191],[9.6629,47.39591],[9.65136,47.40504],[9.65043,47.41937],[9.6446,47.43233],[9.64483,47.43842],[9.65863,47.44847],[9.65728,47.45383],[9.6423,47.45599],[9.62475,47.45685],[9.62158,47.45858],[9.60841,47.47178],[9.60484,47.46358],[9.60205,47.46165],[9.59482,47.46305],[9.58208,47.48344],[9.56312,47.49495],[9.55125,47.53629],[9.25619,47.65939],[9.18203,47.65598],[9.17593,47.65399],[9.1755,47.65584],[9.1705,47.65513],[9.15181,47.66904],[9.13845,47.66389],[9.09891,47.67801],[9.02093,47.6868],[8.94093,47.65596],[8.89946,47.64769],[8.87625,47.65441],[8.87383,47.67045],[8.85065,47.68209],[8.86989,47.70504],[8.82002,47.71458],[8.80663,47.73821],[8.77309,47.72059],[8.76965,47.7075],[8.79966,47.70222],[8.79511,47.67462],[8.75856,47.68969],[8.72809,47.69282]],[[8.95861,45.96485],[8.96668,45.98436],[8.97741,45.98317],[8.97604,45.96151],[8.95861,45.96485]],[[8.70847,47.68904],[8.68985,47.69552],[8.66837,47.68437],[8.65769,47.68928],[8.67508,47.6979],[8.66416,47.71367],[8.70237,47.71453],[8.71773,47.69088],[8.70847,47.68904]]]]}},{type:"Feature",properties:{iso1A2:"CI",iso1A3:"CIV",iso1N3:"384",wikidata:"Q1008",nameEn:"Côte d'Ivoire",groups:["011","202","002"],callingCodes:["225"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.52774,3.7105],[-3.34019,4.17519],[-3.10675,5.08515],[-3.11073,5.12675],[-3.063,5.13665],[-2.96554,5.10397],[-2.95261,5.12477],[-2.75502,5.10657],[-2.73074,5.1364],[-2.77625,5.34621],[-2.72737,5.34789],[-2.76614,5.60963],[-2.85378,5.65156],[-2.93132,5.62137],[-2.96671,5.6415],[-2.95323,5.71865],[-3.01896,5.71697],[-3.25999,6.62521],[-3.21954,6.74407],[-3.23327,6.81744],[-2.95438,7.23737],[-2.97822,7.27165],[-2.92339,7.60847],[-2.79467,7.86002],[-2.78395,7.94974],[-2.74819,7.92613],[-2.67787,8.02055],[-2.61232,8.02645],[-2.62901,8.11495],[-2.49037,8.20872],[-2.58243,8.7789],[-2.66357,9.01771],[-2.77799,9.04949],[-2.69814,9.22717],[-2.68802,9.49343],[-2.76494,9.40778],[-2.93012,9.57403],[-3.00765,9.74019],[-3.16609,9.85147],[-3.19306,9.93781],[-3.27228,9.84981],[-3.31779,9.91125],[-3.69703,9.94279],[-4.25999,9.76012],[-4.31392,9.60062],[-4.6426,9.70696],[-4.96621,9.89132],[-4.96453,9.99923],[-5.12465,10.29788],[-5.39602,10.2929],[-5.51058,10.43177],[-5.65135,10.46767],[-5.78124,10.43952],[-5.99478,10.19694],[-6.18851,10.24244],[-6.1731,10.46983],[-6.24795,10.74248],[-6.325,10.68624],[-6.40646,10.69922],[-6.42847,10.5694],[-6.52974,10.59104],[-6.63541,10.66893],[-6.68164,10.35074],[-6.93921,10.35291],[-7.01186,10.25111],[-6.97444,10.21644],[-7.00966,10.15794],[-7.0603,10.14711],[-7.13331,10.24877],[-7.3707,10.24677],[-7.44555,10.44602],[-7.52261,10.4655],[-7.54462,10.40921],[-7.63048,10.46334],[-7.92107,10.15577],[-7.97971,10.17117],[-8.01225,10.1021],[-8.11921,10.04577],[-8.15652,9.94288],[-8.09434,9.86936],[-8.14657,9.55062],[-8.03463,9.39604],[-7.85056,9.41812],[-7.90777,9.20456],[-7.73862,9.08422],[-7.92518,8.99332],[-7.95503,8.81146],[-7.69882,8.66148],[-7.65653,8.36873],[-7.92518,8.50652],[-8.22991,8.48438],[-8.2411,8.24196],[-8.062,8.16071],[-7.98675,8.20134],[-7.99919,8.11023],[-7.94695,8.00925],[-8.06449,8.04989],[-8.13414,7.87991],[-8.09931,7.78626],[-8.21374,7.54466],[-8.4003,7.6285],[-8.47114,7.55676],[-8.41935,7.51203],[-8.37458,7.25794],[-8.29249,7.1691],[-8.31736,6.82837],[-8.59456,6.50612],[-8.48652,6.43797],[-8.45666,6.49977],[-8.38453,6.35887],[-8.3298,6.36381],[-8.17557,6.28222],[-8.00642,6.31684],[-7.90692,6.27728],[-7.83478,6.20309],[-7.8497,6.08932],[-7.79747,6.07696],[-7.78254,5.99037],[-7.70294,5.90625],[-7.67309,5.94337],[-7.48155,5.80974],[-7.46165,5.84934],[-7.43677,5.84687],[-7.43926,5.74787],[-7.37209,5.61173],[-7.43428,5.42355],[-7.36463,5.32944],[-7.46165,5.26256],[-7.48901,5.14118],[-7.55369,5.08667],[-7.53876,4.94294],[-7.59349,4.8909],[-7.53259,4.35145],[-7.52774,3.7105]]]]}},{type:"Feature",properties:{iso1A2:"CK",iso1A3:"COK",iso1N3:"184",wikidata:"Q26988",nameEn:"Cook Islands",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["682"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.73854,-14.92809],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784],[-167.75195,-10.12005],[-167.73854,-14.92809]]]]}},{type:"Feature",properties:{iso1A2:"CL",iso1A3:"CHL",iso1N3:"152",wikidata:"Q298",nameEn:"Chile",groups:["005","419","019"],callingCodes:["56"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.60702,-52.65781],[-68.41683,-52.33516],[-69.97824,-52.00845],[-71.99889,-51.98018],[-72.33873,-51.59954],[-72.31343,-50.58411],[-73.15765,-50.78337],[-73.55259,-49.92488],[-73.45156,-49.79461],[-73.09655,-49.14342],[-72.56894,-48.81116],[-72.54042,-48.52392],[-72.27662,-48.28727],[-72.50478,-47.80586],[-71.94152,-47.13595],[-71.68577,-46.55385],[-71.75614,-45.61611],[-71.35687,-45.22075],[-72.06985,-44.81756],[-71.26418,-44.75684],[-71.16436,-44.46244],[-71.81318,-44.38097],[-71.64206,-43.64774],[-72.14828,-42.85321],[-72.15541,-42.15941],[-71.74901,-42.11711],[-71.92726,-40.72714],[-71.37826,-38.91474],[-70.89532,-38.6923],[-71.24279,-37.20264],[-70.95047,-36.4321],[-70.38008,-36.02375],[-70.49416,-35.24145],[-69.87386,-34.13344],[-69.88099,-33.34489],[-70.55832,-31.51559],[-70.14479,-30.36595],[-69.8596,-30.26131],[-69.99507,-29.28351],[-69.80969,-29.07185],[-69.66709,-28.44055],[-69.22504,-27.95042],[-68.77586,-27.16029],[-68.43363,-27.08414],[-68.27677,-26.90626],[-68.59048,-26.49861],[-68.56909,-26.28146],[-68.38372,-26.15353],[-68.57622,-25.32505],[-68.38372,-25.08636],[-68.56909,-24.69831],[-68.24825,-24.42596],[-67.33563,-24.04237],[-66.99632,-22.99839],[-67.18382,-22.81525],[-67.54284,-22.89771],[-67.85114,-22.87076],[-68.18816,-21.28614],[-68.40403,-20.94562],[-68.53957,-20.91542],[-68.55383,-20.7355],[-68.44023,-20.62701],[-68.7276,-20.46178],[-68.74273,-20.08817],[-68.57132,-20.03134],[-68.54611,-19.84651],[-68.66761,-19.72118],[-68.41218,-19.40499],[-68.61989,-19.27584],[-68.80602,-19.08355],[-68.87082,-19.06003],[-68.94987,-18.93302],[-69.07432,-18.28259],[-69.14807,-18.16893],[-69.07496,-18.03715],[-69.28671,-17.94844],[-69.34126,-17.72753],[-69.46623,-17.60518],[-69.46897,-17.4988],[-69.66483,-17.65083],[-69.79087,-17.65563],[-69.82868,-17.72048],[-69.75305,-17.94605],[-69.81607,-18.12582],[-69.96732,-18.25992],[-70.16394,-18.31737],[-70.31267,-18.31258],[-70.378,-18.3495],[-70.59118,-18.35072],[-113.52687,-26.52828],[-68.11646,-58.14883],[-66.07313,-55.19618],[-67.11046,-54.94199],[-67.46182,-54.92205],[-68.01394,-54.8753],[-68.60733,-54.9125],[-68.60702,-52.65781]]]]}},{type:"Feature",properties:{iso1A2:"CM",iso1A3:"CMR",iso1N3:"120",wikidata:"Q1009",nameEn:"Cameroon",groups:["017","202","002"],callingCodes:["237"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.83314,12.62963],[14.55058,12.78256],[14.56101,12.91036],[14.46881,13.08259],[14.08251,13.0797],[14.20204,12.53405],[14.17523,12.41916],[14.22215,12.36533],[14.4843,12.35223],[14.6474,12.17466],[14.61612,11.7798],[14.55207,11.72001],[14.64591,11.66166],[14.6124,11.51283],[14.17821,11.23831],[13.97489,11.30258],[13.78945,11.00154],[13.7403,11.00593],[13.70753,10.94451],[13.73434,10.9255],[13.54964,10.61236],[13.5705,10.53183],[13.43644,10.13326],[13.34111,10.12299],[13.25025,10.03647],[13.25323,10.00127],[13.286,9.9822],[13.27409,9.93232],[13.24132,9.91031],[13.25025,9.86042],[13.29941,9.8296],[13.25472,9.76795],[13.22642,9.57266],[13.02385,9.49334],[12.85628,9.36698],[12.91958,9.33905],[12.90022,9.11411],[12.81085,8.91992],[12.79,8.75361],[12.71701,8.7595],[12.68722,8.65938],[12.44146,8.6152],[12.4489,8.52536],[12.26123,8.43696],[12.24782,8.17904],[12.19271,8.10826],[12.20909,7.97553],[11.99908,7.67302],[12.01844,7.52981],[11.93205,7.47812],[11.84864,7.26098],[11.87396,7.09398],[11.63117,6.9905],[11.55818,6.86186],[11.57755,6.74059],[11.51499,6.60892],[11.42264,6.5882],[11.42041,6.53789],[11.09495,6.51717],[11.09644,6.68437],[10.94302,6.69325],[10.8179,6.83377],[10.83727,6.9358],[10.60789,7.06885],[10.59746,7.14719],[10.57214,7.16345],[10.53639,6.93432],[10.21466,6.88996],[10.15135,7.03781],[9.86314,6.77756],[9.77824,6.79088],[9.70674,6.51717],[9.51757,6.43874],[8.84209,5.82562],[8.88156,5.78857],[8.83687,5.68483],[8.92029,5.58403],[8.78027,5.1243],[8.60302,4.87353],[8.34397,4.30689],[9.22018,3.72052],[9.81162,2.33797],[9.82123,2.35097],[9.83754,2.32428],[9.83238,2.29079],[9.84716,2.24676],[9.89012,2.20457],[9.90749,2.20049],[9.991,2.16561],[11.3561,2.17217],[11.37116,2.29975],[13.28534,2.25716],[13.29457,2.16106],[14.61145,2.17866],[15.00996,1.98887],[15.22634,2.03243],[15.34776,1.91264],[15.48942,1.98265],[16.02959,1.76483],[16.02647,1.65591],[16.14634,1.70259],[16.05294,1.9811],[16.08563,2.19733],[16.15568,2.18955],[16.19357,2.21537],[16.08252,2.45708],[16.05449,3.02306],[15.77725,3.26835],[15.73522,3.24348],[15.07686,4.01805],[15.17482,4.05131],[15.10644,4.1362],[15.08609,4.30282],[15.00825,4.41458],[14.73383,4.6135],[14.65489,5.21343],[14.57083,5.23979],[14.52724,5.28319],[14.62531,5.51411],[14.58951,5.59777],[14.62375,5.70466],[14.60974,5.91838],[14.49455,5.91683],[14.42917,6.00508],[14.43073,6.08867],[14.56149,6.18928],[14.74206,6.26356],[14.80122,6.34866],[14.79966,6.39043],[14.96311,6.75693],[15.04717,6.77085],[15.23397,7.25135],[15.49743,7.52179],[15.56964,7.58936],[15.59272,7.7696],[15.50743,7.79302],[15.20426,8.50892],[15.09484,8.65982],[14.83566,8.80557],[14.35707,9.19611],[14.37094,9.2954],[13.97544,9.6365],[14.01793,9.73169],[14.1317,9.82413],[14.20411,10.00055],[14.4673,10.00264],[14.80082,9.93818],[14.95722,9.97926],[15.05999,9.94845],[15.14043,9.99246],[15.24618,9.99246],[15.41408,9.92876],[15.68761,9.99344],[15.50535,10.1098],[15.30874,10.31063],[15.23724,10.47764],[15.14936,10.53915],[15.15532,10.62846],[15.06737,10.80921],[15.09127,10.87431],[15.04957,11.02347],[15.10021,11.04101],[15.0585,11.40481],[15.13149,11.5537],[15.06595,11.71126],[15.11579,11.79313],[15.04808,11.8731],[15.05786,12.0608],[15.0349,12.10698],[15.00146,12.1223],[14.96952,12.0925],[14.89019,12.16593],[14.90827,12.3269],[14.83314,12.62963]]]]}},{type:"Feature",properties:{iso1A2:"CN",iso1A3:"CHN",iso1N3:"156",wikidata:"Q148",nameEn:"China",aliases:["RC"],groups:["030","142"],callingCodes:["86"]},geometry:{type:"MultiPolygon",coordinates:[[[[125.6131,53.07229],[125.17522,53.20225],[124.46078,53.21881],[123.86158,53.49391],[123.26989,53.54843],[122.85966,53.47395],[122.35063,53.49565],[121.39213,53.31888],[120.85633,53.28499],[120.0451,52.7359],[120.04049,52.58773],[120.46454,52.63811],[120.71673,52.54099],[120.61346,52.32447],[120.77337,52.20805],[120.65907,51.93544],[120.10963,51.671],[119.13553,50.37412],[119.38598,50.35162],[119.27996,50.13348],[119.11003,50.00276],[118.61623,49.93809],[117.82343,49.52696],[117.48208,49.62324],[117.27597,49.62544],[117.07142,49.68482],[116.71193,49.83813],[116.03781,48.87014],[116.06565,48.81716],[115.78876,48.51781],[115.811,48.25699],[115.52082,48.15367],[115.57128,47.91988],[115.94296,47.67741],[116.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[117.37875,47.63627],[117.50181,47.77216],[117.80196,48.01661],[118.03676,48.00982],[118.11009,48.04],[118.22677,48.03853],[118.29654,48.00246],[118.55766,47.99277],[118.7564,47.76947],[119.12343,47.66458],[119.13995,47.53997],[119.35892,47.48104],[119.31964,47.42617],[119.54918,47.29505],[119.56019,47.24874],[119.62403,47.24575],[119.71209,47.19192],[119.85518,46.92196],[119.91242,46.90091],[119.89261,46.66423],[119.80455,46.67631],[119.77373,46.62947],[119.68127,46.59015],[119.65265,46.62342],[119.42827,46.63783],[119.37306,46.61132],[119.30261,46.6083],[119.24978,46.64761],[119.10448,46.65516],[119.00541,46.74273],[118.92616,46.72765],[118.89974,46.77139],[118.8337,46.77742],[118.78747,46.68689],[118.30534,46.73519],[117.69554,46.50991],[117.60748,46.59771],[117.41782,46.57862],[117.36609,46.36335],[117.07252,46.35818],[116.83166,46.38637],[116.75551,46.33083],[116.58612,46.30211],[116.26678,45.96479],[116.24012,45.8778],[116.27366,45.78637],[116.16989,45.68603],[115.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[112.74662,44.86297],[112.4164,45.06858],[111.98695,45.09074],[111.76275,44.98032],[111.40498,44.3461],[111.96289,43.81596],[111.93776,43.68709],[111.79758,43.6637],[111.59087,43.51207],[111.0149,43.3289],[110.4327,42.78293],[110.08401,42.6411],[109.89402,42.63111],[109.452,42.44842],[109.00679,42.45302],[108.84489,42.40246],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[100.84979,42.67087],[100.33297,42.68231],[99.50671,42.56535],[97.1777,42.7964],[96.37926,42.72055],[96.35658,42.90363],[95.89543,43.2528],[95.52594,43.99353],[95.32891,44.02407],[95.39772,44.2805],[95.01191,44.25274],[94.71959,44.35284],[94.10003,44.71016],[93.51161,44.95964],[91.64048,45.07408],[90.89169,45.19667],[90.65114,45.49314],[90.70907,45.73437],[91.03026,46.04194],[90.99672,46.14207],[90.89639,46.30711],[91.07696,46.57315],[91.0147,46.58171],[91.03649,46.72916],[90.84035,46.99525],[90.76108,46.99399],[90.48542,47.30438],[90.48854,47.41826],[90.33598,47.68303],[90.10871,47.7375],[90.06512,47.88177],[89.76624,47.82745],[89.55453,48.0423],[89.0711,47.98528],[88.93186,48.10263],[88.8011,48.11302],[88.58316,48.21893],[88.58939,48.34531],[87.96361,48.58478],[88.0788,48.71436],[87.73822,48.89582],[87.88171,48.95853],[87.81333,49.17354],[87.48983,49.13794],[87.478,49.07403],[87.28386,49.11626],[86.87238,49.12432],[86.73568,48.99918],[86.75343,48.70331],[86.38069,48.46064],[85.73581,48.3939],[85.5169,48.05493],[85.61067,47.49753],[85.69696,47.2898],[85.54294,47.06171],[85.22443,47.04816],[84.93995,46.87399],[84.73077,47.01394],[83.92184,46.98912],[83.04622,47.19053],[82.21792,45.56619],[82.58474,45.40027],[82.51374,45.1755],[81.73278,45.3504],[80.11169,45.03352],[79.8987,44.89957],[80.38384,44.63073],[80.40229,44.23319],[80.40031,44.10986],[80.75156,43.44948],[80.69718,43.32589],[80.77771,43.30065],[80.78817,43.14235],[80.62913,43.141],[80.3735,43.01557],[80.58999,42.9011],[80.38169,42.83142],[80.26886,42.8366],[80.16892,42.61137],[80.26841,42.23797],[80.17807,42.21166],[80.17842,42.03211],[79.92977,42.04113],[78.3732,41.39603],[78.15757,41.38565],[78.12873,41.23091],[77.81287,41.14307],[77.76206,41.01574],[77.52723,41.00227],[77.3693,41.0375],[77.28004,41.0033],[76.99302,41.0696],[76.75681,40.95354],[76.5261,40.46114],[76.33659,40.3482],[75.96168,40.38064],[75.91361,40.2948],[75.69663,40.28642],[75.5854,40.66874],[75.22834,40.45382],[75.08243,40.43945],[74.82013,40.52197],[74.78168,40.44886],[74.85996,40.32857],[74.69875,40.34668],[74.35063,40.09742],[74.25533,40.13191],[73.97049,40.04378],[73.83006,39.76136],[73.9051,39.75073],[73.92354,39.69565],[73.94683,39.60733],[73.87018,39.47879],[73.59831,39.46425],[73.59241,39.40843],[73.5004,39.38402],[73.55396,39.3543],[73.54572,39.27567],[73.60638,39.24534],[73.75823,39.023],[73.81728,39.04007],[73.82964,38.91517],[73.7445,38.93867],[73.7033,38.84782],[73.80656,38.66449],[73.79806,38.61106],[73.97933,38.52945],[74.17022,38.65504],[74.51217,38.47034],[74.69619,38.42947],[74.69894,38.22155],[74.80331,38.19889],[74.82665,38.07359],[74.9063,38.03033],[74.92416,37.83428],[75.00935,37.77486],[74.8912,37.67576],[74.94338,37.55501],[75.06011,37.52779],[75.15899,37.41443],[75.09719,37.37297],[75.12328,37.31839],[74.88887,37.23275],[74.80605,37.21565],[74.49981,37.24518],[74.56453,37.03023],[75.13839,37.02622],[75.40481,36.95382],[75.45562,36.71971],[75.72737,36.7529],[75.92391,36.56986],[76.0324,36.41198],[76.00906,36.17511],[75.93028,36.13136],[76.15325,35.9264],[76.14913,35.82848],[76.33453,35.84296],[76.50961,35.8908],[76.77323,35.66062],[76.84539,35.67356],[76.96624,35.5932],[77.44277,35.46132],[77.70232,35.46244],[77.80532,35.52058],[78.11664,35.48022],[78.03466,35.3785],[78.00033,35.23954],[78.22692,34.88771],[78.18435,34.7998],[78.27781,34.61484],[78.54964,34.57283],[78.56475,34.50835],[78.74465,34.45174],[79.05364,34.32482],[78.99802,34.3027],[78.91769,34.15452],[78.66225,34.08858],[78.65657,34.03195],[78.73367,34.01121],[78.77349,33.73871],[78.67599,33.66445],[78.73636,33.56521],[79.15252,33.17156],[79.14016,33.02545],[79.46562,32.69668],[79.26768,32.53277],[79.13174,32.47766],[79.0979,32.38051],[78.99322,32.37948],[78.96713,32.33655],[78.7831,32.46873],[78.73916,32.69438],[78.38897,32.53938],[78.4645,32.45367],[78.49609,32.2762],[78.68754,32.10256],[78.74404,32.00384],[78.78036,31.99478],[78.69933,31.78723],[78.84516,31.60631],[78.71032,31.50197],[78.77898,31.31209],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[79.59884,30.93943],[79.93255,30.88288],[80.20721,30.58541],[80.54504,30.44936],[80.83343,30.32023],[81.03953,30.20059],[81.12842,30.01395],[81.24362,30.0126],[81.29032,30.08806],[81.2623,30.14596],[81.33355,30.15303],[81.39928,30.21862],[81.41018,30.42153],[81.62033,30.44703],[81.99082,30.33423],[82.10135,30.35439],[82.10757,30.23745],[82.19475,30.16884],[82.16984,30.0692],[82.38622,30.02608],[82.5341,29.9735],[82.73024,29.81695],[83.07116,29.61957],[83.28131,29.56813],[83.44787,29.30513],[83.63156,29.16249],[83.82303,29.30513],[83.97559,29.33091],[84.18107,29.23451],[84.24801,29.02783],[84.2231,28.89571],[84.47528,28.74023],[84.62317,28.73887],[84.85511,28.58041],[85.06059,28.68562],[85.19135,28.62825],[85.18668,28.54076],[85.10729,28.34092],[85.38127,28.28336],[85.4233,28.32996],[85.59765,28.30529],[85.60854,28.25045],[85.69105,28.38475],[85.71907,28.38064],[85.74864,28.23126],[85.84672,28.18187],[85.90743,28.05144],[85.97813,27.99023],[85.94946,27.9401],[86.06309,27.90021],[86.12069,27.93047],[86.08333,28.02121],[86.088,28.09264],[86.18607,28.17364],[86.22966,27.9786],[86.42736,27.91122],[86.51609,27.96623],[86.56265,28.09569],[86.74181,28.10638],[86.75582,28.04182],[87.03757,27.94835],[87.11696,27.84104],[87.56996,27.84517],[87.72718,27.80938],[87.82681,27.95248],[88.13378,27.88015],[88.1278,27.95417],[88.25332,27.9478],[88.54858,28.06057],[88.63235,28.12356],[88.83559,28.01936],[88.88091,27.85192],[88.77517,27.45415],[88.82981,27.38814],[88.91901,27.32483],[88.93678,27.33777],[88.96947,27.30319],[89.00216,27.32532],[88.95355,27.4106],[88.97213,27.51671],[89.0582,27.60985],[89.12825,27.62502],[89.59525,28.16433],[89.79762,28.23979],[90.13387,28.19178],[90.58842,28.02838],[90.69894,28.07784],[91.20019,27.98715],[91.25779,28.07509],[91.46327,28.0064],[91.48973,27.93903],[91.5629,27.84823],[91.6469,27.76358],[91.84722,27.76325],[91.87057,27.7195],[92.27432,27.89077],[92.32101,27.79363],[92.42538,27.80092],[92.7275,27.98662],[92.73025,28.05814],[92.65472,28.07632],[92.67486,28.15018],[92.93075,28.25671],[93.14635,28.37035],[93.18069,28.50319],[93.44621,28.67189],[93.72797,28.68821],[94.35897,29.01965],[94.2752,29.11687],[94.69318,29.31739],[94.81353,29.17804],[95.0978,29.14446],[95.11291,29.09527],[95.2214,29.10727],[95.26122,29.07727],[95.3038,29.13847],[95.41091,29.13007],[95.50842,29.13487],[95.72086,29.20797],[95.75149,29.32063],[95.84899,29.31464],[96.05361,29.38167],[96.31316,29.18643],[96.18682,29.11087],[96.20467,29.02325],[96.3626,29.10607],[96.61391,28.72742],[96.40929,28.51526],[96.48895,28.42955],[96.6455,28.61657],[96.85561,28.4875],[96.88445,28.39452],[96.98882,28.32564],[97.1289,28.3619],[97.34547,28.21385],[97.41729,28.29783],[97.47085,28.2688],[97.50518,28.49716],[97.56835,28.55628],[97.70705,28.5056],[97.79632,28.33168],[97.90069,28.3776],[98.15337,28.12114],[98.13964,27.9478],[98.32641,27.51385],[98.42529,27.55404],[98.43353,27.67086],[98.69582,27.56499],[98.7333,26.85615],[98.77547,26.61994],[98.72741,26.36183],[98.67797,26.24487],[98.7329,26.17218],[98.66884,26.09165],[98.63128,26.15492],[98.57085,26.11547],[98.60763,26.01512],[98.70818,25.86241],[98.63128,25.79937],[98.54064,25.85129],[98.40606,25.61129],[98.31268,25.55307],[98.25774,25.6051],[98.16848,25.62739],[98.18084,25.56298],[98.12591,25.50722],[98.14925,25.41547],[97.92541,25.20815],[97.83614,25.2715],[97.77023,25.11492],[97.72216,25.08508],[97.72903,24.91332],[97.79949,24.85655],[97.76481,24.8289],[97.73127,24.83015],[97.70181,24.84557],[97.64354,24.79171],[97.56648,24.76475],[97.56383,24.75535],[97.5542,24.74943],[97.54675,24.74202],[97.56525,24.72838],[97.56286,24.54535],[97.52757,24.43748],[97.60029,24.4401],[97.66998,24.45288],[97.7098,24.35658],[97.65624,24.33781],[97.66723,24.30027],[97.71941,24.29652],[97.76799,24.26365],[97.72998,24.2302],[97.72799,24.18883],[97.75305,24.16902],[97.72903,24.12606],[97.62363,24.00506],[97.5247,23.94032],[97.64667,23.84574],[97.72302,23.89288],[97.79456,23.94836],[97.79416,23.95663],[97.84328,23.97603],[97.86545,23.97723],[97.88811,23.97446],[97.8955,23.97758],[97.89676,23.97931],[97.89683,23.98389],[97.88814,23.98605],[97.88414,23.99405],[97.88616,24.00463],[97.90998,24.02094],[97.93951,24.01953],[97.98691,24.03897],[97.99583,24.04932],[98.04709,24.07616],[98.05302,24.07408],[98.05671,24.07961],[98.0607,24.07812],[98.06703,24.08028],[98.07806,24.07988],[98.20666,24.11406],[98.54476,24.13119],[98.59256,24.08371],[98.85319,24.13042],[98.87998,24.15624],[98.89632,24.10612],[98.67797,23.9644],[98.68209,23.80492],[98.79607,23.77947],[98.82933,23.72921],[98.81775,23.694],[98.88396,23.59555],[98.80294,23.5345],[98.82877,23.47908],[98.87683,23.48995],[98.92104,23.36946],[98.87573,23.33038],[98.93958,23.31414],[98.92515,23.29535],[98.88597,23.18656],[99.05975,23.16382],[99.04601,23.12215],[99.25741,23.09025],[99.34127,23.13099],[99.52214,23.08218],[99.54218,22.90014],[99.43537,22.94086],[99.45654,22.85726],[99.31243,22.73893],[99.38247,22.57544],[99.37972,22.50188],[99.28771,22.4105],[99.17318,22.18025],[99.19176,22.16983],[99.1552,22.15874],[99.33166,22.09656],[99.47585,22.13345],[99.85351,22.04183],[99.96612,22.05965],[99.99084,21.97053],[99.94003,21.82782],[99.98654,21.71064],[100.04956,21.66843],[100.12679,21.70539],[100.17486,21.65306],[100.10757,21.59945],[100.12542,21.50365],[100.1625,21.48704],[100.18447,21.51898],[100.25863,21.47043],[100.35201,21.53176],[100.42892,21.54325],[100.4811,21.46148],[100.57861,21.45637],[100.72143,21.51898],[100.87265,21.67396],[101.11744,21.77659],[101.15156,21.56129],[101.2124,21.56422],[101.19349,21.41959],[101.26912,21.36482],[101.2229,21.23271],[101.29326,21.17254],[101.54563,21.25668],[101.6068,21.23329],[101.59491,21.18621],[101.60886,21.17947],[101.66977,21.20004],[101.70548,21.14911],[101.7622,21.14813],[101.79266,21.19025],[101.76745,21.21571],[101.83887,21.20983],[101.84412,21.25291],[101.74014,21.30967],[101.74224,21.48276],[101.7727,21.51794],[101.7475,21.5873],[101.80001,21.57461],[101.83257,21.61562],[101.74555,21.72852],[101.7791,21.83019],[101.62566,21.96574],[101.57525,22.13026],[101.60675,22.13513],[101.53638,22.24794],[101.56789,22.28876],[101.61306,22.27515],[101.68973,22.46843],[101.7685,22.50337],[101.86828,22.38397],[101.90714,22.38688],[101.91344,22.44417],[101.98487,22.42766],[102.03633,22.46164],[102.1245,22.43372],[102.14099,22.40092],[102.16621,22.43336],[102.26428,22.41321],[102.25339,22.4607],[102.41061,22.64184],[102.38415,22.67919],[102.42618,22.69212],[102.46665,22.77108],[102.51802,22.77969],[102.57095,22.7036],[102.60675,22.73376],[102.8636,22.60735],[102.9321,22.48659],[103.0722,22.44775],[103.07843,22.50097],[103.17961,22.55705],[103.15782,22.59873],[103.18895,22.64471],[103.28079,22.68063],[103.32282,22.8127],[103.43179,22.75816],[103.43646,22.70648],[103.52675,22.59155],[103.57812,22.65764],[103.56255,22.69499],[103.64506,22.79979],[103.87904,22.56683],[103.93286,22.52703],[103.94513,22.52553],[103.95191,22.5134],[103.96352,22.50584],[103.96783,22.51173],[103.97384,22.50634],[103.99247,22.51958],[104.01088,22.51823],[104.03734,22.72945],[104.11384,22.80363],[104.27084,22.8457],[104.25683,22.76534],[104.35593,22.69353],[104.47225,22.75813],[104.58122,22.85571],[104.60457,22.81841],[104.65283,22.83419],[104.72755,22.81984],[104.77114,22.90017],[104.84942,22.93631],[104.86765,22.95178],[104.8334,23.01484],[104.79478,23.12934],[104.87382,23.12854],[104.87992,23.17141],[104.91435,23.18666],[104.9486,23.17235],[104.96532,23.20463],[104.98712,23.19176],[105.07002,23.26248],[105.11672,23.25247],[105.17276,23.28679],[105.22569,23.27249],[105.32376,23.39684],[105.40782,23.28107],[105.42805,23.30824],[105.49966,23.20669],[105.56037,23.16806],[105.57594,23.075],[105.72382,23.06641],[105.8726,22.92756],[105.90119,22.94168],[105.99568,22.94178],[106.00179,22.99049],[106.19705,22.98475],[106.27022,22.87722],[106.34961,22.86718],[106.49749,22.91164],[106.51306,22.94891],[106.55976,22.92311],[106.60179,22.92884],[106.6516,22.86862],[106.6734,22.89587],[106.71387,22.88296],[106.71128,22.85982],[106.78422,22.81532],[106.81271,22.8226],[106.83685,22.8098],[106.82404,22.7881],[106.76293,22.73491],[106.72321,22.63606],[106.71698,22.58432],[106.65316,22.5757],[106.61269,22.60301],[106.58395,22.474],[106.55665,22.46498],[106.57221,22.37],[106.55976,22.34841],[106.6516,22.33977],[106.69986,22.22309],[106.67495,22.1885],[106.6983,22.15102],[106.70142,22.02409],[106.68274,21.99811],[106.69276,21.96013],[106.72551,21.97923],[106.74345,22.00965],[106.81038,21.97934],[106.9178,21.97357],[106.92714,21.93459],[106.97228,21.92592],[106.99252,21.95191],[107.05634,21.92303],[107.06101,21.88982],[107.00964,21.85948],[107.02615,21.81981],[107.10771,21.79879],[107.20734,21.71493],[107.24625,21.7077],[107.29296,21.74674],[107.35834,21.6672],[107.35989,21.60063],[107.38636,21.59774],[107.41593,21.64839],[107.47197,21.6672],[107.49532,21.62958],[107.49065,21.59774],[107.54047,21.5934],[107.56537,21.61945],[107.66967,21.60787],[107.80355,21.66141],[107.86114,21.65128],[107.90006,21.5905],[107.92652,21.58906],[107.95232,21.5388],[107.96774,21.53601],[107.97074,21.54072],[107.97383,21.53961],[107.97932,21.54503],[108.02926,21.54997],[108.0569,21.53604],[108.10003,21.47338],[108.00365,17.98159],[111.60491,13.57105],[118.41371,24.06775],[118.11703,24.39734],[118.28244,24.51231],[118.35291,24.51645],[118.42453,24.54644],[118.56434,24.49266],[120.49232,25.22863],[121.03532,26.8787],[123.5458,31.01942],[122.29378,31.76513],[122.80525,33.30571],[123.85601,37.49093],[123.90497,38.79949],[124.17532,39.8232],[124.23201,39.9248],[124.35029,39.95639],[124.37089,40.03004],[124.3322,40.05573],[124.38556,40.11047],[124.40719,40.13655],[124.86913,40.45387],[125.71172,40.85223],[125.76869,40.87908],[126.00335,40.92835],[126.242,41.15454],[126.53189,41.35206],[126.60631,41.65565],[126.90729,41.79955],[127.17841,41.59714],[127.29712,41.49473],[127.92943,41.44291],[128.02633,41.42103],[128.03311,41.39232],[128.12967,41.37931],[128.18546,41.41279],[128.20061,41.40895],[128.30716,41.60322],[128.15119,41.74568],[128.04487,42.01769],[128.94007,42.03537],[128.96068,42.06657],[129.15178,42.17224],[129.22285,42.26491],[129.22423,42.3553],[129.28541,42.41574],[129.42882,42.44702],[129.54701,42.37254],[129.60482,42.44461],[129.72541,42.43739],[129.75294,42.59409],[129.77183,42.69435],[129.7835,42.76521],[129.80719,42.79218],[129.83277,42.86746],[129.85261,42.96494],[129.8865,43.00395],[129.95082,43.01051],[129.96409,42.97306],[130.12957,42.98361],[130.09764,42.91425],[130.26095,42.9027],[130.23068,42.80125],[130.2385,42.71127],[130.41826,42.6011],[130.44361,42.54849],[130.50123,42.61636],[130.55143,42.52158],[130.62107,42.58413],[130.56576,42.68925],[130.40213,42.70788],[130.44361,42.76205],[130.66524,42.84753],[131.02438,42.86518],[131.02668,42.91246],[131.135,42.94114],[131.10274,43.04734],[131.20414,43.13654],[131.19031,43.21385],[131.30324,43.39498],[131.29402,43.46695],[131.19492,43.53047],[131.21105,43.82383],[131.26176,43.94011],[131.23583,43.96085],[131.25484,44.03131],[131.30365,44.04262],[131.1108,44.70266],[130.95639,44.85154],[131.48415,44.99513],[131.68466,45.12374],[131.66852,45.2196],[131.76532,45.22609],[131.86903,45.33636],[131.99417,45.2567],[132.83978,45.05916],[132.96373,45.0212],[133.12293,45.1332],[133.09279,45.25693],[133.19419,45.51913],[133.41083,45.57723],[133.48457,45.86203],[133.60442,45.90053],[133.67569,45.9759],[133.72695,46.05576],[133.68047,46.14697],[133.88097,46.25066],[133.91496,46.4274],[133.84104,46.46681],[134.03538,46.75668],[134.20016,47.33458],[134.50898,47.4812],[134.7671,47.72051],[134.55508,47.98651],[134.67098,48.1564],[134.75328,48.36763],[134.49516,48.42884],[132.66989,47.96491],[132.57309,47.71741],[131.90448,47.68011],[131.2635,47.73325],[131.09871,47.6852],[130.95985,47.6957],[130.90915,47.90623],[130.65103,48.10052],[130.84462,48.30942],[130.52147,48.61745],[130.66946,48.88251],[130.43232,48.90844],[130.2355,48.86741],[129.85416,49.11067],[129.67598,49.29596],[129.50685,49.42398],[129.40398,49.44194],[129.35317,49.3481],[129.23232,49.40353],[129.11153,49.36813],[128.72896,49.58676],[127.83476,49.5748],[127.53516,49.84306],[127.49299,50.01251],[127.60515,50.23503],[127.37384,50.28393],[127.36009,50.43787],[127.28765,50.46585],[127.36335,50.58306],[127.28165,50.72075],[127.14586,50.91152],[126.93135,51.0841],[126.90369,51.3238],[126.68349,51.70607],[126.44606,51.98254],[126.558,52.13738],[125.6131,53.07229]],[[113.56865,22.20973],[113.57123,22.20416],[113.60504,22.20464],[113.63011,22.10782],[113.57191,22.07696],[113.54839,22.10909],[113.54942,22.14519],[113.54093,22.15497],[113.52659,22.18271],[113.53552,22.20607],[113.53301,22.21235],[113.53591,22.21369],[113.54093,22.21314],[113.54333,22.21688],[113.5508,22.21672],[113.56865,22.20973]],[[114.50148,22.15017],[113.92195,22.13873],[113.83338,22.1826],[113.81621,22.2163],[113.86771,22.42972],[114.03113,22.5065],[114.05438,22.5026],[114.05729,22.51104],[114.06272,22.51617],[114.07267,22.51855],[114.07817,22.52997],[114.08606,22.53276],[114.09048,22.53716],[114.09692,22.53435],[114.1034,22.5352],[114.11181,22.52878],[114.11656,22.53415],[114.12665,22.54003],[114.13823,22.54319],[114.1482,22.54091],[114.15123,22.55163],[114.1597,22.56041],[114.17247,22.55944],[114.18338,22.55444],[114.20655,22.55706],[114.22185,22.55343],[114.22888,22.5436],[114.25154,22.55977],[114.44998,22.55977],[114.50148,22.15017]]]]}},{type:"Feature",properties:{iso1A2:"CO",iso1A3:"COL",iso1N3:"170",wikidata:"Q739",nameEn:"Colombia",groups:["005","419","019"],callingCodes:["57"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.19849,12.65801],[-81.58685,18.0025],[-82.06974,14.49418],[-82.56142,11.91792],[-78.79327,9.93766],[-77.58292,9.22278],[-77.32389,8.81247],[-77.45064,8.49991],[-77.17257,7.97422],[-77.57185,7.51147],[-77.72514,7.72348],[-77.72157,7.47612],[-77.81426,7.48319],[-77.89178,7.22681],[-78.06168,7.07793],[-82.12561,4.00341],[-78.87137,1.47457],[-78.42749,1.15389],[-77.85677,0.80197],[-77.7148,0.85003],[-77.68613,0.83029],[-77.66416,0.81604],[-77.67815,0.73863],[-77.49984,0.64476],[-77.52001,0.40782],[-76.89177,0.24736],[-76.4094,0.24015],[-76.41215,0.38228],[-76.23441,0.42294],[-75.82927,0.09578],[-75.25764,-0.11943],[-75.18513,-0.0308],[-74.42701,-0.50218],[-74.26675,-0.97229],[-73.65312,-1.26222],[-72.92587,-2.44514],[-71.75223,-2.15058],[-70.94377,-2.23142],[-70.04609,-2.73906],[-70.71396,-3.7921],[-70.52393,-3.87553],[-70.3374,-3.79505],[-69.94708,-4.2431],[-69.43395,-1.42219],[-69.4215,-1.01853],[-69.59796,-0.75136],[-69.603,-0.51947],[-70.03658,-0.19681],[-70.04162,0.55437],[-69.47696,0.71065],[-69.20976,0.57958],[-69.14422,0.84172],[-69.26017,1.06856],[-69.82987,1.07864],[-69.83491,1.69353],[-69.53746,1.76408],[-69.38621,1.70865],[-68.18128,1.72881],[-68.26699,1.83463],[-68.18632,2.00091],[-67.9292,1.82455],[-67.40488,2.22258],[-67.299,1.87494],[-67.15784,1.80439],[-67.08222,1.17441],[-66.85795,1.22998],[-67.21967,2.35778],[-67.65696,2.81691],[-67.85862,2.79173],[-67.85862,2.86727],[-67.30945,3.38393],[-67.50067,3.75812],[-67.62671,3.74303],[-67.85358,4.53249],[-67.83341,5.31104],[-67.59141,5.5369],[-67.63914,5.64963],[-67.58558,5.84537],[-67.43513,5.98835],[-67.4625,6.20625],[-67.60654,6.2891],[-69.41843,6.1072],[-70.10716,6.96516],[-70.7596,7.09799],[-71.03941,6.98163],[-71.37234,7.01588],[-71.42212,7.03854],[-71.44118,7.02116],[-71.82441,7.04314],[-72.04895,7.03837],[-72.19437,7.37034],[-72.43132,7.40034],[-72.47415,7.48928],[-72.45321,7.57232],[-72.47827,7.65604],[-72.46763,7.79518],[-72.44454,7.86031],[-72.46183,7.90682],[-72.45806,7.91141],[-72.47042,7.92306],[-72.48183,7.92909],[-72.48801,7.94329],[-72.47213,7.96106],[-72.39137,8.03534],[-72.35163,8.01163],[-72.36987,8.19976],[-72.4042,8.36513],[-72.65474,8.61428],[-72.77415,9.10165],[-72.94052,9.10663],[-73.02119,9.27584],[-73.36905,9.16636],[-72.98085,9.85253],[-72.88002,10.44309],[-72.4767,11.1117],[-72.24983,11.14138],[-71.9675,11.65536],[-71.3275,11.85],[-70.92579,11.96275],[-71.19849,12.65801]]]]}},{type:"Feature",properties:{iso1A2:"CP",iso1A3:"CPT",wikidata:"Q161258",nameEn:"Clipperton Island",country:"FR",isoStatus:"excRes"},geometry:{type:"MultiPolygon",coordinates:[[[[-110.36279,9.79626],[-108.755,9.84085],[-109.04145,11.13245],[-110.36279,9.79626]]]]}},{type:"Feature",properties:{iso1A2:"CR",iso1A3:"CRI",iso1N3:"188",wikidata:"Q800",nameEn:"Costa Rica",groups:["013","003","419","019"],callingCodes:["506"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.68276,11.01562],[-83.66597,10.79916],[-83.90838,10.71161],[-84.68197,11.07568],[-84.92439,10.9497],[-85.60529,11.22607],[-85.71223,11.06868],[-86.14524,11.09059],[-87.41779,5.02401],[-82.94503,7.93865],[-82.89978,8.04083],[-82.89137,8.05755],[-82.88641,8.10219],[-82.9388,8.26634],[-83.05209,8.33394],[-82.93056,8.43465],[-82.8679,8.44042],[-82.8382,8.48117],[-82.83322,8.52464],[-82.83975,8.54755],[-82.82739,8.60153],[-82.8794,8.6981],[-82.92068,8.74832],[-82.91377,8.774],[-82.88253,8.83331],[-82.72126,8.97125],[-82.93516,9.07687],[-82.93516,9.46741],[-82.84871,9.4973],[-82.87919,9.62645],[-82.77206,9.59573],[-82.66667,9.49746],[-82.61345,9.49881],[-82.56507,9.57279],[-82.51044,9.65379],[-83.54024,10.96805],[-83.68276,11.01562]]]]}},{type:"Feature",properties:{iso1A2:"CU",iso1A3:"CUB",iso1N3:"192",wikidata:"Q241",nameEn:"Cuba",groups:["029","003","419","019"],callingCodes:["53"]},geometry:{type:"MultiPolygon",coordinates:[[[[-73.62304,20.6935],[-82.02215,24.23074],[-85.77883,21.92705],[-74.81171,18.82201],[-73.62304,20.6935]]]]}},{type:"Feature",properties:{iso1A2:"CV",iso1A3:"CPV",iso1N3:"132",wikidata:"Q1011",nameEn:"Cape Verde",groups:["011","202","002"],callingCodes:["238"]},geometry:{type:"MultiPolygon",coordinates:[[[[-28.81604,14.57305],[-20.39702,14.12816],[-23.37101,19.134],[-28.81604,14.57305]]]]}},{type:"Feature",properties:{iso1A2:"CW",iso1A3:"CUW",iso1N3:"531",wikidata:"Q25279",nameEn:"Curaçao",country:"NL",groups:["029","003","419","019"],callingCodes:["599"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.90012,12.62309],[-69.59009,12.46019],[-68.99639,11.79035],[-68.33524,11.78151],[-68.90012,12.62309]]]]}},{type:"Feature",properties:{iso1A2:"CX",iso1A3:"CXR",iso1N3:"162",wikidata:"Q31063",nameEn:"Christmas Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.66835,-9.31927],[104.67494,-11.2566],[106.66176,-11.14349],[105.66835,-9.31927]]]]}},{type:"Feature",properties:{iso1A2:"CY",iso1A3:"CYP",iso1N3:"196",wikidata:"Q229",nameEn:"Cyprus",groups:["EU","145","142"],driveSide:"left",callingCodes:["357"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[33.67678,35.03866],[33.67742,35.05963],[33.68474,35.06602],[33.69095,35.06237],[33.70861,35.07644],[33.7161,35.07279],[33.70209,35.04882],[33.71482,35.03722],[33.73824,35.05321],[33.76106,35.04253],[33.78581,35.05104],[33.82067,35.07826],[33.84168,35.06823],[33.8541,35.07201],[33.87479,35.08881],[33.87097,35.09389],[33.87622,35.10457],[33.87224,35.12293],[33.88561,35.12449],[33.88943,35.12007],[33.88737,35.11408],[33.89853,35.11377],[33.91789,35.08688],[33.91299,35.07579],[33.90247,35.07686],[33.89485,35.06873],[33.88367,35.07877],[33.85261,35.0574],[33.8355,35.05777],[33.82051,35.0667],[33.8012,35.04786],[33.81524,35.04192],[33.83055,35.02865],[33.82875,35.01685],[33.84045,35.00616],[33.85216,35.00579],[33.85891,35.001],[33.85621,34.98956],[33.83505,34.98108],[33.84811,34.97075],[33.86432,34.97592],[33.90075,34.96623],[33.98684,34.76642],[35.48515,34.70851],[35.51152,36.10954],[32.82353,35.70297],[30.15137,34.08517],[32.74412,34.43926],[32.75515,34.64985],[32.76136,34.68318],[32.79433,34.67883],[32.82717,34.70622],[32.86014,34.70585],[32.86167,34.68734],[32.9068,34.66102],[32.91398,34.67343],[32.93043,34.67091],[32.92807,34.66736],[32.93449,34.66241],[32.93693,34.67027],[32.94379,34.67111],[32.94683,34.67907],[32.95539,34.68471],[32.99135,34.68061],[32.98668,34.67268],[32.99014,34.65518],[32.97736,34.65277],[32.97079,34.66112],[32.95325,34.66462],[32.94796,34.6587],[32.94976,34.65204],[32.95471,34.64528],[32.95323,34.64075],[32.95891,34.62919],[32.96718,34.63446],[32.96968,34.64046],[33.0138,34.64424],[33.26744,34.49942],[33.83531,34.73974],[33.70575,34.97947],[33.70639,34.99303]]],[[[33.74144,35.01053],[33.7492,35.01319],[33.74983,35.02274],[33.74265,35.02329],[33.73781,35.02181],[33.7343,35.01178],[33.74144,35.01053]]],[[[33.77312,34.9976],[33.75994,35.00113],[33.75682,34.99916],[33.76605,34.99543],[33.76738,34.99188],[33.7778,34.98981],[33.77843,34.988],[33.78149,34.98854],[33.78318,34.98699],[33.78571,34.98951],[33.78917,34.98854],[33.79191,34.98914],[33.78516,34.99582],[33.77553,34.99518],[33.77312,34.9976]]]]}},{type:"Feature",properties:{iso1A2:"CZ",iso1A3:"CZE",iso1N3:"203",wikidata:"Q213",nameEn:"Czechia",groups:["EU","151","150"],callingCodes:["420"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.82803,50.86966],[14.79139,50.81438],[14.70661,50.84096],[14.61993,50.86049],[14.63434,50.8883],[14.65259,50.90513],[14.64802,50.93241],[14.58024,50.91443],[14.56374,50.922],[14.59702,50.96148],[14.59908,50.98685],[14.58215,50.99306],[14.56432,51.01008],[14.53438,51.00374],[14.53321,51.01679],[14.49873,51.02242],[14.50809,51.0427],[14.49991,51.04692],[14.49154,51.04382],[14.49202,51.02286],[14.45827,51.03712],[14.41335,51.02086],[14.30098,51.05515],[14.25665,50.98935],[14.28776,50.97718],[14.32353,50.98556],[14.32793,50.97379],[14.30251,50.96606],[14.31422,50.95243],[14.39848,50.93866],[14.38691,50.89907],[14.30098,50.88448],[14.27123,50.89386],[14.24314,50.88761],[14.22331,50.86049],[14.02982,50.80662],[13.98864,50.8177],[13.89113,50.78533],[13.89444,50.74142],[13.82942,50.7251],[13.76316,50.73487],[13.70204,50.71771],[13.65977,50.73096],[13.52474,50.70394],[13.53748,50.67654],[13.5226,50.64721],[13.49742,50.63133],[13.46413,50.60102],[13.42189,50.61243],[13.37485,50.64931],[13.37805,50.627],[13.32264,50.60317],[13.32594,50.58009],[13.29454,50.57904],[13.25158,50.59268],[13.19043,50.50237],[13.13424,50.51709],[13.08301,50.50132],[13.0312,50.50944],[13.02038,50.4734],[13.02147,50.44763],[12.98433,50.42016],[12.94058,50.40944],[12.82465,50.45738],[12.73476,50.43237],[12.73044,50.42268],[12.70731,50.39948],[12.67261,50.41949],[12.51356,50.39694],[12.48747,50.37278],[12.49214,50.35228],[12.48256,50.34784],[12.46643,50.35527],[12.43722,50.33774],[12.43371,50.32506],[12.39924,50.32302],[12.40158,50.29521],[12.36594,50.28289],[12.35425,50.23993],[12.33263,50.24367],[12.32445,50.20442],[12.33847,50.19432],[12.32596,50.17146],[12.29232,50.17524],[12.28063,50.19544],[12.28755,50.22429],[12.23943,50.24594],[12.24791,50.25525],[12.26953,50.25189],[12.25119,50.27079],[12.20823,50.2729],[12.18013,50.32146],[12.10907,50.32041],[12.13716,50.27396],[12.09287,50.25032],[12.19335,50.19997],[12.21484,50.16399],[12.1917,50.13434],[12.2073,50.10315],[12.23709,50.10213],[12.27433,50.0771],[12.26111,50.06331],[12.30798,50.05719],[12.49908,49.97305],[12.47264,49.94222],[12.55197,49.92094],[12.48256,49.83575],[12.46603,49.78882],[12.40489,49.76321],[12.4462,49.70233],[12.52553,49.68415],[12.53544,49.61888],[12.56188,49.6146],[12.60155,49.52887],[12.64782,49.52565],[12.64121,49.47628],[12.669,49.42935],[12.71227,49.42363],[12.75854,49.3989],[12.78168,49.34618],[12.88414,49.33541],[12.88249,49.35479],[12.94859,49.34079],[13.03618,49.30417],[13.02957,49.27399],[13.05883,49.26259],[13.17665,49.16713],[13.17019,49.14339],[13.20405,49.12303],[13.23689,49.11412],[13.28242,49.1228],[13.39479,49.04812],[13.40802,48.98851],[13.50221,48.93752],[13.50552,48.97441],[13.58319,48.96899],[13.61624,48.9462],[13.67739,48.87886],[13.73854,48.88538],[13.76994,48.83537],[13.78977,48.83319],[13.8096,48.77877],[13.84023,48.76988],[14.06151,48.66873],[14.01482,48.63788],[14.09104,48.5943],[14.20691,48.5898],[14.33909,48.55852],[14.43076,48.58855],[14.4587,48.64695],[14.56139,48.60429],[14.60808,48.62881],[14.66762,48.58215],[14.71794,48.59794],[14.72756,48.69502],[14.80584,48.73489],[14.80821,48.77711],[14.81545,48.7874],[14.94773,48.76268],[14.95641,48.75915],[14.9758,48.76857],[14.98112,48.77524],[14.9782,48.7766],[14.98032,48.77959],[14.95072,48.79101],[14.98917,48.90082],[14.97612,48.96983],[14.99878,49.01444],[15.15534,48.99056],[15.16358,48.94278],[15.26177,48.95766],[15.28305,48.98831],[15.34823,48.98444],[15.48027,48.94481],[15.51357,48.91549],[15.61622,48.89541],[15.6921,48.85973],[15.75341,48.8516],[15.78087,48.87644],[15.84404,48.86921],[16.06034,48.75436],[16.37345,48.729],[16.40915,48.74576],[16.46134,48.80865],[16.67008,48.77699],[16.68518,48.7281],[16.71883,48.73806],[16.79779,48.70998],[16.90354,48.71541],[16.93955,48.60371],[17.00215,48.70887],[17.11202,48.82925],[17.19355,48.87602],[17.29054,48.85546],[17.3853,48.80936],[17.45671,48.85004],[17.5295,48.81117],[17.7094,48.86721],[17.73126,48.87885],[17.77944,48.92318],[17.87831,48.92679],[17.91814,49.01784],[18.06885,49.03157],[18.1104,49.08624],[18.15022,49.24518],[18.18456,49.28909],[18.36446,49.3267],[18.4139,49.36517],[18.4084,49.40003],[18.44686,49.39467],[18.54848,49.47059],[18.53063,49.49022],[18.57183,49.51162],[18.6144,49.49824],[18.67757,49.50895],[18.74761,49.492],[18.84521,49.51672],[18.84786,49.5446],[18.80479,49.6815],[18.72838,49.68163],[18.69817,49.70473],[18.62676,49.71983],[18.62943,49.74603],[18.62645,49.75002],[18.61368,49.75426],[18.61278,49.7618],[18.57183,49.83334],[18.60341,49.86256],[18.57045,49.87849],[18.57697,49.91565],[18.54299,49.92537],[18.54495,49.9079],[18.53423,49.89906],[18.41604,49.93498],[18.33562,49.94747],[18.33278,49.92415],[18.31914,49.91565],[18.27794,49.93863],[18.27107,49.96779],[18.21752,49.97309],[18.20241,49.99958],[18.10628,50.00223],[18.07898,50.04535],[18.03212,50.06574],[18.00396,50.04954],[18.04585,50.03311],[18.04585,50.01194],[18.00191,50.01723],[17.86886,49.97452],[17.77669,50.02253],[17.7506,50.07896],[17.6888,50.12037],[17.66683,50.10275],[17.59404,50.16437],[17.70528,50.18812],[17.76296,50.23382],[17.72176,50.25665],[17.74648,50.29966],[17.69292,50.32859],[17.67764,50.28977],[17.58889,50.27837],[17.3702,50.28123],[17.34548,50.2628],[17.34273,50.32947],[17.27681,50.32246],[17.19991,50.3654],[17.19579,50.38817],[17.14498,50.38117],[17.1224,50.39494],[16.89229,50.45117],[16.85933,50.41093],[16.90877,50.38642],[16.94448,50.31281],[16.99803,50.30316],[17.02138,50.27772],[16.99803,50.25753],[17.02825,50.23118],[17.00353,50.21449],[16.98018,50.24172],[16.8456,50.20834],[16.7014,50.09659],[16.63137,50.1142],[16.55446,50.16613],[16.56407,50.21009],[16.42674,50.32509],[16.39379,50.3207],[16.3622,50.34875],[16.36495,50.37679],[16.30289,50.38292],[16.28118,50.36891],[16.22821,50.41054],[16.21585,50.40627],[16.19526,50.43291],[16.31413,50.50274],[16.34572,50.49575],[16.44597,50.58041],[16.33611,50.66579],[16.23174,50.67101],[16.20839,50.63096],[16.10265,50.66405],[16.02437,50.60046],[15.98317,50.61528],[16.0175,50.63009],[15.97219,50.69799],[15.87331,50.67188],[15.81683,50.75666],[15.73186,50.73885],[15.43798,50.80833],[15.3803,50.77187],[15.36656,50.83956],[15.2773,50.8907],[15.27043,50.97724],[15.2361,50.99886],[15.1743,50.9833],[15.16744,51.01959],[15.11937,50.99021],[15.10152,51.01095],[15.06218,51.02269],[15.03895,51.0123],[15.02433,51.0242],[14.96419,50.99108],[15.01088,50.97984],[14.99852,50.86817],[14.82803,50.86966]]]]}},{type:"Feature",properties:{iso1A2:"DE",iso1A3:"DEU",iso1N3:"276",wikidata:"Q183",nameEn:"Germany",groups:["EU","155","150"],callingCodes:["49"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.70847,47.68904],[8.71773,47.69088],[8.70237,47.71453],[8.66416,47.71367],[8.67508,47.6979],[8.65769,47.68928],[8.66837,47.68437],[8.68985,47.69552],[8.70847,47.68904]]],[[[8.72617,47.69651],[8.72809,47.69282],[8.75856,47.68969],[8.79511,47.67462],[8.79966,47.70222],[8.76965,47.7075],[8.77309,47.72059],[8.80663,47.73821],[8.82002,47.71458],[8.86989,47.70504],[8.85065,47.68209],[8.87383,47.67045],[8.87625,47.65441],[8.89946,47.64769],[8.94093,47.65596],[9.02093,47.6868],[9.09891,47.67801],[9.13845,47.66389],[9.15181,47.66904],[9.1705,47.65513],[9.1755,47.65584],[9.17593,47.65399],[9.18203,47.65598],[9.25619,47.65939],[9.55125,47.53629],[9.72736,47.53457],[9.76748,47.5934],[9.80254,47.59419],[9.82591,47.58158],[9.8189,47.54688],[9.87499,47.52953],[9.87733,47.54688],[9.92407,47.53111],[9.96029,47.53899],[10.00003,47.48216],[10.03859,47.48927],[10.07131,47.45531],[10.09001,47.46005],[10.1052,47.4316],[10.06897,47.40709],[10.09819,47.35724],[10.11805,47.37228],[10.16362,47.36674],[10.17648,47.38889],[10.2127,47.38019],[10.22774,47.38904],[10.23757,47.37609],[10.19998,47.32832],[10.2147,47.31014],[10.17648,47.29149],[10.17531,47.27167],[10.23257,47.27088],[10.33424,47.30813],[10.39851,47.37623],[10.4324,47.38494],[10.4359,47.41183],[10.47446,47.43318],[10.46278,47.47901],[10.44291,47.48453],[10.4324,47.50111],[10.44992,47.5524],[10.43473,47.58394],[10.47329,47.58552],[10.48849,47.54057],[10.56912,47.53584],[10.60337,47.56755],[10.63456,47.5591],[10.68832,47.55752],[10.6965,47.54253],[10.7596,47.53228],[10.77596,47.51729],[10.88814,47.53701],[10.91268,47.51334],[10.86945,47.5015],[10.87061,47.4786],[10.90918,47.48571],[10.93839,47.48018],[10.92437,47.46991],[10.98513,47.42882],[10.97111,47.41617],[10.97111,47.39561],[11.11835,47.39719],[11.12536,47.41222],[11.20482,47.43198],[11.25157,47.43277],[11.22002,47.3964],[11.27844,47.39956],[11.29597,47.42566],[11.33804,47.44937],[11.4175,47.44621],[11.38128,47.47465],[11.4362,47.51413],[11.52618,47.50939],[11.58578,47.52281],[11.58811,47.55515],[11.60681,47.57881],[11.63934,47.59202],[11.84052,47.58354],[11.85572,47.60166],[12.0088,47.62451],[12.02282,47.61033],[12.05788,47.61742],[12.13734,47.60639],[12.17824,47.61506],[12.18145,47.61019],[12.17737,47.60121],[12.18568,47.6049],[12.20398,47.60667],[12.20801,47.61082],[12.19895,47.64085],[12.18507,47.65984],[12.18347,47.66663],[12.16769,47.68167],[12.16217,47.70105],[12.18303,47.70065],[12.22571,47.71776],[12.2542,47.7433],[12.26238,47.73544],[12.24017,47.69534],[12.26004,47.67725],[12.27991,47.68827],[12.336,47.69534],[12.37222,47.68433],[12.43883,47.6977],[12.44117,47.6741],[12.50076,47.62293],[12.53816,47.63553],[12.57438,47.63238],[12.6071,47.6741],[12.7357,47.6787],[12.77777,47.66689],[12.76492,47.64485],[12.82101,47.61493],[12.77427,47.58025],[12.80699,47.54477],[12.84672,47.54556],[12.85256,47.52741],[12.9624,47.47452],[12.98344,47.48716],[12.9998,47.46267],[13.04537,47.49426],[13.03252,47.53373],[13.05355,47.56291],[13.04537,47.58183],[13.06641,47.58577],[13.06407,47.60075],[13.09562,47.63304],[13.07692,47.68814],[13.01382,47.72116],[12.98578,47.7078],[12.92969,47.71094],[12.91333,47.7178],[12.90274,47.72513],[12.91711,47.74026],[12.9353,47.74788],[12.94371,47.76281],[12.93202,47.77302],[12.96311,47.79957],[12.98543,47.82896],[13.00588,47.84374],[12.94163,47.92927],[12.93886,47.94046],[12.93642,47.94436],[12.93419,47.94063],[12.92668,47.93879],[12.91985,47.94069],[12.9211,47.95135],[12.91683,47.95647],[12.87476,47.96195],[12.8549,48.01122],[12.76141,48.07373],[12.74973,48.10885],[12.7617,48.12796],[12.78595,48.12445],[12.80676,48.14979],[12.82673,48.15245],[12.8362,48.15876],[12.836,48.1647],[12.84475,48.16556],[12.87126,48.20318],[12.95306,48.20629],[13.02083,48.25689],[13.0851,48.27711],[13.126,48.27867],[13.18093,48.29577],[13.26039,48.29422],[13.30897,48.31575],[13.40709,48.37292],[13.43929,48.43386],[13.42527,48.45711],[13.45727,48.51092],[13.43695,48.55776],[13.45214,48.56472],[13.46967,48.55157],[13.50663,48.57506],[13.50131,48.58091],[13.51291,48.59023],[13.57535,48.55912],[13.59705,48.57013],[13.62508,48.55501],[13.65186,48.55092],[13.66113,48.53558],[13.72802,48.51208],[13.74816,48.53058],[13.7513,48.5624],[13.76921,48.55324],[13.80519,48.58026],[13.80038,48.59487],[13.82609,48.62345],[13.81901,48.6761],[13.81283,48.68426],[13.81791,48.69832],[13.79337,48.71375],[13.81863,48.73257],[13.82266,48.75544],[13.84023,48.76988],[13.8096,48.77877],[13.78977,48.83319],[13.76994,48.83537],[13.73854,48.88538],[13.67739,48.87886],[13.61624,48.9462],[13.58319,48.96899],[13.50552,48.97441],[13.50221,48.93752],[13.40802,48.98851],[13.39479,49.04812],[13.28242,49.1228],[13.23689,49.11412],[13.20405,49.12303],[13.17019,49.14339],[13.17665,49.16713],[13.05883,49.26259],[13.02957,49.27399],[13.03618,49.30417],[12.94859,49.34079],[12.88249,49.35479],[12.88414,49.33541],[12.78168,49.34618],[12.75854,49.3989],[12.71227,49.42363],[12.669,49.42935],[12.64121,49.47628],[12.64782,49.52565],[12.60155,49.52887],[12.56188,49.6146],[12.53544,49.61888],[12.52553,49.68415],[12.4462,49.70233],[12.40489,49.76321],[12.46603,49.78882],[12.48256,49.83575],[12.55197,49.92094],[12.47264,49.94222],[12.49908,49.97305],[12.30798,50.05719],[12.26111,50.06331],[12.27433,50.0771],[12.23709,50.10213],[12.2073,50.10315],[12.1917,50.13434],[12.21484,50.16399],[12.19335,50.19997],[12.09287,50.25032],[12.13716,50.27396],[12.10907,50.32041],[12.18013,50.32146],[12.20823,50.2729],[12.25119,50.27079],[12.26953,50.25189],[12.24791,50.25525],[12.23943,50.24594],[12.28755,50.22429],[12.28063,50.19544],[12.29232,50.17524],[12.32596,50.17146],[12.33847,50.19432],[12.32445,50.20442],[12.33263,50.24367],[12.35425,50.23993],[12.36594,50.28289],[12.40158,50.29521],[12.39924,50.32302],[12.43371,50.32506],[12.43722,50.33774],[12.46643,50.35527],[12.48256,50.34784],[12.49214,50.35228],[12.48747,50.37278],[12.51356,50.39694],[12.67261,50.41949],[12.70731,50.39948],[12.73044,50.42268],[12.73476,50.43237],[12.82465,50.45738],[12.94058,50.40944],[12.98433,50.42016],[13.02147,50.44763],[13.02038,50.4734],[13.0312,50.50944],[13.08301,50.50132],[13.13424,50.51709],[13.19043,50.50237],[13.25158,50.59268],[13.29454,50.57904],[13.32594,50.58009],[13.32264,50.60317],[13.37805,50.627],[13.37485,50.64931],[13.42189,50.61243],[13.46413,50.60102],[13.49742,50.63133],[13.5226,50.64721],[13.53748,50.67654],[13.52474,50.70394],[13.65977,50.73096],[13.70204,50.71771],[13.76316,50.73487],[13.82942,50.7251],[13.89444,50.74142],[13.89113,50.78533],[13.98864,50.8177],[14.02982,50.80662],[14.22331,50.86049],[14.24314,50.88761],[14.27123,50.89386],[14.30098,50.88448],[14.38691,50.89907],[14.39848,50.93866],[14.31422,50.95243],[14.30251,50.96606],[14.32793,50.97379],[14.32353,50.98556],[14.28776,50.97718],[14.25665,50.98935],[14.30098,51.05515],[14.41335,51.02086],[14.45827,51.03712],[14.49202,51.02286],[14.49154,51.04382],[14.49991,51.04692],[14.50809,51.0427],[14.49873,51.02242],[14.53321,51.01679],[14.53438,51.00374],[14.56432,51.01008],[14.58215,50.99306],[14.59908,50.98685],[14.59702,50.96148],[14.56374,50.922],[14.58024,50.91443],[14.64802,50.93241],[14.65259,50.90513],[14.63434,50.8883],[14.61993,50.86049],[14.70661,50.84096],[14.79139,50.81438],[14.82803,50.86966],[14.81664,50.88148],[14.89681,50.9422],[14.89252,50.94999],[14.92942,50.99744],[14.95529,51.04552],[14.97938,51.07742],[14.98229,51.11354],[14.99689,51.12205],[14.99079,51.14284],[14.99646,51.14365],[15.00083,51.14974],[14.99414,51.15813],[14.99311,51.16249],[15.0047,51.16874],[15.01242,51.21285],[15.04288,51.28387],[14.98008,51.33449],[14.96899,51.38367],[14.9652,51.44793],[14.94749,51.47155],[14.73219,51.52922],[14.72652,51.53902],[14.73047,51.54606],[14.71125,51.56209],[14.7727,51.61263],[14.75759,51.62318],[14.75392,51.67445],[14.69065,51.70842],[14.66386,51.73282],[14.64625,51.79472],[14.60493,51.80473],[14.59089,51.83302],[14.6588,51.88359],[14.6933,51.9044],[14.70601,51.92944],[14.7177,51.94048],[14.72163,51.95188],[14.71836,51.95606],[14.7139,51.95643],[14.70488,51.97679],[14.71339,52.00337],[14.76026,52.06624],[14.72971,52.09167],[14.6917,52.10283],[14.67683,52.13936],[14.70616,52.16927],[14.68344,52.19612],[14.71319,52.22144],[14.70139,52.25038],[14.58149,52.28007],[14.56378,52.33838],[14.55228,52.35264],[14.54423,52.42568],[14.63056,52.48993],[14.60081,52.53116],[14.6289,52.57136],[14.61073,52.59847],[14.22071,52.81175],[14.13806,52.82392],[14.12256,52.84311],[14.15873,52.87715],[14.14056,52.95786],[14.25954,53.00264],[14.35044,53.05829],[14.38679,53.13669],[14.36696,53.16444],[14.37853,53.20405],[14.40662,53.21098],[14.45125,53.26241],[14.44133,53.27427],[14.4215,53.27724],[14.35209,53.49506],[14.3273,53.50587],[14.30416,53.55499],[14.31904,53.61581],[14.2853,53.63392],[14.28477,53.65955],[14.27133,53.66613],[14.2836,53.67721],[14.26782,53.69866],[14.27249,53.74464],[14.21323,53.8664],[14.20823,53.90776],[14.18544,53.91258],[14.20647,53.91671],[14.22634,53.9291],[14.20811,54.12784],[13.93395,54.84044],[12.85844,54.82438],[11.90309,54.38543],[11.00303,54.63689],[10.31111,54.65968],[10.16755,54.73883],[9.89314,54.84171],[9.73563,54.8247],[9.61187,54.85548],[9.62734,54.88057],[9.58937,54.88785],[9.4659,54.83131],[9.43155,54.82586],[9.41213,54.84254],[9.38532,54.83968],[9.36496,54.81749],[9.33849,54.80233],[9.32771,54.80602],[9.2474,54.8112],[9.23445,54.83432],[9.24631,54.84726],[9.20571,54.85841],[9.14275,54.87421],[9.04629,54.87249],[8.92795,54.90452],[8.81178,54.90518],[8.76387,54.8948],[8.63979,54.91069],[8.55769,54.91837],[8.45719,55.06747],[8.02459,55.09613],[5.45168,54.20039],[6.91025,53.44221],[7.00198,53.32672],[7.19052,53.31866],[7.21679,53.20058],[7.22681,53.18165],[7.17898,53.13817],[7.21694,53.00742],[7.07253,52.81083],[7.04557,52.63318],[6.77307,52.65375],[6.71641,52.62905],[6.69507,52.488],[6.94293,52.43597],[6.99041,52.47235],[7.03417,52.40237],[7.07044,52.37805],[7.02703,52.27941],[7.06365,52.23789],[7.03729,52.22695],[6.9897,52.2271],[6.97189,52.20329],[6.83984,52.11728],[6.76117,52.11895],[6.68128,52.05052],[6.83035,51.9905],[6.82357,51.96711],[6.72319,51.89518],[6.68386,51.91861],[6.58556,51.89386],[6.50231,51.86313],[6.47179,51.85395],[6.38815,51.87257],[6.40704,51.82771],[6.30593,51.84998],[6.29872,51.86801],[6.21443,51.86801],[6.15349,51.90439],[6.11551,51.89769],[6.16902,51.84094],[6.10337,51.84829],[6.06705,51.86136],[5.99848,51.83195],[5.94568,51.82786],[5.98665,51.76944],[5.95003,51.7493],[6.04091,51.71821],[6.02767,51.6742],[6.11759,51.65609],[6.09055,51.60564],[6.18017,51.54096],[6.21724,51.48568],[6.20654,51.40049],[6.22641,51.39948],[6.22674,51.36135],[6.16977,51.33169],[6.07889,51.24432],[6.07889,51.17038],[6.17384,51.19589],[6.16706,51.15677],[5.98292,51.07469],[5.9541,51.03496],[5.9134,51.06736],[5.86735,51.05182],[5.87849,51.01969],[5.90493,51.00198],[5.90296,50.97356],[5.95282,50.98728],[6.02697,50.98303],[6.01615,50.93367],[6.09297,50.92066],[6.07486,50.89307],[6.08805,50.87223],[6.07693,50.86025],[6.07431,50.84674],[6.05702,50.85179],[6.05623,50.8572],[6.01921,50.84435],[6.02328,50.81694],[6.00462,50.80065],[5.98404,50.80988],[5.97497,50.79992],[6.02624,50.77453],[6.01976,50.75398],[6.03889,50.74618],[6.0326,50.72647],[6.0406,50.71848],[6.04428,50.72861],[6.11707,50.72231],[6.17852,50.6245],[6.26957,50.62444],[6.2476,50.60392],[6.24888,50.59869],[6.24005,50.58732],[6.22581,50.5907],[6.20281,50.56952],[6.17739,50.55875],[6.17802,50.54179],[6.19735,50.53576],[6.19579,50.5313],[6.18716,50.52653],[6.19193,50.5212],[6.20599,50.52089],[6.22335,50.49578],[6.26637,50.50272],[6.30809,50.50058],[6.3465,50.48833],[6.34005,50.46083],[6.37219,50.45397],[6.36852,50.40776],[6.34406,50.37994],[6.3688,50.35898],[6.40785,50.33557],[6.40641,50.32425],[6.35701,50.31139],[6.32488,50.32333],[6.29949,50.30887],[6.28797,50.27458],[6.208,50.25179],[6.16853,50.2234],[6.18364,50.20815],[6.18739,50.1822],[6.14588,50.17106],[6.14132,50.14971],[6.15298,50.14126],[6.1379,50.12964],[6.12055,50.09171],[6.11274,50.05916],[6.13458,50.04141],[6.13044,50.02929],[6.14666,50.02207],[6.13794,50.01466],[6.13273,50.02019],[6.1295,50.01849],[6.13806,50.01056],[6.14948,50.00908],[6.14147,49.99563],[6.1701,49.98518],[6.16466,49.97086],[6.17872,49.9537],[6.18554,49.95622],[6.18045,49.96611],[6.19089,49.96991],[6.19856,49.95053],[6.22094,49.94955],[6.22608,49.929],[6.21882,49.92403],[6.22926,49.92096],[6.23496,49.89972],[6.26146,49.88203],[6.28874,49.87592],[6.29692,49.86685],[6.30963,49.87021],[6.32303,49.85133],[6.32098,49.83728],[6.33585,49.83785],[6.34267,49.84974],[6.36576,49.85032],[6.40022,49.82029],[6.42521,49.81591],[6.42905,49.81091],[6.44131,49.81443],[6.45425,49.81164],[6.47111,49.82263],[6.48718,49.81267],[6.50647,49.80916],[6.51215,49.80124],[6.52121,49.81338],[6.53122,49.80666],[6.52169,49.79787],[6.50534,49.78952],[6.51669,49.78336],[6.51056,49.77515],[6.51828,49.76855],[6.51646,49.75961],[6.50174,49.75292],[6.50193,49.73291],[6.51805,49.72425],[6.51397,49.72058],[6.50261,49.72718],[6.49535,49.72645],[6.49694,49.72205],[6.5042,49.71808],[6.50647,49.71353],[6.49785,49.71118],[6.48014,49.69767],[6.46048,49.69092],[6.44654,49.67799],[6.42937,49.66857],[6.42726,49.66078],[6.43768,49.66021],[6.4413,49.65722],[6.41861,49.61723],[6.39822,49.60081],[6.385,49.59946],[6.37464,49.58886],[6.38342,49.5799],[6.38024,49.57593],[6.36676,49.57813],[6.35825,49.57053],[6.38228,49.55855],[6.38072,49.55171],[6.35666,49.52931],[6.36788,49.50377],[6.36907,49.48931],[6.36778,49.46937],[6.38352,49.46463],[6.39168,49.4667],[6.40274,49.46546],[6.42432,49.47683],[6.55404,49.42464],[6.533,49.40748],[6.60091,49.36864],[6.58807,49.35358],[6.572,49.35027],[6.60186,49.31055],[6.66583,49.28065],[6.69274,49.21661],[6.71843,49.2208],[6.73256,49.20486],[6.71137,49.18808],[6.73765,49.16375],[6.78265,49.16793],[6.83385,49.15162],[6.84703,49.15734],[6.86225,49.18185],[6.85016,49.19354],[6.85119,49.20038],[6.83555,49.21249],[6.85939,49.22376],[6.89298,49.20863],[6.91875,49.22261],[6.93831,49.2223],[6.94028,49.21641],[6.95963,49.203],[6.97273,49.2099],[7.01318,49.19018],[7.03459,49.19096],[7.0274,49.17042],[7.03178,49.15734],[7.04662,49.13724],[7.04409,49.12123],[7.04843,49.11422],[7.05548,49.11185],[7.06642,49.11415],[7.07162,49.1255],[7.09007,49.13094],[7.07859,49.15031],[7.10715,49.15631],[7.10384,49.13787],[7.12504,49.14253],[7.1358,49.1282],[7.1593,49.1204],[7.23473,49.12971],[7.29514,49.11426],[7.3195,49.14231],[7.35995,49.14399],[7.3662,49.17308],[7.44052,49.18354],[7.44455,49.16765],[7.49473,49.17],[7.49172,49.13915],[7.53012,49.09818],[7.56416,49.08136],[7.62575,49.07654],[7.63618,49.05428],[7.75948,49.04562],[7.79557,49.06583],[7.86386,49.03499],[7.93641,49.05544],[7.97783,49.03161],[8.14189,48.97833],[8.22604,48.97352],[8.20031,48.95856],[8.19989,48.95825],[8.12813,48.87985],[8.10253,48.81829],[8.06802,48.78957],[8.0326,48.79017],[8.01534,48.76085],[7.96994,48.75606],[7.96812,48.72491],[7.89002,48.66317],[7.84098,48.64217],[7.80057,48.5857],[7.80167,48.54758],[7.80647,48.51239],[7.76833,48.48945],[7.73109,48.39192],[7.74562,48.32736],[7.69022,48.30018],[7.6648,48.22219],[7.57137,48.12292],[7.56966,48.03265],[7.62302,47.97898],[7.55673,47.87371],[7.52921,47.77747],[7.54761,47.72912],[7.53722,47.71635],[7.51266,47.70197],[7.51915,47.68335],[7.52067,47.66437],[7.53384,47.65115],[7.5591,47.63849],[7.57423,47.61628],[7.58851,47.60794],[7.59301,47.60058],[7.58945,47.59017],[7.60523,47.58519],[7.60459,47.57869],[7.61929,47.57683],[7.64309,47.59151],[7.64213,47.5944],[7.64599,47.59695],[7.67395,47.59212],[7.68229,47.59905],[7.69385,47.60099],[7.68486,47.59601],[7.67115,47.5871],[7.68904,47.57133],[7.67655,47.56435],[7.63338,47.56256],[7.65083,47.54662],[7.66174,47.54554],[7.6656,47.53752],[7.68101,47.53232],[7.69642,47.53297],[7.71961,47.54219],[7.75261,47.54599],[7.79486,47.55691],[7.81901,47.58798],[7.84412,47.5841],[7.88664,47.58854],[7.90673,47.57674],[7.91251,47.55031],[7.94494,47.54511],[7.95682,47.55789],[7.97581,47.55493],[8.00113,47.55616],[8.02136,47.55096],[8.04383,47.55443],[8.06663,47.56374],[8.08557,47.55768],[8.10002,47.56504],[8.10395,47.57918],[8.11543,47.5841],[8.13662,47.58432],[8.13823,47.59147],[8.14947,47.59558],[8.1652,47.5945],[8.19378,47.61636],[8.20617,47.62141],[8.22011,47.6181],[8.22577,47.60385],[8.23809,47.61204],[8.25863,47.61571],[8.26313,47.6103],[8.2824,47.61225],[8.29722,47.60603],[8.29524,47.5919],[8.30277,47.58607],[8.32735,47.57133],[8.35512,47.57014],[8.38273,47.56608],[8.39477,47.57826],[8.43235,47.56617],[8.49431,47.58107],[8.48949,47.588],[8.46637,47.58389],[8.45578,47.60121],[8.50747,47.61897],[8.51686,47.63476],[8.55756,47.62394],[8.57586,47.59537],[8.60348,47.61204],[8.59545,47.64298],[8.60701,47.65271],[8.61471,47.64514],[8.60412,47.63735],[8.62049,47.63757],[8.62884,47.65098],[8.61113,47.66332],[8.6052,47.67258],[8.57683,47.66158],[8.56141,47.67088],[8.52801,47.66059],[8.5322,47.64687],[8.49656,47.64709],[8.46605,47.64103],[8.4667,47.65747],[8.44711,47.65379],[8.42264,47.66667],[8.41346,47.66676],[8.40473,47.67499],[8.4211,47.68407],[8.40569,47.69855],[8.44807,47.72426],[8.45771,47.7493],[8.48868,47.77215],[8.56814,47.78001],[8.56415,47.80633],[8.61657,47.79998],[8.62408,47.7626],[8.64425,47.76398],[8.65292,47.80066],[8.68022,47.78599],[8.68985,47.75686],[8.71778,47.76571],[8.74251,47.75168],[8.70543,47.73121],[8.73671,47.7169],[8.72617,47.69651]]]]}},{type:"Feature",properties:{iso1A2:"DG",iso1A3:"DGA",wikidata:"Q184851",nameEn:"Diego Garcia",country:"GB",groups:["IO","014","202","002"],isoStatus:"excRes",callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[73.14823,-7.76302],[73.09982,-6.07324],[71.43792,-7.73904],[73.14823,-7.76302]]]]}},{type:"Feature",properties:{iso1A2:"DJ",iso1A3:"DJI",iso1N3:"262",wikidata:"Q977",nameEn:"Djibouti",groups:["014","202","002"],callingCodes:["253"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.42425,11.70983],[43.90659,12.3823],[43.32909,12.59711],[43.29075,12.79154],[42.86195,12.58747],[42.7996,12.42629],[42.6957,12.36201],[42.46941,12.52661],[42.4037,12.46478],[41.95461,11.81157],[41.82878,11.72361],[41.77727,11.49902],[41.8096,11.33606],[41.80056,10.97127],[42.06302,10.92599],[42.13691,10.97586],[42.42669,10.98493],[42.62989,11.09711],[42.75111,11.06992],[42.79037,10.98493],[42.95776,10.98533],[43.42425,11.70983]]]]}},{type:"Feature",properties:{iso1A2:"DK",iso1A3:"DNK",iso1N3:"208",wikidata:"Q35",nameEn:"Denmark",groups:["EU","154","150"],callingCodes:["45"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.16597,56.60205],[10.40861,58.38489],[7.28637,57.35913],[8.02459,55.09613],[8.45719,55.06747],[8.55769,54.91837],[8.63979,54.91069],[8.76387,54.8948],[8.81178,54.90518],[8.92795,54.90452],[9.04629,54.87249],[9.14275,54.87421],[9.20571,54.85841],[9.24631,54.84726],[9.23445,54.83432],[9.2474,54.8112],[9.32771,54.80602],[9.33849,54.80233],[9.36496,54.81749],[9.38532,54.83968],[9.41213,54.84254],[9.43155,54.82586],[9.4659,54.83131],[9.58937,54.88785],[9.62734,54.88057],[9.61187,54.85548],[9.73563,54.8247],[9.89314,54.84171],[10.16755,54.73883],[10.31111,54.65968],[11.00303,54.63689],[11.90309,54.38543],[12.85844,54.82438],[13.93395,54.84044],[15.36991,54.73263],[15.79951,55.54655],[14.89259,55.5623],[14.28399,55.1553],[12.84405,55.13257],[12.60345,55.42675],[12.88472,55.63369],[12.6372,55.91371],[12.65312,56.04345],[12.07466,56.29488],[12.16597,56.60205]]]]}},{type:"Feature",properties:{iso1A2:"DM",iso1A3:"DMA",iso1N3:"212",wikidata:"Q784",nameEn:"Dominica",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 767"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.51867,14.96709],[-60.69955,15.22234],[-60.95725,15.70997],[-61.44899,15.79571],[-61.81728,15.58058],[-61.51867,14.96709]]]]}},{type:"Feature",properties:{iso1A2:"DO",iso1A3:"DOM",iso1N3:"214",wikidata:"Q786",nameEn:"Dominican Republic",groups:["029","003","419","019"],callingCodes:["1 809","1 829","1 849"]},geometry:{type:"MultiPolygon",coordinates:[[[[-67.87844,21.7938],[-72.38946,20.27111],[-71.77419,19.73128],[-71.75865,19.70231],[-71.7429,19.58445],[-71.71449,19.55364],[-71.71268,19.53374],[-71.6802,19.45008],[-71.69448,19.37866],[-71.77766,19.33823],[-71.73229,19.26686],[-71.62642,19.21212],[-71.65337,19.11759],[-71.69938,19.10916],[-71.71088,19.08353],[-71.74088,19.0437],[-71.88102,18.95007],[-71.77766,18.95007],[-71.72624,18.87802],[-71.71885,18.78423],[-71.82556,18.62551],[-71.95412,18.64939],[-72.00201,18.62312],[-71.88102,18.50125],[-71.90875,18.45821],[-71.69952,18.34101],[-71.78271,18.18302],[-71.75465,18.14405],[-71.74994,18.11115],[-71.73783,18.07177],[-71.75671,18.03456],[-72.29523,17.48026],[-68.39466,16.14167],[-67.87844,21.7938]]]]}},{type:"Feature",properties:{iso1A2:"DZ",iso1A3:"DZA",iso1N3:"012",wikidata:"Q262",nameEn:"Algeria",groups:["015","002"],callingCodes:["213"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.59123,37.14286],[2.46645,37.97429],[-2.27707,35.35051],[-2.21248,35.08532],[-2.21445,35.04378],[-2.04734,34.93218],[-1.97833,34.93218],[-1.97469,34.886],[-1.73707,34.74226],[-1.84569,34.61907],[-1.69788,34.48056],[-1.78042,34.39018],[-1.64666,34.10405],[-1.73494,33.71721],[-1.59508,33.59929],[-1.67067,33.27084],[-1.46249,33.0499],[-1.54244,32.95499],[-1.37794,32.73628],[-0.9912,32.52467],[-1.24998,32.32993],[-1.24453,32.1917],[-1.15735,32.12096],[-1.22829,32.07832],[-2.46166,32.16603],[-2.93873,32.06557],[-2.82784,31.79459],[-3.66314,31.6339],[-3.66386,31.39202],[-3.77647,31.31912],[-3.77103,31.14984],[-3.54944,31.0503],[-3.65418,30.85566],[-3.64735,30.67539],[-4.31774,30.53229],[-4.6058,30.28343],[-5.21671,29.95253],[-5.58831,29.48103],[-5.72121,29.52322],[-5.75616,29.61407],[-6.69965,29.51623],[-6.78351,29.44634],[-6.95824,29.50924],[-7.61585,29.36252],[-8.6715,28.71194],[-8.66879,27.6666],[-8.66674,27.31569],[-4.83423,24.99935],[1.15698,21.12843],[1.20992,20.73533],[3.24648,19.81703],[3.12501,19.1366],[3.36082,18.9745],[4.26651,19.14224],[5.8153,19.45101],[7.38361,20.79165],[7.48273,20.87258],[11.96886,23.51735],[11.62498,24.26669],[11.41061,24.21456],[10.85323,24.5595],[10.33159,24.5465],[10.02432,24.98124],[10.03146,25.35635],[9.38834,26.19288],[9.51696,26.39148],[9.89569,26.57696],[9.78136,29.40961],[9.3876,30.16738],[9.55544,30.23971],[9.07483,32.07865],[8.35999,32.50101],[8.31895,32.83483],[8.1179,33.05086],[8.11433,33.10175],[7.83028,33.18851],[7.73687,33.42114],[7.54088,33.7726],[7.52851,34.06493],[7.66174,34.20167],[7.74207,34.16492],[7.81242,34.21841],[7.86264,34.3987],[8.20482,34.57575],[8.29655,34.72798],[8.25189,34.92009],[8.30727,34.95378],[8.3555,35.10007],[8.47318,35.23376],[8.30329,35.29884],[8.36086,35.47774],[8.35371,35.66373],[8.26472,35.73669],[8.2626,35.91733],[8.40731,36.42208],[8.18936,36.44939],[8.16167,36.48817],[8.47609,36.66607],[8.46537,36.7706],[8.57613,36.78062],[8.67706,36.8364],[8.62972,36.86499],[8.64044,36.9401],[8.59123,37.14286]]]]}},{type:"Feature",properties:{iso1A2:"EA",wikidata:"Q28868874",nameEn:"Ceuta, Melilla",country:"ES",groups:["015","002"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401]]]]}},{type:"Feature",properties:{iso1A2:"EC",iso1A3:"ECU",iso1N3:"218",wikidata:"Q736",nameEn:"Ecuador",groups:["005","419","019"],callingCodes:["593"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.25764,-0.11943],[-75.82927,0.09578],[-76.23441,0.42294],[-76.41215,0.38228],[-76.4094,0.24015],[-76.89177,0.24736],[-77.52001,0.40782],[-77.49984,0.64476],[-77.67815,0.73863],[-77.66416,0.81604],[-77.68613,0.83029],[-77.7148,0.85003],[-77.85677,0.80197],[-78.42749,1.15389],[-78.87137,1.47457],[-93.12365,2.64343],[-92.46744,-2.52874],[-80.30602,-3.39149],[-80.20647,-3.431],[-80.24123,-3.46124],[-80.24475,-3.47846],[-80.24586,-3.48677],[-80.23651,-3.48652],[-80.22629,-3.501],[-80.20535,-3.51667],[-80.21642,-3.5888],[-80.19848,-3.59249],[-80.18741,-3.63994],[-80.19926,-3.68894],[-80.13232,-3.90317],[-80.46386,-4.01342],[-80.4822,-4.05477],[-80.45023,-4.20938],[-80.32114,-4.21323],[-80.46386,-4.41516],[-80.39256,-4.48269],[-80.13945,-4.29786],[-79.79722,-4.47558],[-79.59402,-4.46848],[-79.26248,-4.95167],[-79.1162,-4.97774],[-79.01659,-5.01481],[-78.85149,-4.66795],[-78.68394,-4.60754],[-78.34362,-3.38633],[-78.24589,-3.39907],[-78.22642,-3.51113],[-78.14324,-3.47653],[-78.19369,-3.36431],[-77.94147,-3.05454],[-76.6324,-2.58397],[-76.05203,-2.12179],[-75.57429,-1.55961],[-75.3872,-0.9374],[-75.22862,-0.95588],[-75.22862,-0.60048],[-75.53615,-0.19213],[-75.60169,-0.18708],[-75.61997,-0.10012],[-75.40192,-0.17196],[-75.25764,-0.11943]]]]}},{type:"Feature",properties:{iso1A2:"EE",iso1A3:"EST",iso1N3:"233",wikidata:"Q191",nameEn:"Estonia",aliases:["EW"],groups:["EU","154","150"],callingCodes:["372"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.32936,60.00121],[20.5104,59.15546],[19.84909,57.57876],[22.80496,57.87798],[23.20055,57.56697],[24.26221,57.91787],[24.3579,57.87471],[25.19484,58.0831],[25.28237,57.98539],[25.29581,58.08288],[25.73499,57.90193],[26.05949,57.84744],[26.0324,57.79037],[26.02456,57.78342],[26.027,57.78158],[26.0266,57.77441],[26.02069,57.77169],[26.02415,57.76865],[26.03332,57.7718],[26.0543,57.76105],[26.08098,57.76619],[26.2029,57.7206],[26.1866,57.6849],[26.29253,57.59244],[26.46527,57.56885],[26.54675,57.51813],[26.90364,57.62823],[27.34698,57.52242],[27.31919,57.57672],[27.40393,57.62125],[27.3746,57.66834],[27.52615,57.72843],[27.50171,57.78842],[27.56689,57.83356],[27.78526,57.83963],[27.81841,57.89244],[27.67282,57.92627],[27.62393,58.09462],[27.48541,58.22615],[27.55489,58.39525],[27.36366,58.78381],[27.74429,58.98351],[27.80482,59.1116],[27.87978,59.18097],[27.90911,59.24353],[28.00689,59.28351],[28.14215,59.28934],[28.19284,59.35791],[28.20537,59.36491],[28.21137,59.38058],[28.19061,59.39962],[28.04187,59.47017],[27.85643,59.58538],[26.90044,59.63819],[26.32936,60.00121]]]]}},{type:"Feature",properties:{iso1A2:"EG",iso1A3:"EGY",iso1N3:"818",wikidata:"Q79",nameEn:"Egypt",groups:["015","002"],callingCodes:["20"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.62659,31.82938],[25.63787,31.9359],[25.14001,31.67534],[25.06041,31.57937],[24.83101,31.31921],[25.01077,30.73861],[24.71117,30.17441],[24.99968,29.24574],[24.99885,21.99535],[33.17563,22.00405],[34.0765,22.00501],[37.8565,22.00903],[34.51305,27.70027],[34.46254,27.99552],[34.88293,29.37455],[34.92298,29.45305],[34.26742,31.21998],[34.24012,31.29591],[34.23572,31.2966],[34.21853,31.32363],[34.052,31.46619],[33.62659,31.82938]]]]}},{type:"Feature",properties:{iso1A2:"EH",iso1A3:"ESH",iso1N3:"732",wikidata:"Q6250",nameEn:"Western Sahara",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.66879,27.6666],[-8.77527,27.66663],[-8.71787,26.9898],[-9.08698,26.98639],[-9.56957,26.90042],[-9.81998,26.71379],[-10.68417,26.90984],[-11.35695,26.8505],[-11.23622,26.72023],[-11.38635,26.611],[-11.62052,26.05229],[-12.06001,26.04442],[-12.12281,25.13682],[-12.92147,24.39502],[-13.00628,24.01923],[-13.75627,23.77231],[-14.10361,22.75501],[-14.1291,22.41636],[-14.48112,22.00886],[-14.47329,21.63839],[-14.78487,21.36587],[-16.44269,21.39745],[-16.9978,21.36239],[-17.02707,21.34022],[-17.21511,21.34226],[-17.35589,20.80492],[-17.0471,20.76408],[-17.0695,20.85742],[-17.06781,20.92697],[-17.0396,20.9961],[-17.0357,21.05368],[-16.99806,21.12142],[-16.95474,21.33997],[-13.01525,21.33343],[-13.08438,22.53866],[-13.15313,22.75649],[-13.10753,22.89493],[-13.00412,23.02297],[-12.5741,23.28975],[-12.36213,23.3187],[-12.14969,23.41935],[-12.00251,23.4538],[-12.0002,25.9986],[-8.66721,25.99918],[-8.66674,27.31569],[-8.66879,27.6666]]]]}},{type:"Feature",properties:{iso1A2:"ER",iso1A3:"ERI",iso1N3:"232",wikidata:"Q986",nameEn:"Eritrea",groups:["014","202","002"],callingCodes:["291"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.37609,16.19728],[39.63762,18.37348],[38.57727,17.98125],[38.45916,17.87167],[38.37133,17.66269],[38.13362,17.53906],[37.50967,17.32199],[37.42694,17.04041],[36.99777,17.07172],[36.92193,16.23451],[36.76371,15.80831],[36.69761,15.75323],[36.54276,15.23478],[36.44337,15.14963],[36.54376,14.25597],[36.56536,14.26177],[36.55659,14.28237],[36.63364,14.31172],[36.85787,14.32201],[37.01622,14.2561],[37.09486,14.27155],[37.13206,14.40746],[37.3106,14.44657],[37.47319,14.2149],[37.528,14.18413],[37.91287,14.89447],[38.0364,14.72745],[38.25562,14.67287],[38.3533,14.51323],[38.45748,14.41445],[38.78306,14.4754],[38.98058,14.54895],[39.02834,14.63717],[39.16074,14.65187],[39.14772,14.61827],[39.19547,14.56996],[39.23888,14.56365],[39.26927,14.48801],[39.2302,14.44598],[39.2519,14.40393],[39.37685,14.54402],[39.52756,14.49011],[39.50585,14.55735],[39.58182,14.60987],[39.76632,14.54264],[39.9443,14.41024],[40.07236,14.54264],[40.14649,14.53969],[40.21128,14.39342],[40.25686,14.41445],[40.9167,14.11152],[41.25097,13.60787],[41.62864,13.38626],[42.05841,12.80912],[42.21469,12.75832],[42.2798,12.6355],[42.4037,12.46478],[42.46941,12.52661],[42.6957,12.36201],[42.7996,12.42629],[42.86195,12.58747],[43.29075,12.79154],[42.63806,13.58268],[41.29956,15.565],[41.37609,16.19728]]]]}},{type:"Feature",properties:{iso1A2:"ES",iso1A3:"ESP",iso1N3:"724",wikidata:"Q29",nameEn:"Spain",groups:["EU","039","150"],callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.41312,35.17111],[-2.41265,35.1877],[-2.44896,35.18777],[-2.44887,35.17075],[-2.41312,35.17111]]],[[[-3.90602,35.21494],[-3.88926,35.20841],[-3.88617,35.21406],[-3.90288,35.22024],[-3.90602,35.21494]]],[[[-4.30191,35.17419],[-4.30112,35.17058],[-4.29436,35.17149],[-4.30191,35.17419]]],[[[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[3.17156,42.43545],[3.11379,42.43646],[3.10027,42.42621],[3.08167,42.42748],[3.03734,42.47363],[2.96518,42.46692],[2.94283,42.48174],[2.92107,42.4573],[2.88413,42.45938],[2.86983,42.46843],[2.85675,42.45444],[2.84335,42.45724],[2.77464,42.41046],[2.75497,42.42578],[2.72056,42.42298],[2.65311,42.38771],[2.6747,42.33974],[2.57934,42.35808],[2.55516,42.35351],[2.54382,42.33406],[2.48457,42.33933],[2.43508,42.37568],[2.43299,42.39423],[2.38504,42.39977],[2.25551,42.43757],[2.20578,42.41633],[2.16599,42.42314],[2.12789,42.41291],[2.11621,42.38393],[2.06241,42.35906],[2.00488,42.35399],[1.96482,42.37787],[1.9574,42.42401],[1.94084,42.43039],[1.94061,42.43333],[1.94292,42.44316],[1.93663,42.45439],[1.88853,42.4501],[1.83037,42.48395],[1.76335,42.48863],[1.72515,42.50338],[1.70571,42.48867],[1.66826,42.50779],[1.65674,42.47125],[1.58933,42.46275],[1.57953,42.44957],[1.55937,42.45808],[1.55073,42.43299],[1.5127,42.42959],[1.44529,42.43724],[1.43838,42.47848],[1.41648,42.48315],[1.46661,42.50949],[1.44759,42.54431],[1.41245,42.53539],[1.4234,42.55959],[1.44529,42.56722],[1.42512,42.58292],[1.44197,42.60217],[1.35562,42.71944],[1.15928,42.71407],[1.0804,42.78569],[0.98292,42.78754],[0.96166,42.80629],[0.93089,42.79154],[0.711,42.86372],[0.66121,42.84021],[0.65421,42.75872],[0.67873,42.69458],[0.40214,42.69779],[0.36251,42.72282],[0.29407,42.67431],[0.25336,42.7174],[0.17569,42.73424],[-0.02468,42.68513],[-0.10519,42.72761],[-0.16141,42.79535],[-0.17939,42.78974],[-0.3122,42.84788],[-0.38833,42.80132],[-0.41319,42.80776],[-0.44334,42.79939],[-0.50863,42.82713],[-0.55497,42.77846],[-0.67637,42.88303],[-0.69837,42.87945],[-0.72608,42.89318],[-0.73422,42.91228],[-0.72037,42.92541],[-0.75478,42.96916],[-0.81652,42.95166],[-0.97133,42.96239],[-1.00963,42.99279],[-1.10333,43.0059],[-1.22881,43.05534],[-1.25244,43.04164],[-1.30531,43.06859],[-1.30052,43.09581],[-1.27118,43.11961],[-1.32209,43.1127],[-1.34419,43.09665],[-1.35272,43.02658],[-1.44067,43.047],[-1.47555,43.08372],[-1.41562,43.12815],[-1.3758,43.24511],[-1.40942,43.27272],[-1.45289,43.27049],[-1.50992,43.29481],[-1.55963,43.28828],[-1.57674,43.25269],[-1.61341,43.25269],[-1.63052,43.28591],[-1.62481,43.30726],[-1.69407,43.31378],[-1.73074,43.29481],[-1.7397,43.32979],[-1.75079,43.3317],[-1.75334,43.34107],[-1.77068,43.34396],[-1.78714,43.35476],[-1.78332,43.36399],[-1.79319,43.37497],[-1.77289,43.38957],[-1.81005,43.59738],[-10.14298,44.17365],[-9.14112,41.86623],[-8.87157,41.86488],[-8.81794,41.90375],[-8.75712,41.92833],[-8.74606,41.9469],[-8.7478,41.96282],[-8.69071,41.98862],[-8.6681,41.99703],[-8.65832,42.02972],[-8.64626,42.03668],[-8.63791,42.04691],[-8.59493,42.05708],[-8.58086,42.05147],[-8.54563,42.0537],[-8.5252,42.06264],[-8.52837,42.07658],[-8.48185,42.0811],[-8.44123,42.08218],[-8.42512,42.07199],[-8.40143,42.08052],[-8.38323,42.07683],[-8.36353,42.09065],[-8.33912,42.08358],[-8.32161,42.10218],[-8.29809,42.106],[-8.2732,42.12396],[-8.24681,42.13993],[-8.22406,42.1328],[-8.1986,42.15402],[-8.18947,42.13853],[-8.19406,42.12141],[-8.18178,42.06436],[-8.11729,42.08537],[-8.08847,42.05767],[-8.08796,42.01398],[-8.16232,41.9828],[-8.2185,41.91237],[-8.19551,41.87459],[-8.16944,41.87944],[-8.16455,41.81753],[-8.0961,41.81024],[-8.01136,41.83453],[-7.9804,41.87337],[-7.92336,41.8758],[-7.90707,41.92432],[-7.88751,41.92553],[-7.88055,41.84571],[-7.84188,41.88065],[-7.69848,41.90977],[-7.65774,41.88308],[-7.58603,41.87944],[-7.62188,41.83089],[-7.52737,41.83939],[-7.49803,41.87095],[-7.45566,41.86488],[-7.44759,41.84451],[-7.42854,41.83262],[-7.42864,41.80589],[-7.37092,41.85031],[-7.32366,41.8406],[-7.18677,41.88793],[-7.18549,41.97515],[-7.14115,41.98855],[-7.08574,41.97401],[-7.07596,41.94977],[-7.01078,41.94977],[-6.98144,41.9728],[-6.95537,41.96553],[-6.94396,41.94403],[-6.82174,41.94493],[-6.81196,41.99097],[-6.76959,41.98734],[-6.75004,41.94129],[-6.61967,41.94008],[-6.58544,41.96674],[-6.5447,41.94371],[-6.56752,41.88429],[-6.51374,41.8758],[-6.56426,41.74219],[-6.54633,41.68623],[-6.49907,41.65823],[-6.44204,41.68258],[-6.29863,41.66432],[-6.19128,41.57638],[-6.26777,41.48796],[-6.3306,41.37677],[-6.38553,41.38655],[-6.38551,41.35274],[-6.55937,41.24417],[-6.65046,41.24725],[-6.68286,41.21641],[-6.69711,41.1858],[-6.77319,41.13049],[-6.75655,41.10187],[-6.79241,41.05397],[-6.80942,41.03629],[-6.84781,41.02692],[-6.88843,41.03027],[-6.913,41.03922],[-6.9357,41.02888],[-6.8527,40.93958],[-6.84292,40.89771],[-6.80707,40.88047],[-6.79892,40.84842],[-6.82337,40.84472],[-6.82826,40.74603],[-6.79567,40.65955],[-6.84292,40.56801],[-6.80218,40.55067],[-6.7973,40.51723],[-6.84944,40.46394],[-6.84618,40.42177],[-6.78426,40.36468],[-6.80218,40.33239],[-6.86085,40.2976],[-6.86085,40.26776],[-7.00426,40.23169],[-7.02544,40.18564],[-7.00589,40.12087],[-6.94233,40.10716],[-6.86737,40.01986],[-6.91463,39.86618],[-6.97492,39.81488],[-7.01613,39.66877],[-7.24707,39.66576],[-7.33507,39.64569],[-7.54121,39.66717],[-7.49477,39.58794],[-7.2927,39.45847],[-7.3149,39.34857],[-7.23403,39.27579],[-7.23566,39.20132],[-7.12811,39.17101],[-7.14929,39.11287],[-7.10692,39.10275],[-7.04011,39.11919],[-6.97004,39.07619],[-6.95211,39.0243],[-7.051,38.907],[-7.03848,38.87221],[-7.26174,38.72107],[-7.265,38.61674],[-7.32529,38.44336],[-7.15581,38.27597],[-7.09389,38.17227],[-6.93418,38.21454],[-7.00375,38.01914],[-7.05966,38.01966],[-7.10366,38.04404],[-7.12648,38.00296],[-7.24544,37.98884],[-7.27314,37.90145],[-7.33441,37.81193],[-7.41981,37.75729],[-7.51759,37.56119],[-7.46878,37.47127],[-7.43974,37.38913],[-7.43227,37.25152],[-7.41854,37.23813],[-7.41133,37.20314],[-7.39769,37.16868],[-7.37282,36.96896],[-7.27694,35.93599]],[[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907]]],[[[1.99838,42.44682],[2.01564,42.45171],[1.99216,42.46208],[1.98579,42.47486],[1.99766,42.4858],[1.98916,42.49351],[1.98022,42.49569],[1.97697,42.48568],[1.97227,42.48487],[1.97003,42.48081],[1.96215,42.47854],[1.95606,42.45785],[1.96125,42.45364],[1.98378,42.44697],[1.99838,42.44682]]]]}},{type:"Feature",properties:{iso1A2:"ET",iso1A3:"ETH",iso1N3:"231",wikidata:"Q115",nameEn:"Ethiopia",groups:["014","202","002"],callingCodes:["251"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.4037,12.46478],[42.2798,12.6355],[42.21469,12.75832],[42.05841,12.80912],[41.62864,13.38626],[41.25097,13.60787],[40.9167,14.11152],[40.25686,14.41445],[40.21128,14.39342],[40.14649,14.53969],[40.07236,14.54264],[39.9443,14.41024],[39.76632,14.54264],[39.58182,14.60987],[39.50585,14.55735],[39.52756,14.49011],[39.37685,14.54402],[39.2519,14.40393],[39.2302,14.44598],[39.26927,14.48801],[39.23888,14.56365],[39.19547,14.56996],[39.14772,14.61827],[39.16074,14.65187],[39.02834,14.63717],[38.98058,14.54895],[38.78306,14.4754],[38.45748,14.41445],[38.3533,14.51323],[38.25562,14.67287],[38.0364,14.72745],[37.91287,14.89447],[37.528,14.18413],[37.47319,14.2149],[37.3106,14.44657],[37.13206,14.40746],[37.09486,14.27155],[37.01622,14.2561],[36.85787,14.32201],[36.63364,14.31172],[36.55659,14.28237],[36.56536,14.26177],[36.54376,14.25597],[36.44653,13.95666],[36.48824,13.83954],[36.38993,13.56459],[36.24545,13.36759],[36.13374,12.92665],[36.16651,12.88019],[36.14268,12.70879],[36.01458,12.72478],[35.70476,12.67101],[35.24302,11.91132],[35.11492,11.85156],[35.05832,11.71158],[35.09556,11.56278],[34.95704,11.24448],[35.01215,11.19626],[34.93172,10.95946],[34.97789,10.91559],[34.97491,10.86147],[34.86916,10.78832],[34.86618,10.74588],[34.77532,10.69027],[34.77383,10.74588],[34.59062,10.89072],[34.4372,10.781],[34.2823,10.53508],[34.34783,10.23914],[34.32102,10.11599],[34.22718,10.02506],[34.20484,9.9033],[34.13186,9.7492],[34.08717,9.55243],[34.10229,9.50238],[34.14304,9.04654],[34.14453,8.60204],[34.01346,8.50041],[33.89579,8.4842],[33.87195,8.41938],[33.71407,8.3678],[33.66938,8.44442],[33.54575,8.47094],[33.3119,8.45474],[33.19721,8.40317],[33.1853,8.29264],[33.18083,8.13047],[33.08401,8.05822],[33.0006,7.90333],[33.04944,7.78989],[33.24637,7.77939],[33.32531,7.71297],[33.44745,7.7543],[33.71407,7.65983],[33.87642,7.5491],[34.02984,7.36449],[34.03878,7.27437],[34.01495,7.25664],[34.19369,7.12807],[34.19369,7.04382],[34.35753,6.91963],[34.47669,6.91076],[34.53925,6.82794],[34.53776,6.74808],[34.65096,6.72589],[34.77459,6.5957],[34.87736,6.60161],[35.01738,6.46991],[34.96227,6.26415],[35.00546,5.89387],[35.12611,5.68937],[35.13058,5.62118],[35.31188,5.50106],[35.29938,5.34042],[35.50792,5.42431],[35.8576,5.33413],[35.81968,5.10757],[35.82118,4.77382],[35.9419,4.61933],[35.95449,4.53244],[36.03924,4.44406],[36.84474,4.44518],[37.07724,4.33503],[38.14168,3.62487],[38.45812,3.60445],[38.52336,3.62551],[38.91938,3.51198],[39.07736,3.5267],[39.19954,3.47834],[39.49444,3.45521],[39.51551,3.40895],[39.55132,3.39634],[39.58339,3.47434],[39.76808,3.67058],[39.86043,3.86974],[40.77498,4.27683],[41.1754,3.94079],[41.89488,3.97375],[42.07619,4.17667],[42.55853,4.20518],[42.84526,4.28357],[42.97746,4.44032],[43.04177,4.57923],[43.40263,4.79289],[44.02436,4.9451],[44.98104,4.91821],[47.97917,8.00124],[47.92477,8.00111],[46.99339,7.9989],[44.19222,8.93028],[43.32613,9.59205],[43.23518,9.84605],[43.0937,9.90579],[42.87643,10.18441],[42.69452,10.62672],[42.95776,10.98533],[42.79037,10.98493],[42.75111,11.06992],[42.62989,11.09711],[42.42669,10.98493],[42.13691,10.97586],[42.06302,10.92599],[41.80056,10.97127],[41.8096,11.33606],[41.77727,11.49902],[41.82878,11.72361],[41.95461,11.81157],[42.4037,12.46478]]]]}},{type:"Feature",properties:{iso1A2:"EU",iso1A3:"EUE",wikidata:"Q458",nameEn:"European Union",level:"union",isoStatus:"excRes"},geometry:null},{type:"Feature",properties:{iso1A2:"FI",iso1A3:"FIN",iso1N3:"246",wikidata:"Q33",nameEn:"Finland",aliases:["SF"],groups:["EU","154","150"],callingCodes:["358"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.12697,69.69193],[28.36883,69.81658],[28.32849,69.88605],[27.97558,69.99671],[27.95542,70.0965],[27.57226,70.06215],[27.05802,69.92069],[26.64461,69.96565],[26.40261,69.91377],[25.96904,69.68397],[25.69679,69.27039],[25.75729,68.99383],[25.61613,68.89602],[25.42455,68.90328],[25.12206,68.78684],[25.10189,68.63307],[24.93048,68.61102],[24.90023,68.55579],[24.74898,68.65143],[24.18432,68.73936],[24.02299,68.81601],[23.781,68.84514],[23.68017,68.70276],[23.13064,68.64684],[22.53321,68.74393],[22.38367,68.71561],[22.27276,68.89514],[21.63833,69.27485],[21.27827,69.31281],[21.00732,69.22755],[20.98641,69.18809],[21.11099,69.10291],[21.05775,69.0356],[20.72171,69.11874],[20.55258,69.06069],[20.78802,69.03087],[20.91658,68.96764],[20.85104,68.93142],[20.90649,68.89696],[21.03001,68.88969],[22.00429,68.50692],[22.73028,68.40881],[23.10336,68.26551],[23.15377,68.14759],[23.26469,68.15134],[23.40081,68.05545],[23.65793,67.9497],[23.45627,67.85297],[23.54701,67.59306],[23.39577,67.46974],[23.75372,67.43688],[23.75372,67.29914],[23.54701,67.25435],[23.58735,67.20752],[23.56214,67.17038],[23.98563,66.84149],[23.98059,66.79585],[23.89488,66.772],[23.85959,66.56434],[23.63776,66.43568],[23.67591,66.3862],[23.64982,66.30603],[23.71339,66.21299],[23.90497,66.15802],[24.15791,65.85385],[24.14798,65.83466],[24.15107,65.81427],[24.14112,65.39731],[20.15877,63.06556],[19.23413,60.61414],[20.96741,60.71528],[21.15143,60.54555],[21.08159,60.20167],[21.02509,60.12142],[21.35468,59.67511],[20.5104,59.15546],[26.32936,60.00121],[27.44953,60.22766],[27.71177,60.3893],[27.77352,60.52722],[28.47974,60.93365],[28.82816,61.1233],[29.01829,61.17448],[31.10136,62.43042],[31.38369,62.66284],[31.58535,62.91642],[31.29294,63.09035],[31.23244,63.22239],[30.49637,63.46666],[29.98213,63.75795],[30.25437,63.83364],[30.55687,64.09036],[30.4762,64.25728],[30.06279,64.35782],[30.01238,64.57513],[30.12329,64.64862],[30.05271,64.79072],[29.68972,64.80789],[29.61914,65.05993],[29.84096,65.1109],[29.8813,65.22101],[29.61914,65.23791],[29.68972,65.31803],[29.84096,65.56945],[29.74013,65.64025],[29.97205,65.70256],[30.16363,65.66935],[29.91155,66.13863],[28.9839,66.94139],[29.91155,67.51507],[30.02041,67.67523],[29.66955,67.79872],[29.34179,68.06655],[28.62982,68.19816],[28.43941,68.53366],[28.78224,68.86696],[28.45957,68.91417],[28.91738,69.04774],[28.81248,69.11997],[28.8629,69.22395],[29.31664,69.47994],[29.12697,69.69193]]]]}},{type:"Feature",properties:{iso1A2:"FJ",iso1A3:"FJI",iso1N3:"242",wikidata:"Q712",nameEn:"Fiji",groups:["054","009"],driveSide:"left",callingCodes:["679"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-22.5],[179.99999,-22.5],[179.99999,-11.5],[174,-11.5],[174,-22.5]]],[[[-178.60161,-14.95666],[-180,-14.96041],[-180,-22.90585],[-176.74538,-22.89767],[-176.76826,-14.95183],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"FK",iso1A3:"FLK",iso1N3:"238",wikidata:"Q9648",nameEn:"Falkland Islands",country:"GB",groups:["005","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.67376,-55.11859],[-54.56126,-51.26248],[-61.26735,-50.63919],[-63.67376,-55.11859]]]]}},{type:"Feature",properties:{iso1A2:"FM",iso1A3:"FSM",iso1N3:"583",wikidata:"Q702",nameEn:"Federated States of Micronesia",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["691"]},geometry:{type:"MultiPolygon",coordinates:[[[[136.04605,12.45908],[136.27107,6.73747],[156.88247,-1.39237],[165.35175,6.367],[159.04653,10.59067],[136.04605,12.45908]]]]}},{type:"Feature",properties:{iso1A2:"FO",iso1A3:"FRO",iso1N3:"234",wikidata:"Q4628",nameEn:"Faroe Islands",country:"DK",groups:["154","150"],callingCodes:["298"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]]]}},{type:"Feature",properties:{iso1A2:"FR",iso1A3:"FRA",iso1N3:"250",wikidata:"Q142",nameEn:"France",groups:["EU","155","150"],callingCodes:["33"]},geometry:null},{type:"Feature",properties:{iso1A2:"FX",iso1A3:"FXX",iso1N3:"249",wikidata:"Q212429",nameEn:"Metropolitan France",country:"FR",groups:["EU","155","150"],isoStatus:"excRes",callingCodes:["33"]},geometry:{type:"MultiPolygon",coordinates:[[[[2.55904,51.07014],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.65349,49.15373],[-6.13339,48.73907],[-1.81005,43.59738],[-1.77289,43.38957],[-1.79319,43.37497],[-1.78332,43.36399],[-1.78714,43.35476],[-1.77068,43.34396],[-1.75334,43.34107],[-1.75079,43.3317],[-1.7397,43.32979],[-1.73074,43.29481],[-1.69407,43.31378],[-1.62481,43.30726],[-1.63052,43.28591],[-1.61341,43.25269],[-1.57674,43.25269],[-1.55963,43.28828],[-1.50992,43.29481],[-1.45289,43.27049],[-1.40942,43.27272],[-1.3758,43.24511],[-1.41562,43.12815],[-1.47555,43.08372],[-1.44067,43.047],[-1.35272,43.02658],[-1.34419,43.09665],[-1.32209,43.1127],[-1.27118,43.11961],[-1.30052,43.09581],[-1.30531,43.06859],[-1.25244,43.04164],[-1.22881,43.05534],[-1.10333,43.0059],[-1.00963,42.99279],[-0.97133,42.96239],[-0.81652,42.95166],[-0.75478,42.96916],[-0.72037,42.92541],[-0.73422,42.91228],[-0.72608,42.89318],[-0.69837,42.87945],[-0.67637,42.88303],[-0.55497,42.77846],[-0.50863,42.82713],[-0.44334,42.79939],[-0.41319,42.80776],[-0.38833,42.80132],[-0.3122,42.84788],[-0.17939,42.78974],[-0.16141,42.79535],[-0.10519,42.72761],[-0.02468,42.68513],[0.17569,42.73424],[0.25336,42.7174],[0.29407,42.67431],[0.36251,42.72282],[0.40214,42.69779],[0.67873,42.69458],[0.65421,42.75872],[0.66121,42.84021],[0.711,42.86372],[0.93089,42.79154],[0.96166,42.80629],[0.98292,42.78754],[1.0804,42.78569],[1.15928,42.71407],[1.35562,42.71944],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.76335,42.48863],[1.83037,42.48395],[1.88853,42.4501],[1.93663,42.45439],[1.94292,42.44316],[1.94061,42.43333],[1.94084,42.43039],[1.9574,42.42401],[1.96482,42.37787],[2.00488,42.35399],[2.06241,42.35906],[2.11621,42.38393],[2.12789,42.41291],[2.16599,42.42314],[2.20578,42.41633],[2.25551,42.43757],[2.38504,42.39977],[2.43299,42.39423],[2.43508,42.37568],[2.48457,42.33933],[2.54382,42.33406],[2.55516,42.35351],[2.57934,42.35808],[2.6747,42.33974],[2.65311,42.38771],[2.72056,42.42298],[2.75497,42.42578],[2.77464,42.41046],[2.84335,42.45724],[2.85675,42.45444],[2.86983,42.46843],[2.88413,42.45938],[2.92107,42.4573],[2.94283,42.48174],[2.96518,42.46692],[3.03734,42.47363],[3.08167,42.42748],[3.10027,42.42621],[3.11379,42.43646],[3.17156,42.43545],[3.4481,42.4358],[7.60802,41.05927],[10.09675,41.44089],[9.56115,43.20816],[7.50102,43.51859],[7.42422,43.72209],[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.47823,43.73341],[7.53006,43.78405],[7.50423,43.84345],[7.49355,43.86551],[7.51162,43.88301],[7.56075,43.89932],[7.56858,43.94506],[7.60771,43.95772],[7.65266,43.9763],[7.66848,43.99943],[7.6597,44.03009],[7.72508,44.07578],[7.66878,44.12795],[7.68694,44.17487],[7.63245,44.17877],[7.62155,44.14881],[7.36364,44.11882],[7.34547,44.14359],[7.27827,44.1462],[7.16929,44.20352],[7.00764,44.23736],[6.98221,44.28289],[6.89171,44.36637],[6.88784,44.42043],[6.94504,44.43112],[6.86233,44.49834],[6.85507,44.53072],[6.96042,44.62129],[6.95133,44.66264],[7.00582,44.69364],[7.07484,44.68073],[7.00401,44.78782],[7.02217,44.82519],[6.93499,44.8664],[6.90774,44.84322],[6.75518,44.89915],[6.74519,44.93661],[6.74791,45.01939],[6.66981,45.02324],[6.62803,45.11175],[6.7697,45.16044],[6.85144,45.13226],[6.96706,45.20841],[7.07074,45.21228],[7.13115,45.25386],[7.10572,45.32924],[7.18019,45.40071],[7.00037,45.509],[6.98948,45.63869],[6.80785,45.71864],[6.80785,45.83265],[6.95315,45.85163],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.59301,47.60058],[7.58851,47.60794],[7.57423,47.61628],[7.5591,47.63849],[7.53384,47.65115],[7.52067,47.66437],[7.51915,47.68335],[7.51266,47.70197],[7.53722,47.71635],[7.54761,47.72912],[7.52921,47.77747],[7.55673,47.87371],[7.62302,47.97898],[7.56966,48.03265],[7.57137,48.12292],[7.6648,48.22219],[7.69022,48.30018],[7.74562,48.32736],[7.73109,48.39192],[7.76833,48.48945],[7.80647,48.51239],[7.80167,48.54758],[7.80057,48.5857],[7.84098,48.64217],[7.89002,48.66317],[7.96812,48.72491],[7.96994,48.75606],[8.01534,48.76085],[8.0326,48.79017],[8.06802,48.78957],[8.10253,48.81829],[8.12813,48.87985],[8.19989,48.95825],[8.20031,48.95856],[8.22604,48.97352],[8.14189,48.97833],[7.97783,49.03161],[7.93641,49.05544],[7.86386,49.03499],[7.79557,49.06583],[7.75948,49.04562],[7.63618,49.05428],[7.62575,49.07654],[7.56416,49.08136],[7.53012,49.09818],[7.49172,49.13915],[7.49473,49.17],[7.44455,49.16765],[7.44052,49.18354],[7.3662,49.17308],[7.35995,49.14399],[7.3195,49.14231],[7.29514,49.11426],[7.23473,49.12971],[7.1593,49.1204],[7.1358,49.1282],[7.12504,49.14253],[7.10384,49.13787],[7.10715,49.15631],[7.07859,49.15031],[7.09007,49.13094],[7.07162,49.1255],[7.06642,49.11415],[7.05548,49.11185],[7.04843,49.11422],[7.04409,49.12123],[7.04662,49.13724],[7.03178,49.15734],[7.0274,49.17042],[7.03459,49.19096],[7.01318,49.19018],[6.97273,49.2099],[6.95963,49.203],[6.94028,49.21641],[6.93831,49.2223],[6.91875,49.22261],[6.89298,49.20863],[6.85939,49.22376],[6.83555,49.21249],[6.85119,49.20038],[6.85016,49.19354],[6.86225,49.18185],[6.84703,49.15734],[6.83385,49.15162],[6.78265,49.16793],[6.73765,49.16375],[6.71137,49.18808],[6.73256,49.20486],[6.71843,49.2208],[6.69274,49.21661],[6.66583,49.28065],[6.60186,49.31055],[6.572,49.35027],[6.58807,49.35358],[6.60091,49.36864],[6.533,49.40748],[6.55404,49.42464],[6.42432,49.47683],[6.40274,49.46546],[6.39168,49.4667],[6.38352,49.46463],[6.36778,49.46937],[6.3687,49.4593],[6.28818,49.48465],[6.27875,49.503],[6.25029,49.50609],[6.2409,49.51408],[6.19543,49.50536],[6.17386,49.50934],[6.15366,49.50226],[6.16115,49.49297],[6.14321,49.48796],[6.12814,49.49365],[6.12346,49.4735],[6.10325,49.4707],[6.09845,49.46351],[6.10072,49.45268],[6.08373,49.45594],[6.07887,49.46399],[6.05553,49.46663],[6.04176,49.44801],[6.02743,49.44845],[6.02648,49.45451],[5.97693,49.45513],[5.96876,49.49053],[5.94224,49.49608],[5.94128,49.50034],[5.86571,49.50015],[5.83389,49.52152],[5.83467,49.52717],[5.84466,49.53027],[5.83648,49.5425],[5.81664,49.53775],[5.80871,49.5425],[5.81838,49.54777],[5.79195,49.55228],[5.77435,49.56298],[5.7577,49.55915],[5.75649,49.54321],[5.64505,49.55146],[5.60909,49.51228],[5.55001,49.52729],[5.46541,49.49825],[5.46734,49.52648],[5.43713,49.5707],[5.3974,49.61596],[5.34837,49.62889],[5.33851,49.61599],[5.3137,49.61225],[5.30214,49.63055],[5.33039,49.6555],[5.31465,49.66846],[5.26232,49.69456],[5.14545,49.70287],[5.09249,49.76193],[4.96714,49.79872],[4.85464,49.78995],[4.86965,49.82271],[4.85134,49.86457],[4.88529,49.9236],[4.78827,49.95609],[4.8382,50.06738],[4.88602,50.15182],[4.83279,50.15331],[4.82438,50.16878],[4.75237,50.11314],[4.70064,50.09384],[4.68695,49.99685],[4.5414,49.96911],[4.51098,49.94659],[4.43488,49.94122],[4.35051,49.95315],[4.31963,49.97043],[4.20532,49.95803],[4.14239,49.98034],[4.13508,50.01976],[4.16294,50.04719],[4.23101,50.06945],[4.20147,50.13535],[4.13561,50.13078],[4.16014,50.19239],[4.15524,50.21103],[4.21945,50.25539],[4.20651,50.27333],[4.17861,50.27443],[4.17347,50.28838],[4.15524,50.2833],[4.16808,50.25786],[4.13665,50.25609],[4.11954,50.30425],[4.10957,50.30234],[4.10237,50.31247],[4.0689,50.3254],[4.0268,50.35793],[3.96771,50.34989],[3.90781,50.32814],[3.84314,50.35219],[3.73911,50.34809],[3.70987,50.3191],[3.71009,50.30305],[3.66976,50.34563],[3.65709,50.36873],[3.67262,50.38663],[3.67494,50.40239],[3.66153,50.45165],[3.64426,50.46275],[3.61014,50.49568],[3.58361,50.49049],[3.5683,50.50192],[3.49509,50.48885],[3.51564,50.5256],[3.47385,50.53397],[3.44629,50.51009],[3.37693,50.49538],[3.28575,50.52724],[3.2729,50.60718],[3.23951,50.6585],[3.264,50.67668],[3.2536,50.68977],[3.26141,50.69151],[3.26063,50.70086],[3.24593,50.71389],[3.22042,50.71019],[3.20845,50.71662],[3.19017,50.72569],[3.20064,50.73547],[3.18811,50.74025],[3.18339,50.74981],[3.16476,50.76843],[3.15017,50.79031],[3.1257,50.78603],[3.11987,50.79188],[3.11206,50.79416],[3.10614,50.78303],[3.09163,50.77717],[3.04314,50.77674],[3.00537,50.76588],[2.96778,50.75242],[2.95019,50.75138],[2.90873,50.702],[2.91036,50.6939],[2.90069,50.69263],[2.88504,50.70656],[2.87937,50.70298],[2.86985,50.7033],[2.8483,50.72276],[2.81056,50.71773],[2.71165,50.81295],[2.63331,50.81457],[2.59093,50.91751],[2.63074,50.94746],[2.57551,51.00326],[2.55904,51.07014]]]]}},{type:"Feature",properties:{iso1A2:"GA",iso1A3:"GAB",iso1N3:"266",wikidata:"Q1000",nameEn:"Gabon",groups:["017","202","002"],callingCodes:["241"]},geometry:{type:"MultiPolygon",coordinates:[[[[13.29457,2.16106],[13.28534,2.25716],[11.37116,2.29975],[11.3561,2.17217],[11.35307,1.00251],[9.79648,1.0019],[9.78058,1.03996],[9.76085,1.05949],[9.73014,1.06721],[9.68638,1.06836],[9.66092,1.05865],[9.62096,1.03039],[9.54793,1.0185],[9.51998,0.96418],[9.35563,0.84865],[7.24416,-0.64092],[10.75913,-4.39519],[11.12647,-3.94169],[11.22301,-3.69888],[11.48764,-3.51089],[11.57949,-3.52798],[11.68608,-3.68942],[11.87083,-3.71571],[11.92719,-3.62768],[11.8318,-3.5812],[11.96554,-3.30267],[11.70227,-3.17465],[11.70558,-3.0773],[11.80365,-3.00424],[11.64798,-2.81146],[11.5359,-2.85654],[11.64487,-2.61865],[11.57637,-2.33379],[11.74605,-2.39936],[11.96866,-2.33559],[12.04895,-2.41704],[12.47925,-2.32626],[12.44656,-1.92025],[12.61312,-1.8129],[12.82172,-1.91091],[13.02759,-2.33098],[13.47977,-2.43224],[13.75884,-2.09293],[13.92073,-2.35581],[13.85846,-2.46935],[14.10442,-2.49268],[14.23829,-2.33715],[14.16202,-2.23916],[14.23518,-2.15671],[14.25932,-1.97624],[14.41838,-1.89412],[14.52569,-0.57818],[14.41887,-0.44799],[14.2165,-0.38261],[14.06862,-0.20826],[13.90632,-0.2287],[13.88648,0.26652],[14.10909,0.58563],[14.26066,0.57255],[14.48179,0.9152],[14.25186,1.39842],[13.89582,1.4261],[13.15519,1.23368],[13.25447,1.32339],[13.13461,1.57238],[13.29457,2.16106]]]]}},{type:"Feature",properties:{iso1A2:"GB",iso1A3:"GBR",iso1N3:"826",wikidata:"Q145",nameEn:"United Kingdom",aliases:["UK","Britain","Great Britain"],groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.83481,53.87749],[-4.1819,54.57861],[-3.64906,54.12723],[-5.37267,53.63269],[-5.79914,52.03902],[-7.74976,48.64773],[1.17405,50.74239],[2.18458,51.52087],[2.56575,51.85301],[-0.3751,61.32236],[-14.78497,57.60709],[-7.93366,55.84142],[-6.79943,55.54107],[-6.71944,55.27952],[-6.9734,55.19878],[-7.2471,55.06933],[-7.34464,55.04688],[-7.4033,55.00391],[-7.40004,54.94498],[-7.44404,54.9403],[-7.4473,54.87003],[-7.47626,54.83084],[-7.54508,54.79401],[-7.54671,54.74606],[-7.64449,54.75265],[-7.75041,54.7103],[-7.83352,54.73854],[-7.93293,54.66603],[-7.70315,54.62077],[-7.8596,54.53671],[-7.99812,54.54427],[-8.04538,54.48941],[-8.179,54.46763],[-8.04555,54.36292],[-7.87101,54.29299],[-7.8596,54.21779],[-7.81397,54.20159],[-7.69501,54.20731],[-7.55812,54.12239],[-7.4799,54.12239],[-7.44567,54.1539],[-7.32834,54.11475],[-7.30553,54.11869],[-7.34005,54.14698],[-7.29157,54.17191],[-7.28017,54.16714],[-7.29687,54.1354],[-7.29493,54.12013],[-7.26316,54.13863],[-7.25012,54.20063],[-7.14908,54.22732],[-7.19145,54.31296],[-7.02034,54.4212],[-6.87775,54.34682],[-6.85179,54.29176],[-6.81583,54.22791],[-6.74575,54.18788],[-6.70175,54.20218],[-6.6382,54.17071],[-6.66264,54.0666],[-6.62842,54.03503],[-6.47849,54.06947],[-6.36605,54.07234],[-6.36279,54.11248],[-6.32694,54.09337],[-6.29003,54.11278],[-6.26218,54.09785],[-5.83481,53.87749]]],[[[33.70575,34.97947],[33.83531,34.73974],[33.98684,34.76642],[33.90075,34.96623],[33.86432,34.97592],[33.84811,34.97075],[33.83505,34.98108],[33.85621,34.98956],[33.85891,35.001],[33.85216,35.00579],[33.84045,35.00616],[33.82875,35.01685],[33.83055,35.02865],[33.81524,35.04192],[33.8012,35.04786],[33.82051,35.0667],[33.8355,35.05777],[33.85261,35.0574],[33.88367,35.07877],[33.89485,35.06873],[33.90247,35.07686],[33.91299,35.07579],[33.91789,35.08688],[33.89853,35.11377],[33.88737,35.11408],[33.88943,35.12007],[33.88561,35.12449],[33.87224,35.12293],[33.87622,35.10457],[33.87097,35.09389],[33.87479,35.08881],[33.8541,35.07201],[33.84168,35.06823],[33.82067,35.07826],[33.78581,35.05104],[33.76106,35.04253],[33.73824,35.05321],[33.71482,35.03722],[33.70209,35.04882],[33.7161,35.07279],[33.70861,35.07644],[33.69095,35.06237],[33.68474,35.06602],[33.67742,35.05963],[33.67678,35.03866],[33.69938,35.03123],[33.69731,35.01754],[33.71514,35.00294],[33.70639,34.99303],[33.70575,34.97947]],[[33.77312,34.9976],[33.77553,34.99518],[33.78516,34.99582],[33.79191,34.98914],[33.78917,34.98854],[33.78571,34.98951],[33.78318,34.98699],[33.78149,34.98854],[33.77843,34.988],[33.7778,34.98981],[33.76738,34.99188],[33.76605,34.99543],[33.75682,34.99916],[33.75994,35.00113],[33.77312,34.9976]],[[33.74144,35.01053],[33.7343,35.01178],[33.73781,35.02181],[33.74265,35.02329],[33.74983,35.02274],[33.7492,35.01319],[33.74144,35.01053]]],[[[32.86014,34.70585],[32.82717,34.70622],[32.79433,34.67883],[32.76136,34.68318],[32.75515,34.64985],[32.74412,34.43926],[33.26744,34.49942],[33.0138,34.64424],[32.96968,34.64046],[32.96718,34.63446],[32.95891,34.62919],[32.95323,34.64075],[32.95471,34.64528],[32.94976,34.65204],[32.94796,34.6587],[32.95325,34.66462],[32.97079,34.66112],[32.97736,34.65277],[32.99014,34.65518],[32.98668,34.67268],[32.99135,34.68061],[32.95539,34.68471],[32.94683,34.67907],[32.94379,34.67111],[32.93693,34.67027],[32.93449,34.66241],[32.92807,34.66736],[32.93043,34.67091],[32.91398,34.67343],[32.9068,34.66102],[32.86167,34.68734],[32.86014,34.70585]]]]}},{type:"Feature",properties:{iso1A2:"GD",iso1A3:"GRD",iso1N3:"308",wikidata:"Q769",nameEn:"Grenada",aliases:["WG"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 473"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.14806,11.87638],[-61.57265,11.65795],[-61.13395,12.51526],[-61.38256,12.52991],[-61.73897,12.61191],[-62.14806,11.87638]]]]}},{type:"Feature",properties:{iso1A2:"GE",iso1A3:"GEO",iso1N3:"268",wikidata:"Q230",nameEn:"Georgia",groups:["145","142"],callingCodes:["995"]},geometry:{type:"MultiPolygon",coordinates:[[[[46.42738,41.91323],[45.61676,42.20768],[45.78692,42.48358],[45.36501,42.55268],[45.15318,42.70598],[44.88754,42.74934],[44.80941,42.61277],[44.70002,42.74679],[44.54202,42.75699],[43.95517,42.55396],[43.73119,42.62043],[43.81453,42.74297],[43.0419,43.02413],[43.03322,43.08883],[42.75889,43.19651],[42.66667,43.13917],[42.40563,43.23226],[41.64935,43.22331],[40.65957,43.56212],[40.10657,43.57344],[40.04445,43.47776],[40.03312,43.44262],[40.01007,43.42411],[40.01552,43.42025],[40.00853,43.40578],[40.0078,43.38551],[39.81147,43.06294],[40.89217,41.72528],[41.54366,41.52185],[41.7148,41.4932],[41.7124,41.47417],[41.81939,41.43621],[41.95134,41.52466],[42.26387,41.49346],[42.51772,41.43606],[42.59202,41.58183],[42.72794,41.59714],[42.84471,41.58912],[42.78995,41.50126],[42.84899,41.47265],[42.8785,41.50516],[43.02956,41.37891],[43.21707,41.30331],[43.13373,41.25503],[43.1945,41.25242],[43.23096,41.17536],[43.36118,41.2028],[43.44973,41.17666],[43.4717,41.12611],[43.67712,41.13398],[43.74717,41.1117],[43.84835,41.16329],[44.16591,41.19141],[44.18148,41.24644],[44.32139,41.2079],[44.34337,41.20312],[44.34417,41.2382],[44.46791,41.18204],[44.59322,41.1933],[44.61462,41.24018],[44.72814,41.20338],[44.82084,41.21513],[44.87887,41.20195],[44.89911,41.21366],[44.84358,41.23088],[44.81749,41.23488],[44.80053,41.25949],[44.81437,41.30371],[44.93493,41.25685],[45.0133,41.29747],[45.09867,41.34065],[45.1797,41.42231],[45.26285,41.46433],[45.31352,41.47168],[45.4006,41.42402],[45.45973,41.45898],[45.68389,41.3539],[45.71035,41.36208],[45.75705,41.35157],[45.69946,41.29545],[45.80842,41.2229],[45.95786,41.17956],[46.13221,41.19479],[46.27698,41.19011],[46.37661,41.10805],[46.456,41.09984],[46.48558,41.0576],[46.55096,41.1104],[46.63969,41.09515],[46.66148,41.20533],[46.72375,41.28609],[46.63658,41.37727],[46.4669,41.43331],[46.40307,41.48464],[46.33925,41.4963],[46.29794,41.5724],[46.34126,41.57454],[46.34039,41.5947],[46.3253,41.60912],[46.28182,41.60089],[46.26531,41.63339],[46.24429,41.59883],[46.19759,41.62327],[46.17891,41.72094],[46.20538,41.77205],[46.23962,41.75811],[46.30863,41.79133],[46.3984,41.84399],[46.42738,41.91323]]]]}},{type:"Feature",properties:{iso1A2:"GF",iso1A3:"GUF",iso1N3:"254",wikidata:"Q3769",nameEn:"French Guiana",country:"FR",groups:["EU","005","419","019"],callingCodes:["594"]},geometry:{type:"MultiPolygon",coordinates:[[[[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383]]]]}},{type:"Feature",properties:{iso1A2:"GG",iso1A3:"GGY",iso1N3:"831",wikidata:"Q25230",nameEn:"Guernsey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.65349,49.15373],[-2.36485,49.48223],[-2.09454,49.46288],[-2.02963,49.91866],[-3.28154,49.57329],[-2.65349,49.15373]]]]}},{type:"Feature",properties:{iso1A2:"GH",iso1A3:"GHA",iso1N3:"288",wikidata:"Q117",nameEn:"Ghana",groups:["011","202","002"],callingCodes:["233"]},geometry:{type:"MultiPolygon",coordinates:[[[[-0.13493,11.14075],[-0.27374,11.17157],[-0.28566,11.12713],[-0.35955,11.07801],[-0.38219,11.12596],[-0.42391,11.11661],[-0.44298,11.04292],[-0.61937,10.91305],[-0.67143,10.99811],[-2.83373,11.0067],[-2.94232,10.64281],[-2.83108,10.40252],[-2.74174,9.83172],[-2.76534,9.56589],[-2.68802,9.49343],[-2.69814,9.22717],[-2.77799,9.04949],[-2.66357,9.01771],[-2.58243,8.7789],[-2.49037,8.20872],[-2.62901,8.11495],[-2.61232,8.02645],[-2.67787,8.02055],[-2.74819,7.92613],[-2.78395,7.94974],[-2.79467,7.86002],[-2.92339,7.60847],[-2.97822,7.27165],[-2.95438,7.23737],[-3.23327,6.81744],[-3.21954,6.74407],[-3.25999,6.62521],[-3.01896,5.71697],[-2.95323,5.71865],[-2.96671,5.6415],[-2.93132,5.62137],[-2.85378,5.65156],[-2.76614,5.60963],[-2.72737,5.34789],[-2.77625,5.34621],[-2.73074,5.1364],[-2.75502,5.10657],[-2.95261,5.12477],[-2.96554,5.10397],[-3.063,5.13665],[-3.11073,5.12675],[-3.10675,5.08515],[-3.34019,4.17519],[1.07031,5.15655],[1.27574,5.93551],[1.19771,6.11522],[1.19966,6.17069],[1.09187,6.17074],[1.05969,6.22998],[1.03108,6.24064],[0.99652,6.33779],[0.89283,6.33779],[0.71048,6.53083],[0.74862,6.56517],[0.63659,6.63857],[0.6497,6.73682],[0.58176,6.76049],[0.57406,6.80348],[0.52853,6.82921],[0.56508,6.92971],[0.52098,6.94391],[0.52217,6.9723],[0.59606,7.01252],[0.65327,7.31643],[0.62943,7.41099],[0.57223,7.39326],[0.52455,7.45354],[0.51979,7.58706],[0.58295,7.62368],[0.62943,7.85751],[0.58891,8.12779],[0.6056,8.13959],[0.61156,8.18324],[0.5913,8.19622],[0.63897,8.25873],[0.73432,8.29529],[0.64731,8.48866],[0.47211,8.59945],[0.37319,8.75262],[0.52455,8.87746],[0.45424,9.04581],[0.56388,9.40697],[0.49118,9.48339],[0.36485,9.49749],[0.33148,9.44812],[0.25758,9.42696],[0.2254,9.47869],[0.31241,9.50337],[0.30406,9.521],[0.2409,9.52335],[0.23851,9.57389],[0.38153,9.58682],[0.36008,9.6256],[0.29334,9.59387],[0.26712,9.66437],[0.28261,9.69022],[0.32313,9.6491],[0.34816,9.66907],[0.34816,9.71607],[0.32075,9.72781],[0.36366,10.03309],[0.41252,10.02018],[0.41371,10.06361],[0.35293,10.09412],[0.39584,10.31112],[0.33028,10.30408],[0.29453,10.41546],[0.18846,10.4096],[0.12886,10.53149],[-0.05945,10.63458],[-0.09141,10.7147],[-0.07327,10.71845],[-0.07183,10.76794],[-0.0228,10.81916],[-0.02685,10.8783],[-0.00908,10.91644],[-0.0063,10.96417],[0.03355,10.9807],[0.02395,11.06229],[0.00342,11.08317],[-0.00514,11.10763],[-0.0275,11.11202],[-0.05733,11.08628],[-0.14462,11.10811],[-0.13493,11.14075]]]]}},{type:"Feature",properties:{iso1A2:"GI",iso1A3:"GIB",iso1N3:"292",wikidata:"Q1410",nameEn:"Gibraltar",country:"GB",groups:["039","150"],callingCodes:["350"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907]]]]}},{type:"Feature",properties:{iso1A2:"GL",iso1A3:"GRL",iso1N3:"304",wikidata:"Q223",nameEn:"Greenland",country:"DK",groups:["021","003","019"],callingCodes:["299"]},geometry:{type:"MultiPolygon",coordinates:[[[[-45.47832,84.58738],[-68.21821,80.48551],[-76.75614,76.72014],[-46.37635,57.3249],[-9.68082,72.73731],[-5.7106,84.28058],[-45.47832,84.58738]]]]}},{type:"Feature",properties:{iso1A2:"GM",iso1A3:"GMB",iso1N3:"270",wikidata:"Q1005",nameEn:"The Gambia",groups:["011","202","002"],callingCodes:["220"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.14917,13.57989],[-14.36795,13.23033],[-13.79409,13.34472],[-13.8955,13.59126],[-14.34721,13.46578],[-14.93719,13.80173],[-15.36504,13.79313],[-15.47902,13.58758],[-17.43598,13.59273],[-17.43966,13.04579],[-16.74676,13.06025],[-16.69343,13.16791],[-15.80355,13.16729],[-15.80478,13.34832],[-15.26908,13.37768],[-15.14917,13.57989]]]]}},{type:"Feature",properties:{iso1A2:"GN",iso1A3:"GIN",iso1N3:"324",wikidata:"Q1006",nameEn:"Guinea",groups:["011","202","002"],callingCodes:["224"]},geometry:{type:"MultiPolygon",coordinates:[[[[-11.37536,12.40788],[-11.46267,12.44559],[-11.91331,12.42008],[-12.35415,12.32758],[-12.87336,12.51892],[-13.06603,12.49342],[-13.05296,12.64003],[-13.70523,12.68013],[-13.7039,12.60313],[-13.65089,12.49515],[-13.64168,12.42764],[-13.70851,12.24978],[-13.92745,12.24077],[-13.94589,12.16869],[-13.7039,12.00869],[-13.7039,11.70195],[-14.09799,11.63649],[-14.26623,11.67486],[-14.31513,11.60713],[-14.51173,11.49708],[-14.66677,11.51188],[-14.77786,11.36323],[-14.95993,10.99244],[-15.07174,10.89557],[-15.96748,10.162],[-14.36218,8.64107],[-13.29911,9.04245],[-13.18586,9.0925],[-13.08953,9.0409],[-12.94095,9.26335],[-12.76788,9.3133],[-12.47254,9.86834],[-12.24262,9.92386],[-12.12634,9.87203],[-11.91023,9.93927],[-11.89624,9.99763],[-11.2118,10.00098],[-10.6534,9.29919],[-10.74484,9.07998],[-10.5783,9.06386],[-10.56197,8.81225],[-10.47707,8.67669],[-10.61422,8.5314],[-10.70565,8.29235],[-10.63934,8.35326],[-10.54891,8.31174],[-10.37257,8.48941],[-10.27575,8.48711],[-10.203,8.47991],[-10.14579,8.52665],[-10.05375,8.50697],[-10.05873,8.42578],[-9.77763,8.54633],[-9.47415,8.35195],[-9.50898,8.18455],[-9.41445,8.02448],[-9.44928,7.9284],[-9.35724,7.74111],[-9.37465,7.62032],[-9.48161,7.37122],[-9.41943,7.41809],[-9.305,7.42056],[-9.20798,7.38109],[-9.18311,7.30461],[-9.09107,7.1985],[-8.93435,7.2824],[-8.85724,7.26019],[-8.8448,7.35149],[-8.72789,7.51429],[-8.67814,7.69428],[-8.55874,7.70167],[-8.55874,7.62525],[-8.47114,7.55676],[-8.4003,7.6285],[-8.21374,7.54466],[-8.09931,7.78626],[-8.13414,7.87991],[-8.06449,8.04989],[-7.94695,8.00925],[-7.99919,8.11023],[-7.98675,8.20134],[-8.062,8.16071],[-8.2411,8.24196],[-8.22991,8.48438],[-7.92518,8.50652],[-7.65653,8.36873],[-7.69882,8.66148],[-7.95503,8.81146],[-7.92518,8.99332],[-7.73862,9.08422],[-7.90777,9.20456],[-7.85056,9.41812],[-8.03463,9.39604],[-8.14657,9.55062],[-8.09434,9.86936],[-8.15652,9.94288],[-8.11921,10.04577],[-8.01225,10.1021],[-7.97971,10.17117],[-7.9578,10.2703],[-8.10207,10.44649],[-8.22711,10.41722],[-8.32614,10.69273],[-8.2667,10.91762],[-8.35083,11.06234],[-8.66923,10.99397],[-8.40058,11.37466],[-8.80854,11.66715],[-8.94784,12.34842],[-9.13689,12.50875],[-9.38067,12.48446],[-9.32097,12.29009],[-9.63938,12.18312],[-9.714,12.0226],[-10.30604,12.24634],[-10.71897,11.91552],[-10.80355,12.1053],[-10.99758,12.24634],[-11.24136,12.01286],[-11.50006,12.17826],[-11.37536,12.40788]]]]}},{type:"Feature",properties:{iso1A2:"GP",iso1A3:"GLP",iso1N3:"312",wikidata:"Q17012",nameEn:"Guadeloupe",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997]]]]}},{type:"Feature",properties:{iso1A2:"GQ",iso1A3:"GNQ",iso1N3:"226",wikidata:"Q983",nameEn:"Equatorial Guinea",groups:["017","202","002"],callingCodes:["240"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.22018,3.72052],[8.34397,4.30689],[8.05799,3.48284],[8.0168,1.79377],[6.69416,-0.53945],[5.38965,-1.19244],[5.3459,-2.30107],[7.24416,-0.64092],[9.35563,0.84865],[9.51998,0.96418],[9.54793,1.0185],[9.62096,1.03039],[9.66092,1.05865],[9.68638,1.06836],[9.73014,1.06721],[9.76085,1.05949],[9.78058,1.03996],[9.79648,1.0019],[11.35307,1.00251],[11.3561,2.17217],[9.991,2.16561],[9.90749,2.20049],[9.89012,2.20457],[9.84716,2.24676],[9.83238,2.29079],[9.83754,2.32428],[9.82123,2.35097],[9.81162,2.33797],[9.22018,3.72052]]]]}},{type:"Feature",properties:{iso1A2:"GR",iso1A3:"GRC",iso1N3:"300",wikidata:"Q41",nameEn:"Greece",aliases:["EL"],groups:["EU","039","150"],callingCodes:["30"]},geometry:{type:"MultiPolygon",coordinates:[[[[26.03489,40.73051],[26.0754,40.72772],[26.08638,40.73214],[26.12495,40.74283],[26.12854,40.77339],[26.15685,40.80709],[26.21351,40.83298],[26.20856,40.86048],[26.26169,40.9168],[26.29441,40.89119],[26.28623,40.93005],[26.32259,40.94042],[26.35894,40.94292],[26.33297,40.98388],[26.3606,41.02027],[26.31928,41.07386],[26.32259,41.24929],[26.39861,41.25053],[26.5209,41.33993],[26.5837,41.32131],[26.62997,41.34613],[26.61767,41.42281],[26.59742,41.48058],[26.59196,41.60491],[26.5209,41.62592],[26.47958,41.67037],[26.35957,41.71149],[26.30255,41.70925],[26.2654,41.71544],[26.22888,41.74139],[26.21325,41.73223],[26.16841,41.74858],[26.06148,41.70345],[26.07083,41.64584],[26.15146,41.60828],[26.14328,41.55496],[26.17951,41.55409],[26.176,41.50072],[26.14796,41.47533],[26.20288,41.43943],[26.16548,41.42278],[26.12926,41.35878],[25.87919,41.30526],[25.8266,41.34563],[25.70507,41.29209],[25.66183,41.31316],[25.61042,41.30614],[25.55082,41.31667],[25.52394,41.2798],[25.48187,41.28506],[25.28322,41.23411],[25.11611,41.34212],[24.942,41.38685],[24.90928,41.40876],[24.86136,41.39298],[24.82514,41.4035],[24.8041,41.34913],[24.71529,41.41928],[24.61129,41.42278],[24.52599,41.56808],[24.30513,41.51297],[24.27124,41.57682],[24.18126,41.51735],[24.10063,41.54796],[24.06323,41.53222],[24.06908,41.46132],[23.96975,41.44118],[23.91483,41.47971],[23.89613,41.45257],[23.80148,41.43943],[23.76525,41.40175],[23.67644,41.41139],[23.63203,41.37632],[23.52453,41.40262],[23.40416,41.39999],[23.33639,41.36317],[23.31301,41.40525],[23.22771,41.37106],[23.21953,41.33773],[23.1833,41.31755],[22.93334,41.34104],[22.81199,41.3398],[22.76408,41.32225],[22.74538,41.16321],[22.71266,41.13945],[22.65306,41.18168],[22.62852,41.14385],[22.58295,41.11568],[22.5549,41.13065],[22.42285,41.11921],[22.26744,41.16409],[22.17629,41.15969],[22.1424,41.12449],[22.06527,41.15617],[21.90869,41.09191],[21.91102,41.04786],[21.7556,40.92525],[21.69601,40.9429],[21.57448,40.86076],[21.53007,40.90759],[21.41555,40.9173],[21.35595,40.87578],[21.25779,40.86165],[21.21105,40.8855],[21.15262,40.85546],[20.97887,40.85475],[20.98396,40.79109],[20.95752,40.76982],[20.98134,40.76046],[21.05833,40.66586],[21.03932,40.56299],[20.96908,40.51526],[20.94925,40.46625],[20.83688,40.47882],[20.7906,40.42726],[20.78234,40.35803],[20.71789,40.27739],[20.67162,40.09433],[20.62566,40.0897],[20.61081,40.07866],[20.55593,40.06524],[20.51297,40.08168],[20.48487,40.06271],[20.42373,40.06777],[20.37911,39.99058],[20.31135,39.99438],[20.41546,39.82832],[20.41475,39.81437],[20.38572,39.78516],[20.30804,39.81563],[20.29152,39.80421],[20.31961,39.72799],[20.27412,39.69884],[20.22707,39.67459],[20.22376,39.64532],[20.15988,39.652],[20.12956,39.65805],[20.05189,39.69112],[20.00957,39.69227],[19.98042,39.6504],[19.92466,39.69533],[19.97622,39.78684],[19.95905,39.82857],[19.0384,40.35325],[19.20409,39.7532],[22.5213,33.45682],[29.73302,35.92555],[29.69611,36.10365],[29.61805,36.14179],[29.61002,36.1731],[29.48192,36.18377],[29.30783,36.01033],[28.23708,36.56812],[27.95037,36.46155],[27.89482,36.69898],[27.46117,36.53789],[27.24613,36.71622],[27.45627,36.9008],[27.20312,36.94571],[27.14757,37.32],[26.95583,37.64989],[26.99377,37.69034],[27.16428,37.72343],[27.05537,37.9131],[26.21136,38.17558],[26.24183,38.44695],[26.32173,38.48731],[26.21136,38.65436],[26.61814,38.81372],[26.70773,39.0312],[26.43357,39.43096],[25.94257,39.39358],[25.61285,40.17161],[26.04292,40.3958],[25.94795,40.72797],[26.03489,40.73051]]]]}},{type:"Feature",properties:{iso1A2:"GS",iso1A3:"SGS",iso1N3:"239",wikidata:"Q35086",nameEn:"South Georgia and South Sandwich Islands",country:"GB",groups:["005","419","019"],driveSide:"left",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-35.26394,-43.68272],[-53.39656,-59.87088],[-22.31757,-59.85974],[-35.26394,-43.68272]]]]}},{type:"Feature",properties:{iso1A2:"GT",iso1A3:"GTM",iso1N3:"320",wikidata:"Q774",nameEn:"Guatemala",groups:["013","003","419","019"],callingCodes:["502"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.14985,17.81563],[-90.98678,17.81655],[-90.99199,17.25192],[-91.43809,17.25373],[-91.04436,16.92175],[-90.69064,16.70697],[-90.61212,16.49832],[-90.40499,16.40524],[-90.44567,16.07573],[-91.73182,16.07371],[-92.20983,15.26077],[-92.0621,15.07406],[-92.1454,14.98143],[-92.1423,14.88647],[-92.18161,14.84147],[-92.1454,14.6804],[-92.2261,14.53423],[-92.37213,14.39277],[-90.55276,12.8866],[-90.11344,13.73679],[-90.10505,13.85104],[-89.88937,14.0396],[-89.81807,14.07073],[-89.76103,14.02923],[-89.73251,14.04133],[-89.75569,14.07073],[-89.70756,14.1537],[-89.61844,14.21937],[-89.52397,14.22628],[-89.50614,14.26084],[-89.58814,14.33165],[-89.57441,14.41637],[-89.39028,14.44561],[-89.34776,14.43013],[-89.35189,14.47553],[-89.23719,14.58046],[-89.15653,14.57802],[-89.13132,14.71582],[-89.23467,14.85596],[-89.15149,14.97775],[-89.18048,14.99967],[-89.15149,15.07392],[-88.97343,15.14039],[-88.32467,15.63665],[-88.31459,15.66942],[-88.24022,15.69247],[-88.22552,15.72294],[-88.20359,16.03858],[-88.40779,16.09624],[-88.95358,15.88698],[-89.02415,15.9063],[-89.17418,15.90898],[-89.22683,15.88619],[-89.15025,17.04813],[-89.14985,17.81563]]]]}},{type:"Feature",properties:{iso1A2:"GU",iso1A3:"GUM",iso1N3:"316",wikidata:"Q16635",nameEn:"Guam",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 671"]},geometry:{type:"MultiPolygon",coordinates:[[[[146.25931,13.85876],[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876]]]]}},{type:"Feature",properties:{iso1A2:"GW",iso1A3:"GNB",iso1N3:"624",wikidata:"Q1007",nameEn:"Guinea-Bissau",groups:["011","202","002"],callingCodes:["245"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.31513,11.60713],[-14.26623,11.67486],[-14.09799,11.63649],[-13.7039,11.70195],[-13.7039,12.00869],[-13.94589,12.16869],[-13.92745,12.24077],[-13.70851,12.24978],[-13.64168,12.42764],[-13.65089,12.49515],[-13.7039,12.60313],[-13.70523,12.68013],[-15.17582,12.6847],[-15.67302,12.42974],[-16.20591,12.46157],[-16.38191,12.36449],[-16.70562,12.34803],[-17.4623,11.92379],[-15.96748,10.162],[-15.07174,10.89557],[-14.95993,10.99244],[-14.77786,11.36323],[-14.66677,11.51188],[-14.51173,11.49708],[-14.31513,11.60713]]]]}},{type:"Feature",properties:{iso1A2:"GY",iso1A3:"GUY",iso1N3:"328",wikidata:"Q734",nameEn:"Guyana",groups:["005","419","019"],driveSide:"left",callingCodes:["592"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.84822,6.73257],[-59.54058,8.6862],[-59.98508,8.53046],[-59.85562,8.35213],[-59.80661,8.28906],[-59.83156,8.23261],[-59.97059,8.20791],[-60.02407,8.04557],[-60.38056,7.8302],[-60.51959,7.83373],[-60.64793,7.56877],[-60.71923,7.55817],[-60.59802,7.33194],[-60.63367,7.25061],[-60.54098,7.14804],[-60.44116,7.20817],[-60.28074,7.1162],[-60.39419,6.94847],[-60.54873,6.8631],[-61.13632,6.70922],[-61.20762,6.58174],[-61.15058,6.19558],[-61.4041,5.95304],[-60.73204,5.20931],[-60.32352,5.21299],[-60.20944,5.28754],[-59.98129,5.07097],[-60.04189,4.69801],[-60.15953,4.53456],[-59.78878,4.45637],[-59.69361,4.34069],[-59.73353,4.20399],[-59.51963,3.91951],[-59.86899,3.57089],[-59.79769,3.37162],[-59.99733,2.92312],[-59.91177,2.36759],[-59.7264,2.27497],[-59.74066,1.87596],[-59.25583,1.40559],[-58.92072,1.31293],[-58.84229,1.17749],[-58.53571,1.29154],[-58.4858,1.48399],[-58.33887,1.58014],[-58.01873,1.51966],[-57.97336,1.64566],[-57.77281,1.73344],[-57.55743,1.69605],[-57.35073,1.98327],[-57.23981,1.95808],[-57.09109,2.01854],[-57.07092,1.95304],[-56.7659,1.89509],[-56.47045,1.95135],[-56.55439,2.02003],[-56.70519,2.02964],[-57.35891,3.32121],[-58.0307,3.95513],[-57.8699,4.89394],[-57.37442,5.0208],[-57.22536,5.15605],[-57.31629,5.33714],[-56.84822,6.73257]]]]}},{type:"Feature",properties:{iso1A2:"HK",iso1A3:"HKG",iso1N3:"344",wikidata:"Q8646",nameEn:"Hong Kong",country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["852"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.92195,22.13873],[114.50148,22.15017],[114.44998,22.55977],[114.25154,22.55977],[114.22888,22.5436],[114.22185,22.55343],[114.20655,22.55706],[114.18338,22.55444],[114.17247,22.55944],[114.1597,22.56041],[114.15123,22.55163],[114.1482,22.54091],[114.13823,22.54319],[114.12665,22.54003],[114.11656,22.53415],[114.11181,22.52878],[114.1034,22.5352],[114.09692,22.53435],[114.09048,22.53716],[114.08606,22.53276],[114.07817,22.52997],[114.07267,22.51855],[114.06272,22.51617],[114.05729,22.51104],[114.05438,22.5026],[114.03113,22.5065],[113.86771,22.42972],[113.81621,22.2163],[113.83338,22.1826],[113.92195,22.13873]]]]}},{type:"Feature",properties:{iso1A2:"HM",iso1A3:"HMD",iso1N3:"334",wikidata:"Q131198",nameEn:"Heard Island and McDonald Islands",country:"AU",groups:["053","009"],driveSide:"left"},geometry:{type:"MultiPolygon",coordinates:[[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]]]}},{type:"Feature",properties:{iso1A2:"HN",iso1A3:"HND",iso1N3:"340",wikidata:"Q783",nameEn:"Honduras",groups:["013","003","419","019"],callingCodes:["504"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.86109,17.73736],[-88.20359,16.03858],[-88.22552,15.72294],[-88.24022,15.69247],[-88.31459,15.66942],[-88.32467,15.63665],[-88.97343,15.14039],[-89.15149,15.07392],[-89.18048,14.99967],[-89.15149,14.97775],[-89.23467,14.85596],[-89.13132,14.71582],[-89.15653,14.57802],[-89.23719,14.58046],[-89.35189,14.47553],[-89.34776,14.43013],[-89.04187,14.33644],[-88.94608,14.20207],[-88.85785,14.17763],[-88.815,14.11652],[-88.73182,14.10919],[-88.70661,14.04317],[-88.49738,13.97224],[-88.48982,13.86458],[-88.25791,13.91108],[-88.23018,13.99915],[-88.07641,13.98447],[-88.00331,13.86948],[-87.7966,13.91353],[-87.68821,13.80829],[-87.73106,13.75443],[-87.78148,13.52906],[-87.71657,13.50577],[-87.72115,13.46083],[-87.73841,13.44169],[-87.77354,13.45767],[-87.83467,13.44655],[-87.84675,13.41078],[-87.80177,13.35689],[-87.73714,13.32715],[-87.69751,13.25228],[-87.55124,13.12523],[-87.37107,12.98646],[-87.06306,13.00892],[-87.03785,12.98682],[-86.93197,13.05313],[-86.93383,13.18677],[-86.87066,13.30641],[-86.71267,13.30348],[-86.76812,13.79605],[-86.35219,13.77157],[-86.14801,14.04317],[-86.00685,14.08474],[-86.03458,13.99181],[-85.75477,13.8499],[-85.73964,13.9698],[-85.45762,14.11304],[-85.32149,14.2562],[-85.18602,14.24929],[-85.1575,14.53934],[-84.90082,14.80489],[-84.82596,14.82212],[-84.70119,14.68078],[-84.48373,14.63249],[-84.10584,14.76353],[-83.89551,14.76697],[-83.62101,14.89448],[-83.49268,15.01158],[-83.13724,15.00002],[-83.04763,15.03256],[-82.06974,14.49418],[-81.58685,18.0025],[-83.86109,17.73736]]]]}},{type:"Feature",properties:{iso1A2:"HR",iso1A3:"HRV",iso1N3:"191",wikidata:"Q224",nameEn:"Croatia",groups:["EU","039","150"],callingCodes:["385"]},geometry:{type:"MultiPolygon",coordinates:[[[[17.6444,42.88641],[17.5392,42.92787],[17.70879,42.97223],[17.64268,43.08595],[17.46986,43.16559],[17.286,43.33065],[17.25579,43.40353],[17.29699,43.44542],[17.24411,43.49376],[17.15828,43.49376],[17.00585,43.58037],[16.80736,43.76011],[16.75316,43.77157],[16.70922,43.84887],[16.55472,43.95326],[16.50528,44.0244],[16.43629,44.02826],[16.43662,44.07523],[16.36864,44.08263],[16.18688,44.27012],[16.21346,44.35231],[16.12969,44.38275],[16.16814,44.40679],[16.10566,44.52586],[16.03012,44.55572],[16.00884,44.58605],[16.05828,44.61538],[15.89348,44.74964],[15.8255,44.71501],[15.72584,44.82334],[15.79472,44.8455],[15.76096,44.87045],[15.74723,44.96818],[15.78568,44.97401],[15.74585,45.0638],[15.78842,45.11519],[15.76371,45.16508],[15.83512,45.22459],[15.98412,45.23088],[16.12153,45.09616],[16.29036,44.99732],[16.35404,45.00241],[16.35863,45.03529],[16.3749,45.05206],[16.38219,45.05139],[16.38309,45.05955],[16.40023,45.1147],[16.4634,45.14522],[16.49155,45.21153],[16.52982,45.22713],[16.5501,45.2212],[16.56559,45.22307],[16.60194,45.23042],[16.64962,45.20714],[16.74845,45.20393],[16.78219,45.19002],[16.81137,45.18434],[16.83804,45.18951],[16.92405,45.27607],[16.9385,45.22742],[17.0415,45.20759],[17.18438,45.14764],[17.24325,45.146],[17.25131,45.14957],[17.26815,45.18444],[17.32092,45.16246],[17.33573,45.14521],[17.41229,45.13335],[17.4498,45.16119],[17.45615,45.12523],[17.47589,45.12656],[17.51469,45.10791],[17.59104,45.10816],[17.66571,45.13408],[17.84826,45.04489],[17.87148,45.04645],[17.93706,45.08016],[17.97336,45.12245],[17.97834,45.13831],[17.99479,45.14958],[18.01594,45.15163],[18.03121,45.12632],[18.1624,45.07654],[18.24387,45.13699],[18.32077,45.1021],[18.41896,45.11083],[18.47939,45.05871],[18.65723,45.07544],[18.78357,44.97741],[18.80661,44.93561],[18.76369,44.93707],[18.76347,44.90669],[18.8704,44.85097],[19.01994,44.85493],[18.98957,44.90645],[19.02871,44.92541],[19.06853,44.89915],[19.15573,44.95409],[19.05205,44.97692],[19.1011,45.01191],[19.07952,45.14668],[19.14063,45.12972],[19.19144,45.17863],[19.43589,45.17137],[19.41941,45.23475],[19.28208,45.23813],[19.10774,45.29547],[18.97446,45.37528],[18.99918,45.49333],[19.08364,45.48804],[19.07471,45.53086],[18.94562,45.53712],[18.88776,45.57253],[18.96691,45.66731],[18.90305,45.71863],[18.85783,45.85493],[18.81394,45.91329],[18.80211,45.87995],[18.6792,45.92057],[18.57483,45.80772],[18.44368,45.73972],[18.12439,45.78905],[18.08869,45.76511],[17.99805,45.79671],[17.87377,45.78522],[17.66545,45.84207],[17.56821,45.93728],[17.35672,45.95209],[17.14592,46.16697],[16.8903,46.28122],[16.8541,46.36255],[16.7154,46.39523],[16.6639,46.45203],[16.59527,46.47524],[16.52604,46.47831],[16.5007,46.49644],[16.44036,46.5171],[16.38771,46.53608],[16.37193,46.55008],[16.29793,46.5121],[16.26733,46.51505],[16.26759,46.50566],[16.23961,46.49653],[16.25124,46.48067],[16.27398,46.42875],[16.27329,46.41467],[16.30162,46.40437],[16.30233,46.37837],[16.18824,46.38282],[16.14859,46.40547],[16.05281,46.39141],[16.05065,46.3833],[16.07314,46.36458],[16.07616,46.3463],[15.97965,46.30652],[15.79284,46.25811],[15.78817,46.21719],[15.75479,46.20336],[15.75436,46.21969],[15.67395,46.22478],[15.6434,46.21396],[15.64904,46.19229],[15.59909,46.14761],[15.6083,46.11992],[15.62317,46.09103],[15.72977,46.04682],[15.71246,46.01196],[15.70327,46.00015],[15.70636,45.92116],[15.67967,45.90455],[15.68383,45.88867],[15.68232,45.86819],[15.70411,45.8465],[15.66662,45.84085],[15.64185,45.82915],[15.57952,45.84953],[15.52234,45.82195],[15.47325,45.8253],[15.47531,45.79802],[15.40836,45.79491],[15.25423,45.72275],[15.30872,45.69014],[15.34919,45.71623],[15.4057,45.64727],[15.38952,45.63682],[15.34214,45.64702],[15.34695,45.63382],[15.31027,45.6303],[15.27747,45.60504],[15.29837,45.5841],[15.30249,45.53224],[15.38188,45.48752],[15.33051,45.45258],[15.27758,45.46678],[15.16862,45.42309],[15.05187,45.49079],[15.02385,45.48533],[14.92266,45.52788],[14.90554,45.47769],[14.81992,45.45913],[14.80124,45.49515],[14.71718,45.53442],[14.68605,45.53006],[14.69694,45.57366],[14.59576,45.62812],[14.60977,45.66403],[14.57397,45.67165],[14.53816,45.6205],[14.5008,45.60852],[14.49769,45.54424],[14.36693,45.48642],[14.32487,45.47142],[14.27681,45.4902],[14.26611,45.48239],[14.24239,45.50607],[14.22371,45.50388],[14.20348,45.46896],[14.07116,45.48752],[14.00578,45.52352],[13.96063,45.50825],[13.99488,45.47551],[13.97309,45.45258],[13.90771,45.45149],[13.88124,45.42637],[13.81742,45.43729],[13.7785,45.46787],[13.67398,45.4436],[13.62902,45.45898],[13.56979,45.4895],[13.45644,45.59464],[13.05142,45.33128],[13.12821,44.48877],[16.15283,42.18525],[18.45131,42.21682],[18.54128,42.39171],[18.52152,42.42302],[18.43588,42.48556],[18.44307,42.51077],[18.43735,42.55921],[18.36197,42.61423],[18.24318,42.6112],[17.88201,42.83668],[17.80854,42.9182],[17.7948,42.89556],[17.68151,42.92725],[17.6444,42.88641]]]]}},{type:"Feature",properties:{iso1A2:"HT",iso1A3:"HTI",iso1N3:"332",wikidata:"Q790",nameEn:"Haiti",aliases:["RH"],groups:["029","003","419","019"],callingCodes:["509"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.71885,18.78423],[-71.72624,18.87802],[-71.77766,18.95007],[-71.88102,18.95007],[-71.74088,19.0437],[-71.71088,19.08353],[-71.69938,19.10916],[-71.65337,19.11759],[-71.62642,19.21212],[-71.73229,19.26686],[-71.77766,19.33823],[-71.69448,19.37866],[-71.6802,19.45008],[-71.71268,19.53374],[-71.71449,19.55364],[-71.7429,19.58445],[-71.75865,19.70231],[-71.77419,19.73128],[-72.38946,20.27111],[-73.37289,20.43199],[-74.7289,18.71009],[-74.76465,18.06252],[-72.29523,17.48026],[-71.75671,18.03456],[-71.73783,18.07177],[-71.74994,18.11115],[-71.75465,18.14405],[-71.78271,18.18302],[-71.69952,18.34101],[-71.90875,18.45821],[-71.88102,18.50125],[-72.00201,18.62312],[-71.95412,18.64939],[-71.82556,18.62551],[-71.71885,18.78423]]]]}},{type:"Feature",properties:{iso1A2:"HU",iso1A3:"HUN",iso1N3:"348",wikidata:"Q28",nameEn:"Hungary",groups:["EU","151","150"],callingCodes:["36"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.72525,48.34628],[21.67134,48.3989],[21.6068,48.50365],[21.44063,48.58456],[21.11516,48.49546],[20.83248,48.5824],[20.5215,48.53336],[20.29943,48.26104],[20.24312,48.2784],[19.92452,48.1283],[19.63338,48.25006],[19.52489,48.19791],[19.47957,48.09437],[19.28182,48.08336],[19.23924,48.0595],[19.01952,48.07052],[18.82176,48.04206],[18.76134,47.97499],[18.76821,47.87469],[18.8506,47.82308],[18.74074,47.8157],[18.66521,47.76772],[18.56496,47.76588],[18.29305,47.73541],[18.02938,47.75665],[17.71215,47.7548],[17.23699,48.02094],[17.16001,48.00636],[17.09786,47.97336],[17.11022,47.92461],[17.08275,47.87719],[17.00997,47.86245],[17.07039,47.81129],[17.05048,47.79377],[17.08893,47.70928],[16.87538,47.68895],[16.86509,47.72268],[16.82938,47.68432],[16.7511,47.67878],[16.72089,47.73469],[16.65679,47.74197],[16.61183,47.76171],[16.54779,47.75074],[16.53514,47.73837],[16.55129,47.72268],[16.4222,47.66537],[16.58699,47.61772],[16.64193,47.63114],[16.71059,47.52692],[16.64821,47.50155],[16.6718,47.46139],[16.57152,47.40868],[16.52414,47.41007],[16.49908,47.39416],[16.45104,47.41181],[16.47782,47.25918],[16.44142,47.25079],[16.43663,47.21127],[16.41739,47.20649],[16.42801,47.18422],[16.4523,47.18812],[16.46442,47.16845],[16.44932,47.14418],[16.52863,47.13974],[16.46134,47.09395],[16.52176,47.05747],[16.43936,47.03548],[16.51369,47.00084],[16.28202,47.00159],[16.27594,46.9643],[16.22403,46.939],[16.19904,46.94134],[16.10983,46.867],[16.14365,46.8547],[16.15711,46.85434],[16.21892,46.86961],[16.2365,46.87775],[16.2941,46.87137],[16.34547,46.83836],[16.3408,46.80641],[16.31303,46.79838],[16.30966,46.7787],[16.37816,46.69975],[16.42641,46.69228],[16.41863,46.66238],[16.38594,46.6549],[16.39217,46.63673],[16.50139,46.56684],[16.52885,46.53303],[16.52604,46.5051],[16.59527,46.47524],[16.6639,46.45203],[16.7154,46.39523],[16.8541,46.36255],[16.8903,46.28122],[17.14592,46.16697],[17.35672,45.95209],[17.56821,45.93728],[17.66545,45.84207],[17.87377,45.78522],[17.99805,45.79671],[18.08869,45.76511],[18.12439,45.78905],[18.44368,45.73972],[18.57483,45.80772],[18.6792,45.92057],[18.80211,45.87995],[18.81394,45.91329],[18.99712,45.93537],[19.01284,45.96529],[19.0791,45.96458],[19.10388,46.04015],[19.14543,45.9998],[19.28826,45.99694],[19.52473,46.1171],[19.56113,46.16824],[19.66007,46.19005],[19.81491,46.1313],[19.93508,46.17553],[20.01816,46.17696],[20.03533,46.14509],[20.09713,46.17315],[20.26068,46.12332],[20.28324,46.1438],[20.35573,46.16629],[20.45377,46.14405],[20.49718,46.18721],[20.63863,46.12728],[20.76085,46.21002],[20.74574,46.25467],[20.86797,46.28884],[21.06572,46.24897],[21.16872,46.30118],[21.28061,46.44941],[21.26929,46.4993],[21.33214,46.63035],[21.43926,46.65109],[21.5151,46.72147],[21.48935,46.7577],[21.52028,46.84118],[21.59307,46.86935],[21.59581,46.91628],[21.68645,46.99595],[21.648,47.03902],[21.78395,47.11104],[21.94463,47.38046],[22.01055,47.37767],[22.03389,47.42508],[22.00917,47.50492],[22.31816,47.76126],[22.41979,47.7391],[22.46559,47.76583],[22.67247,47.7871],[22.76617,47.8417],[22.77991,47.87211],[22.89849,47.95851],[22.84276,47.98602],[22.87847,48.04665],[22.81804,48.11363],[22.73427,48.12005],[22.66835,48.09162],[22.58733,48.10813],[22.59007,48.15121],[22.49806,48.25189],[22.38133,48.23726],[22.2083,48.42534],[22.14689,48.4005],[21.83339,48.36242],[21.8279,48.33321],[21.72525,48.34628]]]]}},{type:"Feature",properties:{iso1A2:"IC",wikidata:"Q5813",nameEn:"Canary Islands",country:"ES",groups:["EU","039","150"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.92339,29.50503],[-25.3475,27.87574],[-14.43883,27.02969],[-9.94494,32.97138],[-15.92339,29.50503]]]]}},{type:"Feature",properties:{iso1A2:"ID",iso1A3:"IDN",iso1N3:"360",wikidata:"Q252",nameEn:"Indonesia",aliases:["RI"],groups:["035","142"],driveSide:"left",callingCodes:["62"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.02352,0.08993],[128.97621,3.08804],[126.69413,6.02692],[124.97752,4.82064],[118.41402,3.99509],[118.07935,4.15511],[117.89538,4.16637],[117.67641,4.16535],[117.47313,4.18857],[117.25801,4.35108],[115.90217,4.37708],[115.58276,3.93499],[115.53713,3.14776],[115.11343,2.82879],[115.1721,2.49671],[114.80706,2.21665],[114.80706,1.92351],[114.57892,1.5],[114.03788,1.44787],[113.64677,1.23933],[113.01448,1.42832],[113.021,1.57819],[112.48648,1.56516],[112.2127,1.44135],[112.15679,1.17004],[111.94553,1.12016],[111.82846,0.99349],[111.55434,0.97864],[111.22979,1.08326],[110.62374,0.873],[110.49182,0.88088],[110.35354,0.98869],[109.66397,1.60425],[109.66397,1.79972],[109.57923,1.80624],[109.53794,1.91771],[109.62558,1.99182],[109.64506,2.08014],[109.71058,2.32059],[108.10426,5.42408],[105.01437,3.24936],[104.56723,1.44271],[104.34728,1.33529],[104.12282,1.27714],[104.03085,1.26954],[103.74084,1.12902],[103.66049,1.18825],[103.56591,1.19719],[103.03657,1.30383],[96.11174,6.69841],[74.28481,-3.17525],[122.14954,-11.52517],[125.68138,-9.85176],[125.09025,-9.46406],[124.97892,-9.19281],[125.04044,-9.17093],[125.09434,-9.19669],[125.18907,-9.16434],[125.18632,-9.03142],[125.11764,-8.96359],[124.97742,-9.08128],[124.94011,-8.85617],[124.46701,-9.13002],[124.45971,-9.30263],[124.38554,-9.3582],[124.35258,-9.43002],[124.3535,-9.48493],[124.28115,-9.50453],[124.28115,-9.42189],[124.21247,-9.36904],[124.14517,-9.42324],[124.10539,-9.41206],[124.04286,-9.34243],[124.04628,-9.22671],[124.33472,-9.11416],[124.92337,-8.75859],[125.31127,-8.22976],[125.65946,-8.06136],[125.87691,-8.31789],[127.42116,-8.22471],[127.55165,-9.05052],[140.88922,-9.34945],[141.00782,-9.1242],[141.01763,-6.90181],[140.85295,-6.72996],[140.99813,-6.3233],[141.02352,0.08993]]]]}},{type:"Feature",properties:{iso1A2:"IE",iso1A3:"IRL",iso1N3:"372",wikidata:"Q27",nameEn:"Ireland",groups:["EU","154","150"],driveSide:"left",callingCodes:["353"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.26218,54.09785],[-6.29003,54.11278],[-6.32694,54.09337],[-6.36279,54.11248],[-6.36605,54.07234],[-6.47849,54.06947],[-6.62842,54.03503],[-6.66264,54.0666],[-6.6382,54.17071],[-6.70175,54.20218],[-6.74575,54.18788],[-6.81583,54.22791],[-6.85179,54.29176],[-6.87775,54.34682],[-7.02034,54.4212],[-7.19145,54.31296],[-7.14908,54.22732],[-7.25012,54.20063],[-7.26316,54.13863],[-7.29493,54.12013],[-7.29687,54.1354],[-7.28017,54.16714],[-7.29157,54.17191],[-7.34005,54.14698],[-7.30553,54.11869],[-7.32834,54.11475],[-7.44567,54.1539],[-7.4799,54.12239],[-7.55812,54.12239],[-7.69501,54.20731],[-7.81397,54.20159],[-7.8596,54.21779],[-7.87101,54.29299],[-8.04555,54.36292],[-8.179,54.46763],[-8.04538,54.48941],[-7.99812,54.54427],[-7.8596,54.53671],[-7.70315,54.62077],[-7.93293,54.66603],[-7.83352,54.73854],[-7.75041,54.7103],[-7.64449,54.75265],[-7.54671,54.74606],[-7.54508,54.79401],[-7.47626,54.83084],[-7.4473,54.87003],[-7.44404,54.9403],[-7.40004,54.94498],[-7.4033,55.00391],[-7.34464,55.04688],[-7.2471,55.06933],[-6.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-22.01468,48.19557],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785]]]]}},{type:"Feature",properties:{iso1A2:"IL",iso1A3:"ISR",iso1N3:"376",wikidata:"Q801",nameEn:"Israel",groups:["145","142"],callingCodes:["972"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.29262,31.70393],[34.48681,31.59711],[34.56797,31.54197],[34.48892,31.48365],[34.40077,31.40926],[34.36505,31.36404],[34.37381,31.30598],[34.36523,31.28963],[34.29417,31.24194],[34.26742,31.21998],[34.92298,29.45305],[34.97718,29.54294],[34.98207,29.58147],[35.02147,29.66343],[35.14108,30.07374],[35.19183,30.34636],[35.16218,30.43535],[35.19595,30.50297],[35.21379,30.60401],[35.29311,30.71365],[35.33456,30.81224],[35.33984,30.8802],[35.41371,30.95565],[35.43658,31.12444],[35.40316,31.25535],[35.47672,31.49578],[35.39675,31.49572],[35.22921,31.37445],[35.13033,31.3551],[35.02459,31.35979],[34.92571,31.34337],[34.88932,31.37093],[34.87833,31.39321],[34.89756,31.43891],[34.93258,31.47816],[34.94356,31.50743],[34.9415,31.55601],[34.95249,31.59813],[35.00879,31.65426],[35.08226,31.69107],[35.10782,31.71594],[35.11895,31.71454],[35.12933,31.7325],[35.13931,31.73012],[35.15119,31.73634],[35.15474,31.73352],[35.16478,31.73242],[35.18023,31.72067],[35.20538,31.72388],[35.21937,31.71578],[35.22392,31.71899],[35.23972,31.70896],[35.24315,31.71244],[35.2438,31.7201],[35.24981,31.72543],[35.25182,31.73945],[35.26319,31.74846],[35.25225,31.7678],[35.26058,31.79064],[35.25573,31.81362],[35.26404,31.82567],[35.251,31.83085],[35.25753,31.8387],[35.24816,31.8458],[35.2304,31.84222],[35.2249,31.85433],[35.22817,31.8638],[35.22567,31.86745],[35.22294,31.87889],[35.22014,31.88264],[35.2136,31.88241],[35.21276,31.88153],[35.21016,31.88237],[35.20945,31.8815],[35.20791,31.8821],[35.20673,31.88151],[35.20381,31.86716],[35.21128,31.863],[35.216,31.83894],[35.21469,31.81835],[35.19461,31.82687],[35.18169,31.82542],[35.18603,31.80901],[35.14174,31.81325],[35.07677,31.85627],[35.05617,31.85685],[35.01978,31.82944],[34.9724,31.83352],[34.99712,31.85569],[35.03489,31.85919],[35.03978,31.89276],[35.03489,31.92448],[35.00124,31.93264],[34.98682,31.96935],[35.00261,32.027],[34.9863,32.09551],[34.99437,32.10962],[34.98507,32.12606],[34.99039,32.14626],[34.96009,32.17503],[34.95703,32.19522],[34.98885,32.20758],[35.01841,32.23981],[35.02939,32.2671],[35.01119,32.28684],[35.01772,32.33863],[35.04243,32.35008],[35.05142,32.3667],[35.0421,32.38242],[35.05311,32.4024],[35.05423,32.41754],[35.07059,32.4585],[35.08564,32.46948],[35.09236,32.47614],[35.10024,32.47856],[35.10882,32.4757],[35.15937,32.50466],[35.2244,32.55289],[35.25049,32.52453],[35.29306,32.50947],[35.30685,32.51024],[35.35212,32.52047],[35.40224,32.50136],[35.42034,32.46009],[35.41598,32.45593],[35.41048,32.43706],[35.42078,32.41562],[35.55807,32.38674],[35.55494,32.42687],[35.57485,32.48669],[35.56614,32.64393],[35.59813,32.65159],[35.61669,32.67999],[35.66527,32.681],[35.68467,32.70715],[35.75983,32.74803],[35.78745,32.77938],[35.83758,32.82817],[35.84021,32.8725],[35.87012,32.91976],[35.89298,32.9456],[35.87188,32.98028],[35.84802,33.1031],[35.81911,33.11077],[35.81911,33.1336],[35.84285,33.16673],[35.83846,33.19397],[35.81647,33.2028],[35.81295,33.24841],[35.77513,33.27342],[35.813,33.3172],[35.77477,33.33609],[35.62019,33.27278],[35.62283,33.24226],[35.58502,33.26653],[35.58326,33.28381],[35.56523,33.28969],[35.55555,33.25844],[35.54544,33.25513],[35.54808,33.236],[35.5362,33.23196],[35.54228,33.19865],[35.52573,33.11921],[35.50335,33.114],[35.50272,33.09056],[35.448,33.09264],[35.43059,33.06659],[35.35223,33.05617],[35.31429,33.10515],[35.1924,33.08743],[35.10645,33.09318],[34.78515,33.20368],[33.62659,31.82938],[34.052,31.46619]]]]}},{type:"Feature",properties:{iso1A2:"IM",iso1A3:"IMN",iso1N3:"833",wikidata:"Q9676",nameEn:"Isle of Man",country:"GB",groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01624","44 07624","44 07524","44 07924"]},geometry:{type:"MultiPolygon",coordinates:[[[[-3.64906,54.12723],[-4.1819,54.57861],[-5.83481,53.87749],[-5.37267,53.63269],[-3.64906,54.12723]]]]}},{type:"Feature",properties:{iso1A2:"IN",iso1A3:"IND",iso1N3:"356",wikidata:"Q668",nameEn:"India",groups:["034","142"],driveSide:"left",callingCodes:["91"]},geometry:{type:"MultiPolygon",coordinates:[[[[78.11664,35.48022],[77.80532,35.52058],[77.70232,35.46244],[77.44277,35.46132],[76.96624,35.5932],[76.84539,35.67356],[76.77323,35.66062],[76.75475,35.52617],[76.85088,35.39754],[76.93465,35.39866],[77.11796,35.05419],[76.99251,34.93349],[76.87193,34.96906],[76.74514,34.92488],[76.74377,34.84039],[76.67648,34.76371],[76.47186,34.78965],[76.15463,34.6429],[76.04614,34.67566],[75.75438,34.51827],[75.38009,34.55021],[75.01479,34.64629],[74.6663,34.703],[74.58083,34.77386],[74.31239,34.79626],[74.12897,34.70073],[73.96423,34.68244],[73.93401,34.63386],[73.93951,34.57169],[73.89419,34.54568],[73.88732,34.48911],[73.74999,34.3781],[73.74862,34.34183],[73.8475,34.32935],[73.90517,34.35317],[73.98208,34.2522],[73.90677,34.10504],[73.88732,34.05105],[73.91341,34.01235],[74.21554,34.03853],[74.25262,34.01577],[74.26086,33.92237],[74.14001,33.83002],[74.05898,33.82089],[74.00891,33.75437],[73.96423,33.73071],[73.98968,33.66155],[73.97367,33.64061],[74.03576,33.56718],[74.10115,33.56392],[74.18121,33.4745],[74.17983,33.3679],[74.08782,33.26232],[74.01366,33.25199],[74.02144,33.18908],[74.15374,33.13477],[74.17571,33.07495],[74.31854,33.02891],[74.34875,32.97823],[74.31227,32.92795],[74.41467,32.90563],[74.45312,32.77755],[74.6289,32.75561],[74.64675,32.82604],[74.7113,32.84219],[74.65345,32.71225],[74.69542,32.66792],[74.64424,32.60985],[74.65251,32.56416],[74.67431,32.56676],[74.68362,32.49298],[74.84725,32.49075],[74.97634,32.45367],[75.03265,32.49538],[75.28259,32.36556],[75.38046,32.26836],[75.25649,32.10187],[75.00793,32.03786],[74.9269,32.0658],[74.86236,32.04485],[74.79919,31.95983],[74.58907,31.87824],[74.47771,31.72227],[74.57498,31.60382],[74.61517,31.55698],[74.59319,31.50197],[74.64713,31.45605],[74.59773,31.4136],[74.53223,31.30321],[74.51629,31.13829],[74.56023,31.08303],[74.60281,31.10419],[74.60006,31.13711],[74.6852,31.12771],[74.67971,31.05479],[74.5616,31.04153],[73.88993,30.36305],[73.95736,30.28466],[73.97225,30.19829],[73.80299,30.06969],[73.58665,30.01848],[73.3962,29.94707],[73.28094,29.56646],[73.05886,29.1878],[73.01337,29.16422],[72.94272,29.02487],[72.40402,28.78283],[72.29495,28.66367],[72.20329,28.3869],[71.9244,28.11555],[71.89921,27.96035],[70.79054,27.68423],[70.60927,28.02178],[70.37307,28.01208],[70.12502,27.8057],[70.03136,27.56627],[69.58519,27.18109],[69.50904,26.74892],[69.88555,26.56836],[70.05584,26.60398],[70.17532,26.55362],[70.17532,26.24118],[70.08193,26.08094],[70.0985,25.93238],[70.2687,25.71156],[70.37444,25.67443],[70.53649,25.68928],[70.60378,25.71898],[70.67382,25.68186],[70.66695,25.39314],[70.89148,25.15064],[70.94002,24.92843],[71.09405,24.69017],[70.97594,24.60904],[71.00341,24.46038],[71.12838,24.42662],[71.04461,24.34657],[70.94985,24.3791],[70.85784,24.30903],[70.88393,24.27398],[70.71502,24.23517],[70.57906,24.27774],[70.5667,24.43787],[70.11712,24.30915],[70.03428,24.172],[69.73335,24.17007],[69.59579,24.29777],[69.29778,24.28712],[69.19341,24.25646],[69.07806,24.29777],[68.97781,24.26021],[68.90914,24.33156],[68.7416,24.31904],[68.74643,23.97027],[68.39339,23.96838],[68.20763,23.85849],[68.11329,23.53945],[72.15131,7.6285],[78.52781,7.63099],[79.50447,8.91876],[79.42124,9.80115],[80.48418,10.20786],[94.53911,5.99016],[94.6371,13.81803],[92.61042,13.76986],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[89.07114,22.15335],[88.9367,22.58527],[88.94614,22.66941],[88.9151,22.75228],[88.96713,22.83346],[88.87063,22.95235],[88.88327,23.03885],[88.86377,23.08759],[88.99148,23.21134],[88.71133,23.2492],[88.79254,23.46028],[88.79351,23.50535],[88.74841,23.47361],[88.56507,23.64044],[88.58087,23.87105],[88.66189,23.87607],[88.73743,23.91751],[88.6976,24.14703],[88.74841,24.1959],[88.68801,24.31464],[88.50934,24.32474],[88.12296,24.51301],[88.08786,24.63232],[88.00683,24.66477],[88.15515,24.85806],[88.14004,24.93529],[88.21832,24.96642],[88.27325,24.88796],[88.33917,24.86803],[88.46277,25.07468],[88.44766,25.20149],[88.94067,25.18534],[89.00463,25.26583],[89.01105,25.30303],[88.85278,25.34679],[88.81296,25.51546],[88.677,25.46959],[88.4559,25.59227],[88.45103,25.66245],[88.242,25.80811],[88.13138,25.78773],[88.08804,25.91334],[88.16581,26.0238],[88.1844,26.14417],[88.34757,26.22216],[88.35153,26.29123],[88.51649,26.35923],[88.48749,26.45855],[88.36938,26.48683],[88.35153,26.45241],[88.33093,26.48929],[88.41196,26.63837],[88.4298,26.54489],[88.62144,26.46783],[88.69485,26.38353],[88.67837,26.26291],[88.78961,26.31093],[88.85004,26.23211],[89.05328,26.2469],[88.91321,26.37984],[88.92357,26.40711],[88.95612,26.4564],[89.08899,26.38845],[89.15869,26.13708],[89.35953,26.0077],[89.53515,26.00382],[89.57101,25.9682],[89.63968,26.22595],[89.70201,26.15138],[89.73581,26.15818],[89.77865,26.08387],[89.77728,26.04254],[89.86592,25.93115],[89.80585,25.82489],[89.84388,25.70042],[89.86129,25.61714],[89.81208,25.37244],[89.84086,25.31854],[89.83371,25.29548],[89.87629,25.28337],[89.90478,25.31038],[90.1155,25.22686],[90.40034,25.1534],[90.65042,25.17788],[90.87427,25.15799],[91.25517,25.20677],[91.63648,25.12846],[92.0316,25.1834],[92.33957,25.07593],[92.39147,25.01471],[92.49887,24.88796],[92.38626,24.86055],[92.25854,24.9191],[92.15796,24.54435],[92.11662,24.38997],[91.96603,24.3799],[91.89258,24.14674],[91.82596,24.22345],[91.76004,24.23848],[91.73257,24.14703],[91.65292,24.22095],[91.63782,24.1132],[91.55542,24.08687],[91.37414,24.10693],[91.35741,23.99072],[91.29587,24.0041],[91.22308,23.89616],[91.25192,23.83463],[91.15579,23.6599],[91.28293,23.37538],[91.36453,23.06612],[91.40848,23.07117],[91.4035,23.27522],[91.46615,23.2328],[91.54993,23.01051],[91.61571,22.93929],[91.7324,23.00043],[91.81634,23.08001],[91.76417,23.26619],[91.84789,23.42235],[91.95642,23.47361],[91.95093,23.73284],[92.04706,23.64229],[92.15417,23.73409],[92.26541,23.70392],[92.38214,23.28705],[92.37665,22.9435],[92.5181,22.71441],[92.60029,22.1522],[92.56616,22.13554],[92.60949,21.97638],[92.67532,22.03547],[92.70416,22.16017],[92.86208,22.05456],[92.89504,21.95143],[92.93899,22.02656],[92.99804,21.98964],[92.99255,22.05965],[93.04885,22.20595],[93.15734,22.18687],[93.14224,22.24535],[93.19991,22.25425],[93.18206,22.43716],[93.13537,22.45873],[93.11477,22.54374],[93.134,22.59573],[93.09417,22.69459],[93.134,22.92498],[93.12988,23.05772],[93.2878,23.00464],[93.38478,23.13698],[93.36862,23.35426],[93.38781,23.36139],[93.39981,23.38828],[93.38805,23.4728],[93.43475,23.68299],[93.3908,23.7622],[93.3908,23.92925],[93.36059,23.93176],[93.32351,24.04468],[93.34735,24.10151],[93.41415,24.07854],[93.46633,23.97067],[93.50616,23.94432],[93.62871,24.00922],[93.75952,24.0003],[93.80279,23.92549],[93.92089,23.95812],[94.14081,23.83333],[94.30215,24.23752],[94.32362,24.27692],[94.45279,24.56656],[94.50729,24.59281],[94.5526,24.70764],[94.60204,24.70889],[94.73937,25.00545],[94.74212,25.13606],[94.57458,25.20318],[94.68032,25.47003],[94.80117,25.49359],[95.18556,26.07338],[95.11428,26.1019],[95.12801,26.38397],[95.05798,26.45408],[95.23513,26.68499],[95.30339,26.65372],[95.437,26.7083],[95.81603,27.01335],[95.93002,27.04149],[96.04949,27.19428],[96.15591,27.24572],[96.40779,27.29818],[96.55761,27.29928],[96.73888,27.36638],[96.88445,27.25046],[96.85287,27.2065],[96.89132,27.17474],[97.14675,27.09041],[97.17422,27.14052],[96.91431,27.45752],[96.90112,27.62149],[97.29919,27.92233],[97.35824,27.87256],[97.38845,28.01329],[97.35412,28.06663],[97.31292,28.06784],[97.34547,28.21385],[97.1289,28.3619],[96.98882,28.32564],[96.88445,28.39452],[96.85561,28.4875],[96.6455,28.61657],[96.48895,28.42955],[96.40929,28.51526],[96.61391,28.72742],[96.3626,29.10607],[96.20467,29.02325],[96.18682,29.11087],[96.31316,29.18643],[96.05361,29.38167],[95.84899,29.31464],[95.75149,29.32063],[95.72086,29.20797],[95.50842,29.13487],[95.41091,29.13007],[95.3038,29.13847],[95.26122,29.07727],[95.2214,29.10727],[95.11291,29.09527],[95.0978,29.14446],[94.81353,29.17804],[94.69318,29.31739],[94.2752,29.11687],[94.35897,29.01965],[93.72797,28.68821],[93.44621,28.67189],[93.18069,28.50319],[93.14635,28.37035],[92.93075,28.25671],[92.67486,28.15018],[92.65472,28.07632],[92.73025,28.05814],[92.7275,27.98662],[92.42538,27.80092],[92.32101,27.79363],[92.27432,27.89077],[91.87057,27.7195],[91.84722,27.76325],[91.6469,27.76358],[91.55819,27.6144],[91.65007,27.48287],[92.01132,27.47352],[92.12019,27.27829],[92.04702,27.26861],[92.03457,27.07334],[92.11863,26.893],[92.05523,26.8692],[91.83181,26.87318],[91.50067,26.79223],[90.67715,26.77215],[90.48504,26.8594],[90.39271,26.90704],[90.30402,26.85098],[90.04535,26.72422],[89.86124,26.73307],[89.63369,26.74402],[89.42349,26.83727],[89.3901,26.84225],[89.38319,26.85963],[89.37913,26.86224],[89.1926,26.81329],[89.12825,26.81661],[89.09554,26.89089],[88.95807,26.92668],[88.92301,26.99286],[88.8714,26.97488],[88.86984,27.10937],[88.74219,27.144],[88.91901,27.32483],[88.82981,27.38814],[88.77517,27.45415],[88.88091,27.85192],[88.83559,28.01936],[88.63235,28.12356],[88.54858,28.06057],[88.25332,27.9478],[88.1278,27.95417],[88.13378,27.88015],[88.1973,27.85067],[88.19107,27.79285],[88.04008,27.49223],[88.07277,27.43007],[88.01646,27.21612],[88.01587,27.21388],[87.9887,27.11045],[88.11719,26.98758],[88.13422,26.98705],[88.12302,26.95324],[88.19107,26.75516],[88.1659,26.68177],[88.16452,26.64111],[88.09963,26.54195],[88.09414,26.43732],[88.00895,26.36029],[87.90115,26.44923],[87.89085,26.48565],[87.84193,26.43663],[87.7918,26.46737],[87.76004,26.40711],[87.67893,26.43501],[87.66803,26.40294],[87.59175,26.38342],[87.55274,26.40596],[87.51571,26.43106],[87.46566,26.44058],[87.37314,26.40815],[87.34568,26.34787],[87.26568,26.37294],[87.26587,26.40592],[87.24682,26.4143],[87.18863,26.40558],[87.14751,26.40542],[87.09147,26.45039],[87.0707,26.58571],[87.04691,26.58685],[87.01559,26.53228],[86.95912,26.52076],[86.94543,26.52076],[86.82898,26.43919],[86.76797,26.45892],[86.74025,26.42386],[86.69124,26.45169],[86.62686,26.46891],[86.61313,26.48658],[86.57073,26.49825],[86.54258,26.53819],[86.49726,26.54218],[86.31564,26.61925],[86.26235,26.61886],[86.22513,26.58863],[86.13596,26.60651],[86.02729,26.66756],[85.8492,26.56667],[85.85126,26.60866],[85.83126,26.61134],[85.76907,26.63076],[85.72315,26.67471],[85.73483,26.79613],[85.66239,26.84822],[85.61621,26.86721],[85.59461,26.85161],[85.5757,26.85955],[85.56471,26.84133],[85.47752,26.79292],[85.34302,26.74954],[85.21159,26.75933],[85.18046,26.80519],[85.19291,26.86909],[85.15883,26.86966],[85.02635,26.85381],[85.05592,26.88991],[85.00536,26.89523],[84.97186,26.9149],[84.96687,26.95599],[84.85754,26.98984],[84.82913,27.01989],[84.793,26.9968],[84.64496,27.04669],[84.69166,27.21294],[84.62161,27.33885],[84.29315,27.39],[84.25735,27.44941],[84.21376,27.45218],[84.10791,27.52399],[84.02229,27.43836],[83.93306,27.44939],[83.86182,27.4241],[83.85595,27.35797],[83.61288,27.47013],[83.39495,27.4798],[83.38872,27.39276],[83.35136,27.33885],[83.29999,27.32778],[83.2673,27.36235],[83.27197,27.38309],[83.19413,27.45632],[82.94938,27.46036],[82.93261,27.50328],[82.74119,27.49838],[82.70378,27.72122],[82.46405,27.6716],[82.06554,27.92222],[81.97214,27.93322],[81.91223,27.84995],[81.47867,28.08303],[81.48179,28.12148],[81.38683,28.17638],[81.32923,28.13521],[81.19847,28.36284],[81.08507,28.38346],[80.89648,28.47237],[80.55142,28.69182],[80.50575,28.6706],[80.52443,28.54897],[80.44504,28.63098],[80.37188,28.63371],[80.12125,28.82346],[80.06957,28.82763],[80.05743,28.91479],[80.18085,29.13649],[80.23178,29.11626],[80.26602,29.13938],[80.24112,29.21414],[80.28626,29.20327],[80.31428,29.30784],[80.24322,29.44299],[80.37939,29.57098],[80.41858,29.63581],[80.38428,29.68513],[80.36803,29.73865],[80.41554,29.79451],[80.43458,29.80466],[80.48997,29.79566],[80.56247,29.86661],[80.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[81.03953,30.20059],[80.83343,30.32023],[80.54504,30.44936],[80.20721,30.58541],[79.93255,30.88288],[79.59884,30.93943],[79.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[78.77898,31.31209],[78.71032,31.50197],[78.84516,31.60631],[78.69933,31.78723],[78.78036,31.99478],[78.74404,32.00384],[78.68754,32.10256],[78.49609,32.2762],[78.4645,32.45367],[78.38897,32.53938],[78.73916,32.69438],[78.7831,32.46873],[78.96713,32.33655],[78.99322,32.37948],[79.0979,32.38051],[79.13174,32.47766],[79.26768,32.53277],[79.46562,32.69668],[79.14016,33.02545],[79.15252,33.17156],[78.73636,33.56521],[78.67599,33.66445],[78.77349,33.73871],[78.73367,34.01121],[78.65657,34.03195],[78.66225,34.08858],[78.91769,34.15452],[78.99802,34.3027],[79.05364,34.32482],[78.74465,34.45174],[78.56475,34.50835],[78.54964,34.57283],[78.27781,34.61484],[78.18435,34.7998],[78.22692,34.88771],[78.00033,35.23954],[78.03466,35.3785],[78.11664,35.48022]]]]}},{type:"Feature",properties:{iso1A2:"IO",iso1A3:"IOT",iso1N3:"086",wikidata:"Q43448",nameEn:"British Indian Ocean Territory",country:"GB",groups:["014","202","002"],callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.64754,-4.95745],[70.67958,-8.2663],[73.70488,-4.92492],[70.64754,-4.95745]]]]}},{type:"Feature",properties:{iso1A2:"IQ",iso1A3:"IRQ",iso1N3:"368",wikidata:"Q796",nameEn:"Iraq",groups:["145","142"],callingCodes:["964"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.78887,37.38615],[42.56725,37.14878],[42.35724,37.10998],[42.36697,37.0627],[41.81736,36.58782],[41.40058,36.52502],[41.28864,36.35368],[41.2564,36.06012],[41.37027,35.84095],[41.38184,35.62502],[41.26569,35.42708],[41.21654,35.1508],[41.2345,34.80049],[41.12388,34.65742],[40.97676,34.39788],[40.64314,34.31604],[38.79171,33.37328],[39.08202,32.50304],[38.98762,32.47694],[39.04251,32.30203],[39.26157,32.35555],[39.29903,32.23259],[40.01521,32.05667],[42.97601,30.72204],[42.97796,30.48295],[44.72255,29.19736],[46.42415,29.05947],[46.5527,29.10283],[46.89695,29.50584],[47.15166,30.01044],[47.37192,30.10421],[47.7095,30.10453],[48.01114,29.98906],[48.06782,30.02906],[48.17332,30.02448],[48.40479,29.85763],[48.59531,29.66815],[48.83867,29.78572],[48.61441,29.93675],[48.51011,29.96238],[48.44785,30.00148],[48.4494,30.04456],[48.43384,30.08233],[48.38869,30.11062],[48.38714,30.13485],[48.41671,30.17254],[48.41117,30.19846],[48.26393,30.3408],[48.24385,30.33846],[48.21279,30.31644],[48.19425,30.32796],[48.18321,30.39703],[48.14585,30.44133],[48.02443,30.4789],[48.03221,30.9967],[47.68219,31.00004],[47.6804,31.39086],[47.86337,31.78422],[47.64771,32.07666],[47.52474,32.15972],[47.57144,32.20583],[47.37529,32.47808],[47.17218,32.45393],[46.46788,32.91992],[46.32298,32.9731],[46.17198,32.95612],[46.09103,32.98354],[46.15175,33.07229],[46.03966,33.09577],[46.05367,33.13097],[46.11905,33.11924],[46.20623,33.20395],[45.99919,33.5082],[45.86687,33.49263],[45.96183,33.55751],[45.89801,33.63661],[45.77814,33.60938],[45.50261,33.94968],[45.42789,33.9458],[45.41077,33.97421],[45.47264,34.03099],[45.56176,34.15088],[45.58667,34.30147],[45.53552,34.35148],[45.49171,34.3439],[45.46697,34.38221],[45.43879,34.45949],[45.51883,34.47692],[45.53219,34.60441],[45.59074,34.55558],[45.60224,34.55057],[45.73923,34.54416],[45.70031,34.69277],[45.65672,34.7222],[45.68284,34.76624],[45.70031,34.82322],[45.73641,34.83975],[45.79682,34.85133],[45.78904,34.91135],[45.86532,34.89858],[45.89477,34.95805],[45.87864,35.03441],[45.92173,35.0465],[45.92203,35.09538],[45.93108,35.08148],[45.94756,35.09188],[46.06508,35.03699],[46.07747,35.0838],[46.11763,35.07551],[46.19116,35.11097],[46.15642,35.1268],[46.16229,35.16984],[46.19738,35.18536],[46.18457,35.22561],[46.11367,35.23729],[46.15474,35.2883],[46.13152,35.32548],[46.05358,35.38568],[45.98453,35.49848],[46.01518,35.52012],[45.97584,35.58132],[46.03028,35.57416],[46.01307,35.59756],[46.0165,35.61501],[45.99452,35.63574],[46.0117,35.65059],[46.01631,35.69139],[46.23736,35.71414],[46.34166,35.78363],[46.32921,35.82655],[46.17198,35.8013],[46.08325,35.8581],[45.94711,35.82218],[45.89784,35.83708],[45.81442,35.82107],[45.76145,35.79898],[45.6645,35.92872],[45.60018,35.96069],[45.55245,35.99943],[45.46594,36.00042],[45.38275,35.97156],[45.33916,35.99424],[45.37652,36.06222],[45.37312,36.09917],[45.32235,36.17383],[45.30038,36.27769],[45.26261,36.3001],[45.27394,36.35846],[45.23953,36.43257],[45.11811,36.40751],[45.00759,36.5402],[45.06985,36.62645],[45.06985,36.6814],[45.01537,36.75128],[44.84725,36.77622],[44.83479,36.81362],[44.90173,36.86096],[44.91199,36.91468],[44.89862,37.01897],[44.81611,37.04383],[44.75229,37.11958],[44.78319,37.1431],[44.76698,37.16162],[44.63179,37.19229],[44.42631,37.05825],[44.38117,37.05825],[44.35315,37.04955],[44.35937,37.02843],[44.30645,36.97373],[44.25975,36.98119],[44.18503,37.09551],[44.22239,37.15756],[44.27998,37.16501],[44.2613,37.25055],[44.13521,37.32486],[44.02002,37.33229],[43.90949,37.22453],[43.84878,37.22205],[43.82699,37.19477],[43.8052,37.22825],[43.7009,37.23692],[43.63085,37.21957],[43.56702,37.25675],[43.50787,37.24436],[43.33508,37.33105],[43.30083,37.30629],[43.11403,37.37436],[42.93705,37.32015],[42.78887,37.38615]]]]}},{type:"Feature",properties:{iso1A2:"IR",iso1A3:"IRN",iso1N3:"364",wikidata:"Q794",nameEn:"Iran",groups:["034","142"],callingCodes:["98"]},geometry:{type:"MultiPolygon",coordinates:[[[[44.96746,39.42998],[44.88916,39.59653],[44.81043,39.62677],[44.71806,39.71124],[44.65422,39.72163],[44.6137,39.78393],[44.47298,39.68788],[44.48111,39.61579],[44.41849,39.56659],[44.42832,39.4131],[44.37921,39.4131],[44.29818,39.378],[44.22452,39.4169],[44.03667,39.39223],[44.1043,39.19842],[44.20946,39.13975],[44.18863,38.93881],[44.30322,38.81581],[44.26155,38.71427],[44.28065,38.6465],[44.32058,38.62752],[44.3207,38.49799],[44.3119,38.37887],[44.38309,38.36117],[44.44386,38.38295],[44.50115,38.33939],[44.42476,38.25763],[44.22509,37.88859],[44.3883,37.85433],[44.45948,37.77065],[44.55498,37.783],[44.62096,37.71985],[44.56887,37.6429],[44.61401,37.60165],[44.58449,37.45018],[44.81021,37.2915],[44.75986,37.21549],[44.7868,37.16644],[44.78319,37.1431],[44.75229,37.11958],[44.81611,37.04383],[44.89862,37.01897],[44.91199,36.91468],[44.90173,36.86096],[44.83479,36.81362],[44.84725,36.77622],[45.01537,36.75128],[45.06985,36.6814],[45.06985,36.62645],[45.00759,36.5402],[45.11811,36.40751],[45.23953,36.43257],[45.27394,36.35846],[45.26261,36.3001],[45.30038,36.27769],[45.32235,36.17383],[45.37312,36.09917],[45.37652,36.06222],[45.33916,35.99424],[45.38275,35.97156],[45.46594,36.00042],[45.55245,35.99943],[45.60018,35.96069],[45.6645,35.92872],[45.76145,35.79898],[45.81442,35.82107],[45.89784,35.83708],[45.94711,35.82218],[46.08325,35.8581],[46.17198,35.8013],[46.32921,35.82655],[46.34166,35.78363],[46.23736,35.71414],[46.01631,35.69139],[46.0117,35.65059],[45.99452,35.63574],[46.0165,35.61501],[46.01307,35.59756],[46.03028,35.57416],[45.97584,35.58132],[46.01518,35.52012],[45.98453,35.49848],[46.05358,35.38568],[46.13152,35.32548],[46.15474,35.2883],[46.11367,35.23729],[46.18457,35.22561],[46.19738,35.18536],[46.16229,35.16984],[46.15642,35.1268],[46.19116,35.11097],[46.11763,35.07551],[46.07747,35.0838],[46.06508,35.03699],[45.94756,35.09188],[45.93108,35.08148],[45.92203,35.09538],[45.92173,35.0465],[45.87864,35.03441],[45.89477,34.95805],[45.86532,34.89858],[45.78904,34.91135],[45.79682,34.85133],[45.73641,34.83975],[45.70031,34.82322],[45.68284,34.76624],[45.65672,34.7222],[45.70031,34.69277],[45.73923,34.54416],[45.60224,34.55057],[45.59074,34.55558],[45.53219,34.60441],[45.51883,34.47692],[45.43879,34.45949],[45.46697,34.38221],[45.49171,34.3439],[45.53552,34.35148],[45.58667,34.30147],[45.56176,34.15088],[45.47264,34.03099],[45.41077,33.97421],[45.42789,33.9458],[45.50261,33.94968],[45.77814,33.60938],[45.89801,33.63661],[45.96183,33.55751],[45.86687,33.49263],[45.99919,33.5082],[46.20623,33.20395],[46.11905,33.11924],[46.05367,33.13097],[46.03966,33.09577],[46.15175,33.07229],[46.09103,32.98354],[46.17198,32.95612],[46.32298,32.9731],[46.46788,32.91992],[47.17218,32.45393],[47.37529,32.47808],[47.57144,32.20583],[47.52474,32.15972],[47.64771,32.07666],[47.86337,31.78422],[47.6804,31.39086],[47.68219,31.00004],[48.03221,30.9967],[48.02443,30.4789],[48.14585,30.44133],[48.18321,30.39703],[48.19425,30.32796],[48.21279,30.31644],[48.24385,30.33846],[48.26393,30.3408],[48.41117,30.19846],[48.41671,30.17254],[48.38714,30.13485],[48.38869,30.11062],[48.43384,30.08233],[48.4494,30.04456],[48.44785,30.00148],[48.51011,29.96238],[48.61441,29.93675],[48.83867,29.78572],[49.98877,27.87827],[50.37726,27.89227],[54.39838,25.68383],[55.14145,25.62624],[55.81777,26.18798],[56.2644,26.58649],[56.68954,26.76645],[56.79239,26.41236],[56.82555,25.7713],[56.86325,25.03856],[61.5251,24.57287],[61.57592,25.0492],[61.6433,25.27541],[61.683,25.66638],[61.83968,25.7538],[61.83831,26.07249],[61.89391,26.26251],[62.05117,26.31647],[62.21304,26.26601],[62.31484,26.528],[62.77352,26.64099],[63.1889,26.65072],[63.18688,26.83844],[63.25005,26.84212],[63.25005,27.08692],[63.32283,27.14437],[63.19649,27.25674],[62.80604,27.22412],[62.79684,27.34381],[62.84905,27.47627],[62.7638,28.02992],[62.79412,28.28108],[62.59499,28.24842],[62.40259,28.42703],[61.93581,28.55284],[61.65978,28.77937],[61.53765,29.00507],[61.31508,29.38903],[60.87231,29.86514],[61.80829,30.84224],[61.78268,30.92724],[61.8335,30.97669],[61.83257,31.0452],[61.80957,31.12576],[61.80569,31.16167],[61.70929,31.37391],[60.84541,31.49561],[60.86191,32.22565],[60.56485,33.12944],[60.88908,33.50219],[60.91133,33.55596],[60.69573,33.56054],[60.57762,33.59772],[60.5485,33.73422],[60.5838,33.80793],[60.50209,34.13992],[60.66502,34.31539],[60.91321,34.30411],[60.72316,34.52857],[60.99922,34.63064],[61.00197,34.70631],[61.06926,34.82139],[61.12831,35.09938],[61.0991,35.27845],[61.18187,35.30249],[61.27371,35.61482],[61.22719,35.67038],[61.26152,35.80749],[61.22444,35.92879],[61.12007,35.95992],[61.22719,36.12759],[61.1393,36.38782],[61.18187,36.55348],[61.14516,36.64644],[60.34767,36.63214],[60.00768,37.04102],[59.74678,37.12499],[59.55178,37.13594],[59.39385,37.34257],[59.39797,37.47892],[59.33507,37.53146],[59.22905,37.51161],[58.9338,37.67374],[58.6921,37.64548],[58.5479,37.70526],[58.47786,37.6433],[58.39959,37.63134],[58.22999,37.6856],[58.21399,37.77281],[57.79534,37.89299],[57.35042,37.98546],[57.37236,38.09321],[57.21169,38.28965],[57.03453,38.18717],[56.73928,38.27887],[56.62255,38.24005],[56.43303,38.26054],[56.32454,38.18502],[56.33278,38.08132],[55.97847,38.08024],[55.76561,38.12238],[55.44152,38.08564],[55.13412,37.94705],[54.851,37.75739],[54.77684,37.62264],[54.81804,37.61285],[54.77822,37.51597],[54.67247,37.43532],[54.58664,37.45809],[54.36211,37.34912],[54.24565,37.32047],[53.89734,37.3464],[48.88288,38.43975],[48.84969,38.45015],[48.81072,38.44853],[48.78979,38.45026],[48.70001,38.40564],[48.62217,38.40198],[48.58793,38.45076],[48.45084,38.61013],[48.3146,38.59958],[48.24773,38.71883],[48.02581,38.82705],[48.01409,38.90333],[48.07734,38.91616],[48.08627,38.94434],[48.28437,38.97186],[48.33884,39.03022],[48.31239,39.09278],[48.15361,39.19419],[48.12404,39.25208],[48.15984,39.30028],[48.37385,39.37584],[48.34264,39.42935],[47.98977,39.70999],[47.84774,39.66285],[47.50099,39.49615],[47.38978,39.45999],[47.31301,39.37492],[47.05927,39.24846],[47.05771,39.20143],[46.95341,39.13505],[46.92539,39.16644],[46.83822,39.13143],[46.75752,39.03231],[46.53497,38.86548],[46.34059,38.92076],[46.20601,38.85262],[46.14785,38.84206],[46.06766,38.87861],[46.00228,38.87376],[45.94624,38.89072],[45.90266,38.87739],[45.83883,38.90768],[45.65172,38.95199],[45.6155,38.94304],[45.6131,38.964],[45.44966,38.99243],[45.44811,39.04927],[45.40452,39.07224],[45.40148,39.09007],[45.30489,39.18333],[45.16168,39.21952],[45.08751,39.35052],[45.05932,39.36435],[44.96746,39.42998]]]]}},{type:"Feature",properties:{iso1A2:"IS",iso1A3:"ISL",iso1N3:"352",wikidata:"Q189",nameEn:"Iceland",groups:["154","150"],callingCodes:["354"]},geometry:{type:"MultiPolygon",coordinates:[[[[-33.15676,62.62995],[-8.25539,63.0423],[-15.70914,69.67442],[-33.15676,62.62995]]]]}},{type:"Feature",properties:{iso1A2:"IT",iso1A3:"ITA",iso1N3:"380",wikidata:"Q38",nameEn:"Italy",groups:["EU","039","150"],callingCodes:["39"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.95861,45.96485],[8.97604,45.96151],[8.97741,45.98317],[8.96668,45.98436],[8.95861,45.96485]]],[[[7.63035,43.57419],[9.56115,43.20816],[10.09675,41.44089],[7.60802,41.05927],[7.89009,38.19924],[11.2718,37.6713],[12.13667,34.20326],[14.02721,36.53141],[17.67657,35.68918],[18.83516,40.36999],[16.15283,42.18525],[13.12821,44.48877],[13.05142,45.33128],[13.45644,45.59464],[13.6076,45.64761],[13.7198,45.59352],[13.74587,45.59811],[13.78445,45.5825],[13.84106,45.58185],[13.86771,45.59898],[13.8695,45.60835],[13.9191,45.6322],[13.87933,45.65207],[13.83422,45.68703],[13.83332,45.70855],[13.8235,45.7176],[13.66986,45.79955],[13.59784,45.8072],[13.58858,45.83503],[13.57563,45.8425],[13.58644,45.88173],[13.59565,45.89446],[13.60857,45.89907],[13.61931,45.91782],[13.63815,45.93607],[13.6329,45.94894],[13.64307,45.98326],[13.63458,45.98947],[13.62074,45.98388],[13.58903,45.99009],[13.56759,45.96991],[13.52963,45.96588],[13.50104,45.98078],[13.47474,46.00546],[13.49702,46.01832],[13.50998,46.04498],[13.49568,46.04839],[13.50104,46.05986],[13.57072,46.09022],[13.64053,46.13587],[13.66472,46.17392],[13.64451,46.18966],[13.56682,46.18703],[13.56114,46.2054],[13.47587,46.22725],[13.42218,46.20758],[13.37671,46.29668],[13.44808,46.33507],[13.43418,46.35992],[13.47019,46.3621],[13.5763,46.40915],[13.5763,46.42613],[13.59777,46.44137],[13.68684,46.43881],[13.7148,46.5222],[13.64088,46.53438],[13.27627,46.56059],[12.94445,46.60401],[12.59992,46.6595],[12.38708,46.71529],[12.27591,46.88651],[12.2006,46.88854],[12.11675,47.01241],[12.21781,47.03996],[12.19254,47.09331],[11.74789,46.98484],[11.50739,47.00644],[11.33355,46.99862],[11.10618,46.92966],[11.00764,46.76896],[10.72974,46.78972],[10.75753,46.82258],[10.66405,46.87614],[10.54783,46.84505],[10.47197,46.85698],[10.38659,46.67847],[10.40475,46.63671],[10.44686,46.64162],[10.49375,46.62049],[10.46136,46.53164],[10.25309,46.57432],[10.23674,46.63484],[10.10307,46.61003],[10.03715,46.44479],[10.165,46.41051],[10.10506,46.3372],[10.17862,46.25626],[10.14439,46.22992],[10.07055,46.21668],[9.95249,46.38045],[9.73086,46.35071],[9.71273,46.29266],[9.57015,46.2958],[9.46117,46.37481],[9.45936,46.50873],[9.40487,46.46621],[9.36128,46.5081],[9.28136,46.49685],[9.25502,46.43743],[9.29226,46.32717],[9.24503,46.23616],[9.01618,46.04928],[8.99257,45.9698],[9.09065,45.89906],[9.06642,45.8761],[9.04546,45.84968],[9.04059,45.8464],[9.03505,45.83976],[9.03793,45.83548],[9.03279,45.82865],[9.0298,45.82127],[9.00324,45.82055],[8.99663,45.83466],[8.9621,45.83707],[8.94737,45.84285],[8.91129,45.8388],[8.93504,45.86245],[8.94372,45.86587],[8.93649,45.86775],[8.88904,45.95465],[8.86688,45.96135],[8.85121,45.97239],[8.8319,45.9879],[8.79362,45.99207],[8.78585,45.98973],[8.79414,46.00913],[8.85617,46.0748],[8.80778,46.10085],[8.75697,46.10395],[8.62242,46.12112],[8.45032,46.26869],[8.46317,46.43712],[8.42464,46.46367],[8.30648,46.41587],[8.31162,46.38044],[8.08814,46.26692],[8.16866,46.17817],[8.11383,46.11577],[8.02906,46.10331],[7.98881,45.99867],[7.9049,45.99945],[7.85949,45.91485],[7.56343,45.97421],[7.10685,45.85653],[7.04151,45.92435],[6.95315,45.85163],[6.80785,45.83265],[6.80785,45.71864],[6.98948,45.63869],[7.00037,45.509],[7.18019,45.40071],[7.10572,45.32924],[7.13115,45.25386],[7.07074,45.21228],[6.96706,45.20841],[6.85144,45.13226],[6.7697,45.16044],[6.62803,45.11175],[6.66981,45.02324],[6.74791,45.01939],[6.74519,44.93661],[6.75518,44.89915],[6.90774,44.84322],[6.93499,44.8664],[7.02217,44.82519],[7.00401,44.78782],[7.07484,44.68073],[7.00582,44.69364],[6.95133,44.66264],[6.96042,44.62129],[6.85507,44.53072],[6.86233,44.49834],[6.94504,44.43112],[6.88784,44.42043],[6.89171,44.36637],[6.98221,44.28289],[7.00764,44.23736],[7.16929,44.20352],[7.27827,44.1462],[7.34547,44.14359],[7.36364,44.11882],[7.62155,44.14881],[7.63245,44.17877],[7.68694,44.17487],[7.66878,44.12795],[7.72508,44.07578],[7.6597,44.03009],[7.66848,43.99943],[7.65266,43.9763],[7.60771,43.95772],[7.56858,43.94506],[7.56075,43.89932],[7.51162,43.88301],[7.49355,43.86551],[7.50423,43.84345],[7.53006,43.78405],[7.63035,43.57419]],[[12.45181,41.90056],[12.44834,41.90095],[12.44582,41.90194],[12.44815,41.90326],[12.44984,41.90545],[12.45091,41.90625],[12.45543,41.90738],[12.45561,41.90629],[12.45762,41.9058],[12.45755,41.9033],[12.45826,41.90281],[12.45834,41.90174],[12.4577,41.90115],[12.45691,41.90125],[12.45626,41.90172],[12.45435,41.90143],[12.45446,41.90028],[12.45181,41.90056]],[[12.45648,43.89369],[12.44184,43.90498],[12.41641,43.89991],[12.40935,43.9024],[12.41233,43.90956],[12.40733,43.92379],[12.41551,43.92984],[12.41165,43.93769],[12.40506,43.94325],[12.40415,43.95485],[12.41414,43.95273],[12.42005,43.9578],[12.43662,43.95698],[12.44684,43.96597],[12.46205,43.97463],[12.47853,43.98052],[12.49406,43.98492],[12.50678,43.99113],[12.51463,43.99122],[12.5154,43.98508],[12.51064,43.98165],[12.51109,43.97201],[12.50622,43.97131],[12.50875,43.96198],[12.50655,43.95796],[12.51427,43.94897],[12.51553,43.94096],[12.50496,43.93017],[12.50269,43.92363],[12.49724,43.92248],[12.49247,43.91774],[12.49429,43.90973],[12.48771,43.89706],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"JE",iso1A3:"JEY",iso1N3:"832",wikidata:"Q785",nameEn:"Jersey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01534"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.65349,49.15373],[-2.00491,48.86706]]]]}},{type:"Feature",properties:{iso1A2:"JM",iso1A3:"JAM",iso1N3:"388",wikidata:"Q766",nameEn:"Jamaica",aliases:["JA"],groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 876","1 658"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.50728,17.08879],[-76.34192,18.86145],[-78.75694,18.78765],[-78.34606,16.57862],[-75.50728,17.08879]]]]}},{type:"Feature",properties:{iso1A2:"JO",iso1A3:"JOR",iso1N3:"400",wikidata:"Q810",nameEn:"Jordan",groups:["145","142"],callingCodes:["962"]},geometry:{type:"MultiPolygon",coordinates:[[[[39.04251,32.30203],[38.98762,32.47694],[39.08202,32.50304],[38.79171,33.37328],[36.83946,32.31293],[36.40959,32.37908],[36.23948,32.50108],[36.20875,32.49529],[36.20379,32.52751],[36.08074,32.51463],[36.02239,32.65911],[35.96633,32.66237],[35.93307,32.71966],[35.88405,32.71321],[35.75983,32.74803],[35.68467,32.70715],[35.66527,32.681],[35.61669,32.67999],[35.59813,32.65159],[35.56614,32.64393],[35.57485,32.48669],[35.55494,32.42687],[35.55807,32.38674],[35.57111,32.21877],[35.52012,32.04076],[35.54375,31.96587],[35.52758,31.9131],[35.55941,31.76535],[35.47672,31.49578],[35.40316,31.25535],[35.43658,31.12444],[35.41371,30.95565],[35.33984,30.8802],[35.33456,30.81224],[35.29311,30.71365],[35.21379,30.60401],[35.19595,30.50297],[35.16218,30.43535],[35.19183,30.34636],[35.14108,30.07374],[35.02147,29.66343],[34.98207,29.58147],[34.97718,29.54294],[34.92298,29.45305],[34.88293,29.37455],[34.95987,29.35727],[36.07081,29.18469],[36.50005,29.49696],[36.75083,29.86903],[37.4971,29.99949],[37.66395,30.33245],[37.99354,30.49998],[36.99791,31.50081],[38.99233,31.99721],[39.29903,32.23259],[39.26157,32.35555],[39.04251,32.30203]]]]}},{type:"Feature",properties:{iso1A2:"JP",iso1A3:"JPN",iso1N3:"392",wikidata:"Q17",nameEn:"Japan",groups:["030","142"],driveSide:"left",callingCodes:["81"]},geometry:{type:"MultiPolygon",coordinates:[[[[145.82361,43.38904],[145.23667,43.76813],[145.82343,44.571],[140.9182,45.92937],[133.61399,37.41],[129.2669,34.87122],[122.26612,25.98197],[123.92912,17.8782],[155.16731,23.60141],[145.82361,43.38904]]]]}},{type:"Feature",properties:{iso1A2:"KE",iso1A3:"KEN",iso1N3:"404",wikidata:"Q114",nameEn:"Kenya",groups:["014","202","002"],driveSide:"left",callingCodes:["254"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.9419,4.61933],[35.51424,4.61643],[35.42366,4.76969],[35.47843,4.91872],[35.30992,4.90402],[35.34151,5.02364],[34.47601,4.72162],[33.9873,4.23316],[34.06046,4.15235],[34.15429,3.80464],[34.45815,3.67385],[34.44922,3.51627],[34.39112,3.48802],[34.41794,3.44342],[34.40006,3.37949],[34.45815,3.18319],[34.56242,3.11478],[34.60114,2.93034],[34.65774,2.8753],[34.73967,2.85447],[34.78137,2.76223],[34.77244,2.70272],[34.95267,2.47209],[34.90947,2.42447],[34.98692,1.97348],[34.9899,1.6668],[34.92734,1.56109],[34.87819,1.5596],[34.7918,1.36752],[34.82606,1.30944],[34.82606,1.26626],[34.80223,1.22754],[34.67562,1.21265],[34.58029,1.14712],[34.57427,1.09868],[34.52369,1.10692],[34.43349,0.85254],[34.40041,0.80266],[34.31516,0.75693],[34.27345,0.63182],[34.20196,0.62289],[34.13493,0.58118],[34.11408,0.48884],[34.08727,0.44713],[34.10067,0.36372],[33.90936,0.10581],[33.98449,-0.13079],[33.9264,-0.54188],[33.93107,-0.99298],[34.02286,-1.00779],[34.03084,-1.05101],[34.0824,-1.02264],[37.67199,-3.06222],[37.71745,-3.304],[37.5903,-3.42735],[37.63099,-3.50723],[37.75036,-3.54243],[37.81321,-3.69179],[39.21631,-4.67835],[39.44306,-4.93877],[39.62121,-4.68136],[41.75542,-1.85308],[41.56362,-1.66375],[41.56,-1.59812],[41.00099,-0.83068],[40.98767,2.82959],[41.31368,3.14314],[41.89488,3.97375],[41.1754,3.94079],[40.77498,4.27683],[39.86043,3.86974],[39.76808,3.67058],[39.58339,3.47434],[39.55132,3.39634],[39.51551,3.40895],[39.49444,3.45521],[39.19954,3.47834],[39.07736,3.5267],[38.91938,3.51198],[38.52336,3.62551],[38.45812,3.60445],[38.14168,3.62487],[37.07724,4.33503],[36.84474,4.44518],[36.03924,4.44406],[35.95449,4.53244],[35.9419,4.61933]]]]}},{type:"Feature",properties:{iso1A2:"KG",iso1A3:"KGZ",iso1N3:"417",wikidata:"Q813",nameEn:"Kyrgyzstan",groups:["143","142"],callingCodes:["996"]},geometry:{type:"MultiPolygon",coordinates:[[[[74.88756,42.98612],[74.75,42.99029],[74.70331,43.02519],[74.64615,43.05881],[74.57491,43.13702],[74.22489,43.24657],[73.55634,43.03071],[73.50992,42.82356],[73.44393,42.43098],[71.88792,42.83578],[71.62405,42.76613],[71.53272,42.8014],[71.2724,42.77853],[71.22785,42.69248],[71.17807,42.67381],[71.15232,42.60486],[70.97717,42.50147],[70.85973,42.30188],[70.94483,42.26238],[71.13263,42.28356],[71.28719,42.18033],[70.69777,41.92554],[70.17682,41.5455],[70.48909,41.40335],[70.67586,41.47953],[70.78572,41.36419],[70.77885,41.24813],[70.86263,41.23833],[70.9615,41.16393],[71.02193,41.19494],[71.11806,41.15359],[71.25813,41.18796],[71.27187,41.11015],[71.34877,41.16807],[71.40198,41.09436],[71.46148,41.13958],[71.43814,41.19644],[71.46688,41.31883],[71.57227,41.29175],[71.6787,41.42111],[71.65914,41.49599],[71.73054,41.54713],[71.71132,41.43012],[71.76625,41.4466],[71.83914,41.3546],[71.91457,41.2982],[71.85964,41.19081],[72.07249,41.11739],[72.10745,41.15483],[72.16433,41.16483],[72.17594,41.15522],[72.14864,41.13363],[72.1792,41.10621],[72.21061,41.05607],[72.17594,41.02377],[72.18339,40.99571],[72.324,41.03381],[72.34026,41.04539],[72.34757,41.06104],[72.36138,41.04384],[72.38511,41.02785],[72.45206,41.03018],[72.48742,40.97136],[72.55109,40.96046],[72.59136,40.86947],[72.68157,40.84942],[72.84291,40.85512],[72.94454,40.8094],[73.01869,40.84681],[73.13267,40.83512],[73.13412,40.79122],[73.0612,40.76678],[72.99133,40.76457],[72.93296,40.73089],[72.8722,40.71111],[72.85372,40.7116],[72.84754,40.67229],[72.80137,40.67856],[72.74866,40.60873],[72.74894,40.59592],[72.75982,40.57273],[72.74862,40.57131],[72.74768,40.58051],[72.73995,40.58409],[72.69579,40.59778],[72.66713,40.59076],[72.66713,40.5219],[72.47795,40.5532],[72.40517,40.61917],[72.34406,40.60144],[72.41714,40.55736],[72.38384,40.51535],[72.41513,40.50856],[72.44191,40.48222],[72.40346,40.4007],[72.24368,40.46091],[72.18648,40.49893],[71.96401,40.31907],[72.05464,40.27586],[71.85002,40.25647],[71.82646,40.21872],[71.73054,40.14818],[71.71719,40.17886],[71.69621,40.18492],[71.70569,40.20391],[71.68386,40.26984],[71.61931,40.26775],[71.61725,40.20615],[71.51549,40.22986],[71.51215,40.26943],[71.4246,40.28619],[71.36663,40.31593],[71.13042,40.34106],[71.05901,40.28765],[70.95789,40.28761],[70.9818,40.22392],[70.80495,40.16813],[70.7928,40.12797],[70.65827,40.0981],[70.65946,39.9878],[70.58912,39.95211],[70.55033,39.96619],[70.47557,39.93216],[70.57384,39.99394],[70.58297,40.00891],[70.01283,40.23288],[69.67001,40.10639],[69.64704,40.12165],[69.57615,40.10524],[69.55555,40.12296],[69.53794,40.11833],[69.53855,40.0887],[69.5057,40.03277],[69.53615,39.93991],[69.43557,39.92877],[69.43134,39.98431],[69.35649,40.01994],[69.26938,39.8127],[69.3594,39.52516],[69.68677,39.59281],[69.87491,39.53882],[70.11111,39.58223],[70.2869,39.53141],[70.44757,39.60128],[70.64087,39.58792],[70.7854,39.38933],[71.06418,39.41586],[71.08752,39.50704],[71.49814,39.61397],[71.55856,39.57588],[71.5517,39.45722],[71.62688,39.44056],[71.76816,39.45456],[71.80164,39.40631],[71.7522,39.32031],[71.79202,39.27355],[71.90601,39.27674],[72.04059,39.36704],[72.09689,39.26823],[72.17242,39.2661],[72.23834,39.17248],[72.33173,39.33093],[72.62027,39.39696],[72.85934,39.35116],[73.18454,39.35536],[73.31912,39.38615],[73.45096,39.46677],[73.59831,39.46425],[73.87018,39.47879],[73.94683,39.60733],[73.92354,39.69565],[73.9051,39.75073],[73.83006,39.76136],[73.97049,40.04378],[74.25533,40.13191],[74.35063,40.09742],[74.69875,40.34668],[74.85996,40.32857],[74.78168,40.44886],[74.82013,40.52197],[75.08243,40.43945],[75.22834,40.45382],[75.5854,40.66874],[75.69663,40.28642],[75.91361,40.2948],[75.96168,40.38064],[76.33659,40.3482],[76.5261,40.46114],[76.75681,40.95354],[76.99302,41.0696],[77.28004,41.0033],[77.3693,41.0375],[77.52723,41.00227],[77.76206,41.01574],[77.81287,41.14307],[78.12873,41.23091],[78.15757,41.38565],[78.3732,41.39603],[79.92977,42.04113],[80.17842,42.03211],[80.17807,42.21166],[79.97364,42.42816],[79.52921,42.44778],[79.19763,42.804],[78.91502,42.76839],[78.48469,42.89649],[75.82823,42.94848],[75.72174,42.79672],[75.29966,42.86183],[75.22619,42.85528],[74.88756,42.98612]],[[70.74189,39.86319],[70.63105,39.77923],[70.59667,39.83542],[70.54998,39.85137],[70.52631,39.86989],[70.53651,39.89155],[70.74189,39.86319]],[[71.86463,39.98598],[71.84316,39.95582],[71.7504,39.93701],[71.71511,39.96348],[71.78838,40.01404],[71.86463,39.98598]],[[71.21139,40.03369],[71.1427,39.95026],[71.23067,39.93581],[71.16101,39.88423],[71.10531,39.91354],[71.04979,39.89808],[71.10501,39.95568],[71.09063,39.99],[71.11668,39.99291],[71.11037,40.01984],[71.01035,40.05481],[71.00236,40.18154],[71.06305,40.1771],[71.12218,40.03052],[71.21139,40.03369]]]]}},{type:"Feature",properties:{iso1A2:"KH",iso1A3:"KHM",iso1N3:"116",wikidata:"Q424",nameEn:"Cambodia",groups:["035","142"],callingCodes:["855"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.87328,11.55953],[105.81645,11.56876],[105.80867,11.60536],[105.8507,11.66635],[105.88962,11.67854],[105.95188,11.63738],[106.00792,11.7197],[106.02038,11.77457],[106.06708,11.77761],[106.13158,11.73283],[106.18539,11.75171],[106.26478,11.72122],[106.30525,11.67549],[106.37219,11.69836],[106.44691,11.66787],[106.45158,11.68616],[106.41577,11.76999],[106.44535,11.8279],[106.44068,11.86294],[106.4687,11.86751],[106.4111,11.97413],[106.70687,11.96956],[106.79405,12.0807],[106.92325,12.06548],[106.99953,12.08983],[107.15831,12.27547],[107.34511,12.33327],[107.42917,12.24657],[107.4463,12.29373],[107.55059,12.36824],[107.5755,12.52177],[107.55993,12.7982],[107.49611,12.88926],[107.49144,13.01215],[107.62843,13.3668],[107.61909,13.52577],[107.53503,13.73908],[107.45252,13.78897],[107.46498,13.91593],[107.44318,13.99751],[107.38247,13.99147],[107.35757,14.02319],[107.37158,14.07906],[107.33577,14.11832],[107.40427,14.24509],[107.39493,14.32655],[107.44941,14.41552],[107.48521,14.40346],[107.52569,14.54665],[107.52102,14.59034],[107.55371,14.628],[107.54361,14.69092],[107.47238,14.61523],[107.44435,14.52785],[107.37897,14.54443],[107.3276,14.58812],[107.29803,14.58963],[107.26534,14.54292],[107.256,14.48716],[107.21241,14.48716],[107.17038,14.41782],[107.09722,14.3937],[107.03962,14.45099],[107.04585,14.41782],[106.98825,14.36806],[106.9649,14.3198],[106.90574,14.33639],[106.8497,14.29416],[106.80767,14.31226],[106.73762,14.42687],[106.63333,14.44194],[106.59908,14.50977],[106.57106,14.50525],[106.54148,14.59565],[106.50723,14.58963],[106.45898,14.55045],[106.47766,14.50977],[106.43874,14.52032],[106.40916,14.45249],[106.32355,14.44043],[106.25194,14.48415],[106.21302,14.36203],[106.00131,14.36957],[105.99509,14.32734],[106.02311,14.30623],[106.04801,14.20363],[106.10872,14.18401],[106.11962,14.11307],[106.18656,14.06324],[106.16632,14.01794],[106.10094,13.98471],[106.10405,13.9137],[105.90791,13.92881],[105.78182,14.02247],[105.78338,14.08438],[105.5561,14.15684],[105.44869,14.10703],[105.36775,14.09948],[105.2759,14.17496],[105.20894,14.34967],[105.17748,14.34432],[105.14012,14.23873],[105.08408,14.20402],[105.02804,14.23722],[104.97667,14.38806],[104.69335,14.42726],[104.55014,14.36091],[104.27616,14.39861],[103.93836,14.3398],[103.70175,14.38052],[103.71109,14.4348],[103.53518,14.42575],[103.39353,14.35639],[103.16469,14.33075],[102.93275,14.19044],[102.91251,14.01531],[102.77864,13.93374],[102.72727,13.77806],[102.56848,13.69366],[102.5481,13.6589],[102.58635,13.6286],[102.62483,13.60883],[102.57573,13.60461],[102.5358,13.56933],[102.44601,13.5637],[102.36859,13.57488],[102.33828,13.55613],[102.361,13.50551],[102.35563,13.47307],[102.35692,13.38274],[102.34611,13.35618],[102.36001,13.31142],[102.36146,13.26006],[102.43422,13.09061],[102.46011,13.08057],[102.52275,12.99813],[102.48694,12.97537],[102.49335,12.92711],[102.53053,12.77506],[102.4994,12.71736],[102.51963,12.66117],[102.57567,12.65358],[102.7796,12.43781],[102.78116,12.40284],[102.73134,12.37091],[102.70176,12.1686],[102.77026,12.06815],[102.78427,11.98746],[102.83957,11.8519],[102.90973,11.75613],[102.91449,11.65512],[102.52395,11.25257],[102.47649,9.66162],[103.99198,10.48391],[104.43778,10.42386],[104.47963,10.43046],[104.49869,10.4057],[104.59018,10.53073],[104.87933,10.52833],[104.95094,10.64003],[105.09571,10.72722],[105.02722,10.89236],[105.08326,10.95656],[105.11449,10.96332],[105.34011,10.86179],[105.42884,10.96878],[105.50045,10.94586],[105.77751,11.03671],[105.86376,10.89839],[105.84603,10.85873],[105.93403,10.83853],[105.94535,10.9168],[106.06708,10.8098],[106.18539,10.79451],[106.14301,10.98176],[106.20095,10.97795],[106.1757,11.07301],[106.1527,11.10476],[106.10444,11.07879],[105.86782,11.28343],[105.88962,11.43605],[105.87328,11.55953]]]]}},{type:"Feature",properties:{iso1A2:"KI",iso1A3:"KIR",iso1N3:"296",wikidata:"Q710",nameEn:"Kiribati",groups:["057","009"],driveSide:"left",callingCodes:["686"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[169,-3.5],[178,-3.5],[178,3.9],[169,3.9]]],[[[-158.62058,-1.35506],[-161.04969,-1.36251],[-175.33482,-1.40631],[-175.31804,-7.54825],[-174.18707,-7.54408],[-167.75329,-7.52784],[-156.50903,-7.4975],[-156.4957,-12.32002],[-149.61166,-12.30171],[-149.6249,-7.51261],[-149.65979,5.27712],[-161.06795,5.2462],[-161.05669,1.11722],[-158.62734,1.1296],[-158.62058,-1.35506]]]]}},{type:"Feature",properties:{iso1A2:"KM",iso1A3:"COM",iso1N3:"174",wikidata:"Q970",nameEn:"Comoros",groups:["014","202","002"],callingCodes:["269"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.93552,-11.11413],[42.99868,-12.65261],[44.75722,-12.58368],[44.69407,-11.04481],[42.93552,-11.11413]]]]}},{type:"Feature",properties:{iso1A2:"KN",iso1A3:"KNA",iso1N3:"659",wikidata:"Q763",nameEn:"St. Kitts and Nevis",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 869"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.27053,17.22145],[-62.76692,17.64353],[-63.11114,17.23125],[-62.62949,16.82364],[-62.27053,17.22145]]]]}},{type:"Feature",properties:{iso1A2:"KP",iso1A3:"PRK",iso1N3:"408",wikidata:"Q423",nameEn:"North Korea",groups:["030","142"],callingCodes:["850"]},geometry:{type:"MultiPolygon",coordinates:[[[[130.26095,42.9027],[130.09764,42.91425],[130.12957,42.98361],[129.96409,42.97306],[129.95082,43.01051],[129.8865,43.00395],[129.85261,42.96494],[129.83277,42.86746],[129.80719,42.79218],[129.7835,42.76521],[129.77183,42.69435],[129.75294,42.59409],[129.72541,42.43739],[129.60482,42.44461],[129.54701,42.37254],[129.42882,42.44702],[129.28541,42.41574],[129.22423,42.3553],[129.22285,42.26491],[129.15178,42.17224],[128.96068,42.06657],[128.94007,42.03537],[128.04487,42.01769],[128.15119,41.74568],[128.30716,41.60322],[128.20061,41.40895],[128.18546,41.41279],[128.12967,41.37931],[128.03311,41.39232],[128.02633,41.42103],[127.92943,41.44291],[127.29712,41.49473],[127.17841,41.59714],[126.90729,41.79955],[126.60631,41.65565],[126.53189,41.35206],[126.242,41.15454],[126.00335,40.92835],[125.76869,40.87908],[125.71172,40.85223],[124.86913,40.45387],[124.40719,40.13655],[124.38556,40.11047],[124.3322,40.05573],[124.37089,40.03004],[124.35029,39.95639],[124.23201,39.9248],[124.17532,39.8232],[123.90497,38.79949],[123.85601,37.49093],[124.67666,38.05679],[124.84224,37.977],[124.87921,37.80827],[125.06408,37.66334],[125.37112,37.62643],[125.81159,37.72949],[126.13074,37.70512],[126.18776,37.74728],[126.19097,37.81462],[126.24402,37.83113],[126.43239,37.84095],[126.46818,37.80873],[126.56709,37.76857],[126.59918,37.76364],[126.66067,37.7897],[126.68793,37.83728],[126.68793,37.9175],[126.67023,37.95852],[126.84961,38.0344],[126.88106,38.10246],[126.95887,38.1347],[126.95338,38.17735],[127.04479,38.25518],[127.15749,38.30722],[127.38727,38.33227],[127.49672,38.30647],[127.55013,38.32257],[128.02917,38.31861],[128.27652,38.41657],[128.31105,38.58462],[128.37487,38.62345],[128.65655,38.61914],[131.95041,41.5445],[130.65022,42.32281],[130.66367,42.38024],[130.64181,42.41422],[130.60805,42.4317],[130.56835,42.43281],[130.55143,42.52158],[130.50123,42.61636],[130.44361,42.54849],[130.41826,42.6011],[130.2385,42.71127],[130.23068,42.80125],[130.26095,42.9027]]]]}},{type:"Feature",properties:{iso1A2:"KR",iso1A3:"KOR",iso1N3:"410",wikidata:"Q884",nameEn:"South Korea",groups:["030","142"],callingCodes:["82"]},geometry:{type:"MultiPolygon",coordinates:[[[[133.61399,37.41],[128.65655,38.61914],[128.37487,38.62345],[128.31105,38.58462],[128.27652,38.41657],[128.02917,38.31861],[127.55013,38.32257],[127.49672,38.30647],[127.38727,38.33227],[127.15749,38.30722],[127.04479,38.25518],[126.95338,38.17735],[126.95887,38.1347],[126.88106,38.10246],[126.84961,38.0344],[126.67023,37.95852],[126.68793,37.9175],[126.68793,37.83728],[126.66067,37.7897],[126.59918,37.76364],[126.56709,37.76857],[126.46818,37.80873],[126.43239,37.84095],[126.24402,37.83113],[126.19097,37.81462],[126.18776,37.74728],[126.13074,37.70512],[125.81159,37.72949],[125.37112,37.62643],[125.06408,37.66334],[124.87921,37.80827],[124.84224,37.977],[124.67666,38.05679],[123.85601,37.49093],[122.80525,33.30571],[125.99728,32.63328],[129.2669,34.87122],[133.61399,37.41]]]]}},{type:"Feature",properties:{iso1A2:"KW",iso1A3:"KWT",iso1N3:"414",wikidata:"Q817",nameEn:"Kuwait",groups:["145","142"],callingCodes:["965"]},geometry:{type:"MultiPolygon",coordinates:[[[[49.00421,28.81495],[48.59531,29.66815],[48.40479,29.85763],[48.17332,30.02448],[48.06782,30.02906],[48.01114,29.98906],[47.7095,30.10453],[47.37192,30.10421],[47.15166,30.01044],[46.89695,29.50584],[46.5527,29.10283],[47.46202,29.0014],[47.58376,28.83382],[47.59863,28.66798],[47.70561,28.5221],[48.42991,28.53628],[49.00421,28.81495]]]]}},{type:"Feature",properties:{iso1A2:"KY",iso1A3:"CYM",iso1N3:"136",wikidata:"Q5785",nameEn:"Cayman Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 345"]},geometry:{type:"MultiPolygon",coordinates:[[[[-82.11509,19.60401],[-80.36068,18.11751],[-79.32727,20.06742],[-82.11509,19.60401]]]]}},{type:"Feature",properties:{iso1A2:"KZ",iso1A3:"KAZ",iso1N3:"398",wikidata:"Q232",nameEn:"Kazakhstan",groups:["143","142"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[68.90865,55.38148],[68.19206,55.18823],[68.26661,55.09226],[68.21308,54.98645],[65.20174,54.55216],[65.24663,54.35721],[65.11033,54.33028],[64.97216,54.4212],[63.97686,54.29763],[64.02715,54.22679],[63.91224,54.20013],[63.80604,54.27079],[62.58651,54.05871],[62.56876,53.94047],[62.45931,53.90737],[62.38535,54.03961],[62.00966,54.04134],[62.03913,53.94768],[61.65318,54.02445],[61.56941,53.95703],[61.47603,54.08048],[61.3706,54.08464],[61.26863,53.92797],[60.99796,53.93699],[61.14283,53.90063],[61.22574,53.80268],[60.90626,53.62937],[61.55706,53.57144],[61.57185,53.50112],[61.37957,53.45887],[61.29082,53.50992],[61.14291,53.41481],[61.19024,53.30536],[62.14574,53.09626],[62.12799,52.99133],[62.0422,52.96105],[61.23462,53.03227],[61.05842,52.92217],[60.71989,52.75923],[60.71693,52.66245],[60.84118,52.63912],[60.84709,52.52228],[60.98021,52.50068],[61.05417,52.35096],[60.78201,52.22067],[60.72581,52.15538],[60.48915,52.15175],[60.19925,51.99173],[59.99809,51.98263],[60.09867,51.87135],[60.50986,51.7964],[60.36787,51.66815],[60.5424,51.61675],[60.92401,51.61124],[60.95655,51.48615],[61.50677,51.40687],[61.55114,51.32746],[61.6813,51.25716],[61.56889,51.23679],[61.4431,50.80679],[60.81833,50.6629],[60.31914,50.67705],[60.17262,50.83312],[60.01288,50.8163],[59.81172,50.54451],[59.51886,50.49937],[59.48928,50.64216],[58.87974,50.70852],[58.3208,51.15151],[57.75578,51.13852],[57.74986,50.93017],[57.44221,50.88354],[57.17302,51.11253],[56.17906,50.93204],[56.11398,50.7471],[55.67774,50.54508],[54.72067,51.03261],[54.56685,51.01958],[54.71476,50.61214],[54.55797,50.52006],[54.41894,50.61214],[54.46331,50.85554],[54.12248,51.11542],[53.69299,51.23466],[53.46165,51.49445],[52.54329,51.48444],[52.36119,51.74161],[51.8246,51.67916],[51.77431,51.49536],[51.301,51.48799],[51.26254,51.68466],[50.59695,51.61859],[50.26859,51.28677],[49.97277,51.2405],[49.76866,51.11067],[49.39001,51.09396],[49.41959,50.85927],[49.12673,50.78639],[48.86936,50.61589],[48.57946,50.63278],[48.90782,50.02281],[48.68352,49.89546],[48.42564,49.82283],[48.24519,49.86099],[48.10044,50.09242],[47.58551,50.47867],[47.30448,50.30894],[47.34589,50.09308],[47.18319,49.93721],[46.9078,49.86707],[46.78398,49.34026],[46.98795,49.23531],[47.04416,49.17152],[47.01458,49.07085],[46.91104,48.99715],[46.78392,48.95352],[46.49011,48.43019],[47.11516,48.27188],[47.12107,47.83687],[47.38731,47.68176],[47.41689,47.83687],[47.64973,47.76559],[48.15348,47.74545],[48.45173,47.40818],[48.52326,47.4102],[49.01136,46.72716],[48.51142,46.69268],[48.54988,46.56267],[49.16518,46.38542],[49.32259,46.26944],[49.88945,46.04554],[49.2134,44.84989],[52.26048,41.69249],[52.47884,41.78034],[52.97575,42.1308],[54.20635,42.38477],[54.95182,41.92424],[55.45471,41.25609],[56.00314,41.32584],[55.97584,44.99322],[55.97584,44.99328],[55.97584,44.99338],[55.97584,44.99343],[55.97584,44.99348],[55.97584,44.99353],[55.97584,44.99359],[55.97584,44.99369],[55.97584,44.99374],[55.97584,44.99384],[55.97584,44.9939],[55.97584,44.994],[55.97584,44.99405],[55.97584,44.99415],[55.97584,44.99421],[55.97584,44.99426],[55.97584,44.99431],[55.97584,44.99436],[55.97584,44.99441],[55.97594,44.99446],[55.97605,44.99452],[55.97605,44.99457],[55.97605,44.99462],[55.97605,44.99467],[55.97605,44.99477],[55.97615,44.99477],[55.97615,44.99483],[55.97615,44.99493],[55.97615,44.99498],[55.97615,44.99503],[55.97615,44.99508],[55.97625,44.99514],[55.97636,44.99519],[55.97636,44.99524],[55.97646,44.99529],[55.97646,44.99534],[55.97656,44.99539],[55.97667,44.99545],[55.97677,44.9955],[55.97677,44.99555],[55.97677,44.9956],[55.97687,44.9956],[55.97698,44.99565],[55.97698,44.9957],[55.97708,44.99576],[55.97718,44.99581],[55.97729,44.99586],[55.97739,44.99586],[55.97739,44.99591],[55.97749,44.99591],[55.9776,44.99591],[55.9777,44.99596],[55.9777,44.99601],[55.9778,44.99607],[55.97791,44.99607],[55.97801,44.99607],[55.97801,44.99612],[55.97811,44.99617],[55.97822,44.99617],[55.97832,44.99622],[55.97842,44.99622],[58.59711,45.58671],[61.01475,44.41383],[62.01711,43.51008],[63.34656,43.64003],[64.53885,43.56941],[64.96464,43.74748],[65.18666,43.48835],[65.53277,43.31856],[65.85194,42.85481],[66.09482,42.93426],[66.00546,41.94455],[66.53302,41.87388],[66.69129,41.1311],[67.9644,41.14611],[67.98511,41.02794],[68.08273,41.08148],[68.1271,41.0324],[67.96736,40.83798],[68.49983,40.56437],[68.63,40.59358],[68.58444,40.91447],[68.49983,40.99669],[68.62221,41.03019],[68.65662,40.93861],[68.73945,40.96989],[68.7217,41.05025],[69.01308,41.22804],[69.05006,41.36183],[69.15137,41.43078],[69.17701,41.43769],[69.18528,41.45175],[69.20439,41.45391],[69.22671,41.46298],[69.23332,41.45847],[69.25059,41.46693],[69.29778,41.43673],[69.35554,41.47211],[69.37468,41.46555],[69.45081,41.46246],[69.39485,41.51518],[69.45751,41.56863],[69.49545,41.545],[70.94483,42.26238],[70.85973,42.30188],[70.97717,42.50147],[71.15232,42.60486],[71.17807,42.67381],[71.22785,42.69248],[71.2724,42.77853],[71.53272,42.8014],[71.62405,42.76613],[71.88792,42.83578],[73.44393,42.43098],[73.50992,42.82356],[73.55634,43.03071],[74.22489,43.24657],[74.57491,43.13702],[74.64615,43.05881],[74.70331,43.02519],[74.75,42.99029],[74.88756,42.98612],[75.22619,42.85528],[75.29966,42.86183],[75.72174,42.79672],[75.82823,42.94848],[78.48469,42.89649],[78.91502,42.76839],[79.19763,42.804],[79.52921,42.44778],[79.97364,42.42816],[80.17807,42.21166],[80.26841,42.23797],[80.16892,42.61137],[80.26886,42.8366],[80.38169,42.83142],[80.58999,42.9011],[80.3735,43.01557],[80.62913,43.141],[80.78817,43.14235],[80.77771,43.30065],[80.69718,43.32589],[80.75156,43.44948],[80.40031,44.10986],[80.40229,44.23319],[80.38384,44.63073],[79.8987,44.89957],[80.11169,45.03352],[81.73278,45.3504],[82.51374,45.1755],[82.58474,45.40027],[82.21792,45.56619],[83.04622,47.19053],[83.92184,46.98912],[84.73077,47.01394],[84.93995,46.87399],[85.22443,47.04816],[85.54294,47.06171],[85.69696,47.2898],[85.61067,47.49753],[85.5169,48.05493],[85.73581,48.3939],[86.38069,48.46064],[86.75343,48.70331],[86.73568,48.99918],[86.87238,49.12432],[87.28386,49.11626],[87.31465,49.23603],[87.03071,49.25142],[86.82606,49.51796],[86.61307,49.60239],[86.79056,49.74787],[86.63674,49.80136],[86.18709,49.50259],[85.24047,49.60239],[84.99198,50.06793],[84.29385,50.27257],[83.8442,50.87375],[83.14607,51.00796],[82.55443,50.75412],[81.94999,50.79307],[81.46581,50.77658],[81.41248,50.97524],[81.06091,50.94833],[81.16999,51.15662],[80.80318,51.28262],[80.44819,51.20855],[80.4127,50.95581],[80.08138,50.77658],[79.11255,52.01171],[77.90383,53.29807],[76.54243,53.99329],[76.44076,54.16017],[76.82266,54.1798],[76.91052,54.4677],[75.3668,54.07439],[75.43398,53.98652],[75.07405,53.80831],[73.39218,53.44623],[73.25412,53.61532],[73.68921,53.86522],[73.74778,54.07194],[73.37963,53.96132],[72.71026,54.1161],[72.43415,53.92685],[72.17477,54.36303],[71.96141,54.17736],[71.10379,54.13326],[71.08706,54.33376],[71.24185,54.64965],[71.08288,54.71253],[70.96009,55.10558],[70.76493,55.3027],[70.19179,55.1476],[69.74917,55.35545],[69.34224,55.36344],[68.90865,55.38148]]]]}},{type:"Feature",properties:{iso1A2:"LA",iso1A3:"LAO",iso1N3:"418",wikidata:"Q819",nameEn:"Laos",groups:["035","142"],callingCodes:["856"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.1245,22.43372],[102.03633,22.46164],[101.98487,22.42766],[101.91344,22.44417],[101.90714,22.38688],[101.86828,22.38397],[101.7685,22.50337],[101.68973,22.46843],[101.61306,22.27515],[101.56789,22.28876],[101.53638,22.24794],[101.60675,22.13513],[101.57525,22.13026],[101.62566,21.96574],[101.7791,21.83019],[101.74555,21.72852],[101.83257,21.61562],[101.80001,21.57461],[101.7475,21.5873],[101.7727,21.51794],[101.74224,21.48276],[101.74014,21.30967],[101.84412,21.25291],[101.83887,21.20983],[101.76745,21.21571],[101.79266,21.19025],[101.7622,21.14813],[101.70548,21.14911],[101.66977,21.20004],[101.60886,21.17947],[101.59491,21.18621],[101.6068,21.23329],[101.54563,21.25668],[101.29326,21.17254],[101.2229,21.23271],[101.26912,21.36482],[101.19349,21.41959],[101.2124,21.56422],[101.15156,21.56129],[101.16198,21.52808],[101.00234,21.39612],[100.80173,21.2934],[100.72716,21.31786],[100.63578,21.05639],[100.55281,21.02796],[100.50974,20.88574],[100.64628,20.88279],[100.60112,20.8347],[100.51079,20.82194],[100.36375,20.82783],[100.1957,20.68247],[100.08404,20.36626],[100.09999,20.31614],[100.09337,20.26293],[100.11785,20.24787],[100.1712,20.24324],[100.16668,20.2986],[100.22076,20.31598],[100.25769,20.3992],[100.33383,20.4028],[100.37439,20.35156],[100.41473,20.25625],[100.44992,20.23644],[100.4537,20.19971],[100.47567,20.19133],[100.51052,20.14928],[100.55218,20.17741],[100.58808,20.15791],[100.5094,19.87904],[100.398,19.75047],[100.49604,19.53504],[100.58219,19.49164],[100.64606,19.55884],[100.77231,19.48324],[100.90302,19.61901],[101.08928,19.59748],[101.26545,19.59242],[101.26991,19.48324],[101.21347,19.46223],[101.20604,19.35296],[101.24911,19.33334],[101.261,19.12717],[101.35606,19.04716],[101.25803,18.89545],[101.22832,18.73377],[101.27585,18.68875],[101.06047,18.43247],[101.18227,18.34367],[101.15108,18.25624],[101.19118,18.2125],[101.1793,18.0544],[101.02185,17.87637],[100.96541,17.57926],[101.15108,17.47586],[101.44667,17.7392],[101.72294,17.92867],[101.78087,18.07559],[101.88485,18.02474],[102.11359,18.21532],[102.45523,17.97106],[102.59234,17.96127],[102.60971,17.95411],[102.61432,17.92273],[102.5896,17.84889],[102.59485,17.83537],[102.68194,17.80151],[102.69946,17.81686],[102.67543,17.84529],[102.68538,17.86653],[102.75954,17.89561],[102.79044,17.93612],[102.81988,17.94233],[102.86323,17.97531],[102.95812,18.0054],[102.9912,17.9949],[103.01998,17.97095],[103.0566,18.00144],[103.07823,18.03833],[103.07343,18.12351],[103.1493,18.17799],[103.14994,18.23172],[103.17093,18.2618],[103.29757,18.30475],[103.23818,18.34875],[103.24779,18.37807],[103.30977,18.4341],[103.41044,18.4486],[103.47773,18.42841],[103.60957,18.40528],[103.699,18.34125],[103.82449,18.33979],[103.85642,18.28666],[103.93916,18.33914],[103.97725,18.33631],[104.06533,18.21656],[104.10927,18.10826],[104.21776,17.99335],[104.2757,17.86139],[104.35432,17.82871],[104.45404,17.66788],[104.69867,17.53038],[104.80061,17.39367],[104.80716,17.19025],[104.73712,17.01404],[104.7373,16.91125],[104.76442,16.84752],[104.7397,16.81005],[104.76099,16.69302],[104.73349,16.565],[104.88057,16.37311],[105.00262,16.25627],[105.06204,16.09792],[105.42001,16.00657],[105.38508,15.987],[105.34115,15.92737],[105.37959,15.84074],[105.42285,15.76971],[105.46573,15.74742],[105.61756,15.68792],[105.60446,15.53301],[105.58191,15.41031],[105.47635,15.3796],[105.4692,15.33709],[105.50662,15.32054],[105.58043,15.32724],[105.46661,15.13132],[105.61162,15.00037],[105.5121,14.80802],[105.53864,14.55731],[105.43783,14.43865],[105.20894,14.34967],[105.2759,14.17496],[105.36775,14.09948],[105.44869,14.10703],[105.5561,14.15684],[105.78338,14.08438],[105.78182,14.02247],[105.90791,13.92881],[106.10405,13.9137],[106.10094,13.98471],[106.16632,14.01794],[106.18656,14.06324],[106.11962,14.11307],[106.10872,14.18401],[106.04801,14.20363],[106.02311,14.30623],[105.99509,14.32734],[106.00131,14.36957],[106.21302,14.36203],[106.25194,14.48415],[106.32355,14.44043],[106.40916,14.45249],[106.43874,14.52032],[106.47766,14.50977],[106.45898,14.55045],[106.50723,14.58963],[106.54148,14.59565],[106.57106,14.50525],[106.59908,14.50977],[106.63333,14.44194],[106.73762,14.42687],[106.80767,14.31226],[106.8497,14.29416],[106.90574,14.33639],[106.9649,14.3198],[106.98825,14.36806],[107.04585,14.41782],[107.03962,14.45099],[107.09722,14.3937],[107.17038,14.41782],[107.21241,14.48716],[107.256,14.48716],[107.26534,14.54292],[107.29803,14.58963],[107.3276,14.58812],[107.37897,14.54443],[107.44435,14.52785],[107.47238,14.61523],[107.54361,14.69092],[107.51579,14.79282],[107.59285,14.87795],[107.48277,14.93751],[107.46516,15.00982],[107.61486,15.0566],[107.61926,15.13949],[107.58844,15.20111],[107.62587,15.2266],[107.60605,15.37524],[107.62367,15.42193],[107.53341,15.40496],[107.50699,15.48771],[107.3815,15.49832],[107.34408,15.62345],[107.27583,15.62769],[107.27143,15.71459],[107.21859,15.74638],[107.21419,15.83747],[107.34188,15.89464],[107.39471,15.88829],[107.46296,16.01106],[107.44975,16.08511],[107.33968,16.05549],[107.25822,16.13587],[107.14595,16.17816],[107.15035,16.26271],[107.09091,16.3092],[107.02597,16.31132],[106.97385,16.30204],[106.96638,16.34938],[106.88067,16.43594],[106.88727,16.52671],[106.84104,16.55415],[106.74418,16.41904],[106.65832,16.47816],[106.66052,16.56892],[106.61477,16.60713],[106.58267,16.6012],[106.59013,16.62259],[106.55485,16.68704],[106.55265,16.86831],[106.52183,16.87884],[106.51963,16.92097],[106.54824,16.92729],[106.55045,17.0031],[106.50862,16.9673],[106.43597,17.01362],[106.31929,17.20509],[106.29287,17.3018],[106.24444,17.24714],[106.18991,17.28227],[106.09019,17.36399],[105.85744,17.63221],[105.76612,17.67147],[105.60381,17.89356],[105.64784,17.96687],[105.46292,18.22008],[105.38366,18.15315],[105.15942,18.38691],[105.10408,18.43533],[105.1327,18.58355],[105.19654,18.64196],[105.12829,18.70453],[104.64617,18.85668],[104.5361,18.97747],[103.87125,19.31854],[104.06058,19.43484],[104.10832,19.51575],[104.05617,19.61743],[104.06498,19.66926],[104.23229,19.70242],[104.41281,19.70035],[104.53169,19.61743],[104.64837,19.62365],[104.68359,19.72729],[104.8355,19.80395],[104.8465,19.91783],[104.9874,20.09573],[104.91695,20.15567],[104.86852,20.14121],[104.61315,20.24452],[104.62195,20.36633],[104.72102,20.40554],[104.66158,20.47774],[104.47886,20.37459],[104.40621,20.3849],[104.38199,20.47155],[104.63957,20.6653],[104.27412,20.91433],[104.11121,20.96779],[103.98024,20.91531],[103.82282,20.8732],[103.73478,20.6669],[103.68633,20.66324],[103.45737,20.82382],[103.38032,20.79501],[103.21497,20.89832],[103.12055,20.89994],[103.03469,21.05821],[102.97745,21.05821],[102.89825,21.24707],[102.80794,21.25736],[102.88939,21.3107],[102.94223,21.46034],[102.86297,21.4255],[102.98846,21.58936],[102.97965,21.74076],[102.86077,21.71213],[102.85637,21.84501],[102.81894,21.83888],[102.82115,21.73667],[102.74189,21.66713],[102.67145,21.65894],[102.62301,21.91447],[102.49092,21.99002],[102.51734,22.02676],[102.18712,22.30403],[102.14099,22.40092],[102.1245,22.43372]]]]}},{type:"Feature",properties:{iso1A2:"LB",iso1A3:"LBN",iso1N3:"422",wikidata:"Q822",nameEn:"Lebanon",aliases:["RL"],groups:["145","142"],callingCodes:["961"]},geometry:{type:"MultiPolygon",coordinates:[[[[35.94816,33.47886],[35.94465,33.52774],[36.05723,33.57904],[35.9341,33.6596],[36.06778,33.82927],[36.14517,33.85118],[36.3967,33.83365],[36.38263,33.86579],[36.28589,33.91981],[36.41078,34.05253],[36.50576,34.05982],[36.5128,34.09916],[36.62537,34.20251],[36.59195,34.2316],[36.58667,34.27667],[36.60778,34.31009],[36.56556,34.31881],[36.53039,34.3798],[36.55853,34.41609],[36.46179,34.46541],[36.4442,34.50165],[36.34745,34.5002],[36.3369,34.52629],[36.39846,34.55672],[36.41429,34.61175],[36.45299,34.59438],[36.46003,34.6378],[36.42941,34.62505],[36.35384,34.65447],[36.35135,34.68516],[36.32399,34.69334],[36.29165,34.62991],[35.98718,34.64977],[35.97386,34.63322],[35.48515,34.70851],[34.78515,33.20368],[35.10645,33.09318],[35.1924,33.08743],[35.31429,33.10515],[35.35223,33.05617],[35.43059,33.06659],[35.448,33.09264],[35.50272,33.09056],[35.50335,33.114],[35.52573,33.11921],[35.54228,33.19865],[35.5362,33.23196],[35.54808,33.236],[35.54544,33.25513],[35.55555,33.25844],[35.56523,33.28969],[35.58326,33.28381],[35.58502,33.26653],[35.62283,33.24226],[35.62019,33.27278],[35.77477,33.33609],[35.81324,33.36354],[35.82577,33.40479],[35.88668,33.43183],[35.94816,33.47886]]]]}},{type:"Feature",properties:{iso1A2:"LC",iso1A3:"LCA",iso1N3:"662",wikidata:"Q760",nameEn:"St. Lucia",aliases:["WL"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 758"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-61.26561,14.25664],[-61.43129,13.68336],[-60.70539,13.41452],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"LI",iso1A3:"LIE",iso1N3:"438",wikidata:"Q347",nameEn:"Liechtenstein",aliases:["FL"],groups:["155","150"],callingCodes:["423"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.60717,47.06091],[9.61216,47.07732],[9.63395,47.08443],[9.62623,47.14685],[9.56539,47.17124],[9.58264,47.20673],[9.56981,47.21926],[9.55176,47.22585],[9.56766,47.24281],[9.53116,47.27029],[9.52406,47.24959],[9.50318,47.22153],[9.4891,47.19346],[9.48774,47.17402],[9.51044,47.13727],[9.52089,47.10019],[9.51362,47.08505],[9.47139,47.06402],[9.47548,47.05257],[9.54041,47.06495],[9.55721,47.04762],[9.60717,47.06091]]]]}},{type:"Feature",properties:{iso1A2:"LK",iso1A3:"LKA",iso1N3:"144",wikidata:"Q854",nameEn:"Sri Lanka",groups:["034","142"],driveSide:"left",callingCodes:["94"]},geometry:{type:"MultiPolygon",coordinates:[[[[76.25812,4.62435],[85.15017,5.21497],[80.48418,10.20786],[79.42124,9.80115],[79.50447,8.91876],[76.25812,4.62435]]]]}},{type:"Feature",properties:{iso1A2:"LR",iso1A3:"LBR",iso1N3:"430",wikidata:"Q1014",nameEn:"Liberia",groups:["011","202","002"],callingCodes:["231"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.47114,7.55676],[-8.55874,7.62525],[-8.55874,7.70167],[-8.67814,7.69428],[-8.72789,7.51429],[-8.8448,7.35149],[-8.85724,7.26019],[-8.93435,7.2824],[-9.09107,7.1985],[-9.18311,7.30461],[-9.20798,7.38109],[-9.305,7.42056],[-9.41943,7.41809],[-9.48161,7.37122],[-9.37465,7.62032],[-9.35724,7.74111],[-9.44928,7.9284],[-9.41445,8.02448],[-9.50898,8.18455],[-9.47415,8.35195],[-9.77763,8.54633],[-10.05873,8.42578],[-10.05375,8.50697],[-10.14579,8.52665],[-10.203,8.47991],[-10.27575,8.48711],[-10.30084,8.30008],[-10.31635,8.28554],[-10.29839,8.21283],[-10.35227,8.15223],[-10.45023,8.15627],[-10.51554,8.1393],[-10.57523,8.04829],[-10.60492,8.04072],[-10.60422,7.7739],[-11.29417,7.21576],[-11.4027,6.97746],[-11.50429,6.92704],[-12.15048,6.15992],[-7.52774,3.7105],[-7.53259,4.35145],[-7.59349,4.8909],[-7.53876,4.94294],[-7.55369,5.08667],[-7.48901,5.14118],[-7.46165,5.26256],[-7.36463,5.32944],[-7.43428,5.42355],[-7.37209,5.61173],[-7.43926,5.74787],[-7.43677,5.84687],[-7.46165,5.84934],[-7.48155,5.80974],[-7.67309,5.94337],[-7.70294,5.90625],[-7.78254,5.99037],[-7.79747,6.07696],[-7.8497,6.08932],[-7.83478,6.20309],[-7.90692,6.27728],[-8.00642,6.31684],[-8.17557,6.28222],[-8.3298,6.36381],[-8.38453,6.35887],[-8.45666,6.49977],[-8.48652,6.43797],[-8.59456,6.50612],[-8.31736,6.82837],[-8.29249,7.1691],[-8.37458,7.25794],[-8.41935,7.51203],[-8.47114,7.55676]]]]}},{type:"Feature",properties:{iso1A2:"LS",iso1A3:"LSO",iso1N3:"426",wikidata:"Q1013",nameEn:"Lesotho",groups:["018","202","002"],driveSide:"left",callingCodes:["266"]},geometry:{type:"MultiPolygon",coordinates:[[[[29.33204,-29.45598],[29.44883,-29.3772],[29.40524,-29.21246],[28.68043,-28.58744],[28.65091,-28.57025],[28.40612,-28.6215],[28.30518,-28.69531],[28.2348,-28.69471],[28.1317,-28.7293],[28.02503,-28.85991],[27.98675,-28.8787],[27.9392,-28.84864],[27.88933,-28.88156],[27.8907,-28.91612],[27.75458,-28.89839],[27.55974,-29.18954],[27.5158,-29.2261],[27.54258,-29.25575],[27.48679,-29.29349],[27.45125,-29.29708],[27.47254,-29.31968],[27.4358,-29.33465],[27.33464,-29.48161],[27.01016,-29.65439],[27.09489,-29.72796],[27.22719,-30.00718],[27.29603,-30.05473],[27.32555,-30.14785],[27.40778,-30.14577],[27.37293,-30.19401],[27.36649,-30.27246],[27.38108,-30.33456],[27.45452,-30.32239],[27.56901,-30.42504],[27.56781,-30.44562],[27.62137,-30.50509],[27.6521,-30.51707],[27.67819,-30.53437],[27.69467,-30.55862],[27.74814,-30.60635],[28.12073,-30.68072],[28.2319,-30.28476],[28.399,-30.1592],[28.68627,-30.12885],[28.80222,-30.10579],[28.9338,-30.05072],[29.16548,-29.91706],[29.12553,-29.76266],[29.28545,-29.58456],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"LT",iso1A3:"LTU",iso1N3:"440",wikidata:"Q37",nameEn:"Lithuania",groups:["EU","154","150"],callingCodes:["370"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.89005,56.46666],[24.83686,56.41565],[24.70022,56.40483],[24.57353,56.31525],[24.58143,56.29125],[24.42746,56.26522],[24.32334,56.30226],[24.13139,56.24881],[24.02657,56.3231],[23.75726,56.37282],[23.49803,56.34307],[23.40486,56.37689],[23.31606,56.3827],[23.17312,56.36795],[23.09531,56.30511],[22.96988,56.41213],[22.83048,56.367],[22.69354,56.36284],[22.56441,56.39305],[22.3361,56.4016],[22.09728,56.42851],[22.00548,56.41508],[21.74558,56.33181],[21.57888,56.31406],[21.49736,56.29106],[21.24644,56.16917],[21.15016,56.07818],[20.68447,56.04073],[20.60454,55.40986],[20.95181,55.27994],[21.26425,55.24456],[21.35465,55.28427],[21.38446,55.29348],[21.46766,55.21115],[21.51095,55.18507],[21.55605,55.20311],[21.64954,55.1791],[21.85521,55.09493],[21.96505,55.07353],[21.99543,55.08691],[22.03984,55.07888],[22.02582,55.05078],[22.06087,55.02935],[22.11697,55.02131],[22.14267,55.05345],[22.31562,55.0655],[22.47688,55.04408],[22.58907,55.07085],[22.60075,55.01863],[22.65451,54.97037],[22.68723,54.9811],[22.76422,54.92521],[22.85083,54.88711],[22.87317,54.79492],[22.73631,54.72952],[22.73397,54.66604],[22.75467,54.6483],[22.74225,54.64339],[22.7522,54.63525],[22.68021,54.58486],[22.71293,54.56454],[22.67788,54.532],[22.70208,54.45312],[22.7253,54.41732],[22.79705,54.36264],[22.83756,54.40827],[23.00584,54.38514],[22.99649,54.35927],[23.05726,54.34565],[23.04323,54.31567],[23.104,54.29794],[23.13905,54.31567],[23.15526,54.31076],[23.15938,54.29894],[23.24656,54.25701],[23.3494,54.25155],[23.39525,54.21672],[23.42418,54.17911],[23.45223,54.17775],[23.49196,54.14764],[23.52702,54.04622],[23.48261,53.98855],[23.51284,53.95052],[23.61677,53.92691],[23.71726,53.93379],[23.80543,53.89558],[23.81309,53.94205],[23.95098,53.9613],[23.98837,53.92554],[24.19638,53.96405],[24.34128,53.90076],[24.44411,53.90076],[24.62275,54.00217],[24.69652,54.01901],[24.69185,53.96543],[24.74279,53.96663],[24.85311,54.02862],[24.77131,54.11091],[24.96894,54.17589],[24.991,54.14241],[25.0728,54.13419],[25.19199,54.219],[25.22705,54.26271],[25.35559,54.26544],[25.509,54.30267],[25.56823,54.25212],[25.51452,54.17799],[25.54724,54.14925],[25.64875,54.1259],[25.71084,54.16704],[25.78563,54.15747],[25.78553,54.23327],[25.68513,54.31727],[25.55425,54.31591],[25.5376,54.33158],[25.63371,54.42075],[25.62203,54.4656],[25.64813,54.48704],[25.68045,54.5321],[25.75977,54.57252],[25.74122,54.80108],[25.89462,54.93438],[25.99129,54.95705],[26.05907,54.94631],[26.13386,54.98924],[26.20397,54.99729],[26.26941,55.08032],[26.23202,55.10439],[26.30628,55.12536],[26.35121,55.1525],[26.46249,55.12814],[26.51481,55.16051],[26.54753,55.14181],[26.69243,55.16718],[26.68075,55.19787],[26.72983,55.21788],[26.73017,55.24226],[26.835,55.28182],[26.83266,55.30444],[26.80929,55.31642],[26.6714,55.33902],[26.5709,55.32572],[26.44937,55.34832],[26.5522,55.40277],[26.55094,55.5093],[26.63167,55.57887],[26.63231,55.67968],[26.58248,55.6754],[26.46661,55.70375],[26.39561,55.71156],[26.18509,55.86813],[26.03815,55.95884],[25.90047,56.0013],[25.85893,56.00188],[25.81773,56.05444],[25.69246,56.08892],[25.68588,56.14725],[25.53621,56.16663],[25.39751,56.15707],[25.23099,56.19147],[25.09325,56.1878],[25.05762,56.26742],[24.89005,56.46666]]]]}},{type:"Feature",properties:{iso1A2:"LU",iso1A3:"LUX",iso1N3:"442",wikidata:"Q32",nameEn:"Luxembourg",groups:["EU","155","150"],callingCodes:["352"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.1379,50.12964],[6.1137,50.13668],[6.12028,50.16374],[6.08577,50.17246],[6.06406,50.15344],[6.03093,50.16362],[6.02488,50.18283],[5.96453,50.17259],[5.95929,50.13295],[5.89488,50.11476],[5.8857,50.07824],[5.85474,50.06342],[5.86904,50.04614],[5.8551,50.02683],[5.81866,50.01286],[5.82331,49.99662],[5.83968,49.9892],[5.83467,49.97823],[5.81163,49.97142],[5.80833,49.96451],[5.77291,49.96056],[5.77314,49.93646],[5.73621,49.89796],[5.78415,49.87922],[5.75269,49.8711],[5.75861,49.85631],[5.74567,49.85368],[5.75884,49.84811],[5.74953,49.84709],[5.74975,49.83933],[5.74076,49.83823],[5.7404,49.83452],[5.74844,49.82435],[5.74364,49.82058],[5.74953,49.81428],[5.75409,49.79239],[5.78871,49.7962],[5.82245,49.75048],[5.83149,49.74729],[5.82562,49.72395],[5.84193,49.72161],[5.86503,49.72739],[5.88677,49.70951],[5.86527,49.69291],[5.86175,49.67862],[5.9069,49.66377],[5.90164,49.6511],[5.90599,49.63853],[5.88552,49.63507],[5.88393,49.62802],[5.87609,49.62047],[5.8762,49.60898],[5.84826,49.5969],[5.84971,49.58674],[5.86986,49.58756],[5.87256,49.57539],[5.8424,49.56082],[5.84692,49.55663],[5.84143,49.5533],[5.81838,49.54777],[5.80871,49.5425],[5.81664,49.53775],[5.83648,49.5425],[5.84466,49.53027],[5.83467,49.52717],[5.83389,49.52152],[5.86571,49.50015],[5.94128,49.50034],[5.94224,49.49608],[5.96876,49.49053],[5.97693,49.45513],[6.02648,49.45451],[6.02743,49.44845],[6.04176,49.44801],[6.05553,49.46663],[6.07887,49.46399],[6.08373,49.45594],[6.10072,49.45268],[6.09845,49.46351],[6.10325,49.4707],[6.12346,49.4735],[6.12814,49.49365],[6.14321,49.48796],[6.16115,49.49297],[6.15366,49.50226],[6.17386,49.50934],[6.19543,49.50536],[6.2409,49.51408],[6.25029,49.50609],[6.27875,49.503],[6.28818,49.48465],[6.3687,49.4593],[6.36778,49.46937],[6.36907,49.48931],[6.36788,49.50377],[6.35666,49.52931],[6.38072,49.55171],[6.38228,49.55855],[6.35825,49.57053],[6.36676,49.57813],[6.38024,49.57593],[6.38342,49.5799],[6.37464,49.58886],[6.385,49.59946],[6.39822,49.60081],[6.41861,49.61723],[6.4413,49.65722],[6.43768,49.66021],[6.42726,49.66078],[6.42937,49.66857],[6.44654,49.67799],[6.46048,49.69092],[6.48014,49.69767],[6.49785,49.71118],[6.50647,49.71353],[6.5042,49.71808],[6.49694,49.72205],[6.49535,49.72645],[6.50261,49.72718],[6.51397,49.72058],[6.51805,49.72425],[6.50193,49.73291],[6.50174,49.75292],[6.51646,49.75961],[6.51828,49.76855],[6.51056,49.77515],[6.51669,49.78336],[6.50534,49.78952],[6.52169,49.79787],[6.53122,49.80666],[6.52121,49.81338],[6.51215,49.80124],[6.50647,49.80916],[6.48718,49.81267],[6.47111,49.82263],[6.45425,49.81164],[6.44131,49.81443],[6.42905,49.81091],[6.42521,49.81591],[6.40022,49.82029],[6.36576,49.85032],[6.34267,49.84974],[6.33585,49.83785],[6.32098,49.83728],[6.32303,49.85133],[6.30963,49.87021],[6.29692,49.86685],[6.28874,49.87592],[6.26146,49.88203],[6.23496,49.89972],[6.22926,49.92096],[6.21882,49.92403],[6.22608,49.929],[6.22094,49.94955],[6.19856,49.95053],[6.19089,49.96991],[6.18045,49.96611],[6.18554,49.95622],[6.17872,49.9537],[6.16466,49.97086],[6.1701,49.98518],[6.14147,49.99563],[6.14948,50.00908],[6.13806,50.01056],[6.1295,50.01849],[6.13273,50.02019],[6.13794,50.01466],[6.14666,50.02207],[6.13044,50.02929],[6.13458,50.04141],[6.11274,50.05916],[6.12055,50.09171],[6.1379,50.12964]]]]}},{type:"Feature",properties:{iso1A2:"LV",iso1A3:"LVA",iso1N3:"428",wikidata:"Q211",nameEn:"Latvia",groups:["EU","154","150"],callingCodes:["371"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.34698,57.52242],[26.90364,57.62823],[26.54675,57.51813],[26.46527,57.56885],[26.29253,57.59244],[26.1866,57.6849],[26.2029,57.7206],[26.08098,57.76619],[26.0543,57.76105],[26.03332,57.7718],[26.02415,57.76865],[26.02069,57.77169],[26.0266,57.77441],[26.027,57.78158],[26.02456,57.78342],[26.0324,57.79037],[26.05949,57.84744],[25.73499,57.90193],[25.29581,58.08288],[25.28237,57.98539],[25.19484,58.0831],[24.3579,57.87471],[24.26221,57.91787],[23.20055,57.56697],[22.80496,57.87798],[19.84909,57.57876],[19.64795,57.06466],[20.68447,56.04073],[21.15016,56.07818],[21.24644,56.16917],[21.49736,56.29106],[21.57888,56.31406],[21.74558,56.33181],[22.00548,56.41508],[22.09728,56.42851],[22.3361,56.4016],[22.56441,56.39305],[22.69354,56.36284],[22.83048,56.367],[22.96988,56.41213],[23.09531,56.30511],[23.17312,56.36795],[23.31606,56.3827],[23.40486,56.37689],[23.49803,56.34307],[23.75726,56.37282],[24.02657,56.3231],[24.13139,56.24881],[24.32334,56.30226],[24.42746,56.26522],[24.58143,56.29125],[24.57353,56.31525],[24.70022,56.40483],[24.83686,56.41565],[24.89005,56.46666],[25.05762,56.26742],[25.09325,56.1878],[25.23099,56.19147],[25.39751,56.15707],[25.53621,56.16663],[25.68588,56.14725],[25.69246,56.08892],[25.81773,56.05444],[25.85893,56.00188],[25.90047,56.0013],[26.03815,55.95884],[26.18509,55.86813],[26.39561,55.71156],[26.46661,55.70375],[26.58248,55.6754],[26.63231,55.67968],[26.64888,55.70515],[26.71802,55.70645],[26.76872,55.67658],[26.87448,55.7172],[26.97153,55.8102],[27.1559,55.85032],[27.27804,55.78299],[27.3541,55.8089],[27.61683,55.78558],[27.63065,55.89687],[27.97865,56.11849],[28.15217,56.16964],[28.23716,56.27588],[28.16599,56.37806],[28.19057,56.44637],[28.10069,56.524],[28.13526,56.57989],[28.04768,56.59004],[27.86101,56.88204],[27.66511,56.83921],[27.86101,57.29402],[27.52453,57.42826],[27.56832,57.53728],[27.34698,57.52242]]]]}},{type:"Feature",properties:{iso1A2:"LY",iso1A3:"LBY",iso1N3:"434",wikidata:"Q1016",nameEn:"Libya",groups:["015","002"],callingCodes:["218"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.5213,33.45682],[11.66543,33.34642],[11.56255,33.16754],[11.55852,33.1409],[11.51549,33.09826],[11.46037,32.6307],[11.57828,32.48013],[11.53898,32.4138],[11.04234,32.2145],[10.7315,31.97235],[10.62788,31.96629],[10.48497,31.72956],[10.31364,31.72648],[10.12239,31.42098],[10.29516,30.90337],[9.88152,30.34074],[9.76848,30.34366],[9.55544,30.23971],[9.3876,30.16738],[9.78136,29.40961],[9.89569,26.57696],[9.51696,26.39148],[9.38834,26.19288],[10.03146,25.35635],[10.02432,24.98124],[10.33159,24.5465],[10.85323,24.5595],[11.41061,24.21456],[11.62498,24.26669],[11.96886,23.51735],[13.5631,23.16574],[14.22918,22.61719],[14.99751,23.00539],[15.99566,23.49639],[23.99539,19.49944],[23.99715,20.00038],[24.99794,19.99661],[24.99885,21.99535],[24.99968,29.24574],[24.71117,30.17441],[25.01077,30.73861],[24.83101,31.31921],[25.06041,31.57937],[25.14001,31.67534],[25.63787,31.9359],[22.5213,33.45682]]]]}},{type:"Feature",properties:{iso1A2:"MA",iso1A3:"MAR",iso1N3:"504",wikidata:"Q1028",nameEn:"Morocco",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.27707,35.35051],[-2.85819,35.63219],[-5.10878,36.05227],[-5.64962,35.93752],[-7.27694,35.93599],[-14.43883,27.02969],[-17.27295,21.93519],[-17.21511,21.34226],[-17.02707,21.34022],[-16.9978,21.36239],[-16.44269,21.39745],[-14.78487,21.36587],[-14.47329,21.63839],[-14.48112,22.00886],[-14.1291,22.41636],[-14.10361,22.75501],[-13.75627,23.77231],[-13.00628,24.01923],[-12.92147,24.39502],[-12.12281,25.13682],[-12.06001,26.04442],[-11.62052,26.05229],[-11.38635,26.611],[-11.23622,26.72023],[-11.35695,26.8505],[-10.68417,26.90984],[-9.81998,26.71379],[-9.56957,26.90042],[-9.08698,26.98639],[-8.71787,26.9898],[-8.77527,27.66663],[-8.66879,27.6666],[-8.6715,28.71194],[-7.61585,29.36252],[-6.95824,29.50924],[-6.78351,29.44634],[-6.69965,29.51623],[-5.75616,29.61407],[-5.72121,29.52322],[-5.58831,29.48103],[-5.21671,29.95253],[-4.6058,30.28343],[-4.31774,30.53229],[-3.64735,30.67539],[-3.65418,30.85566],[-3.54944,31.0503],[-3.77103,31.14984],[-3.77647,31.31912],[-3.66386,31.39202],[-3.66314,31.6339],[-2.82784,31.79459],[-2.93873,32.06557],[-2.46166,32.16603],[-1.22829,32.07832],[-1.15735,32.12096],[-1.24453,32.1917],[-1.24998,32.32993],[-0.9912,32.52467],[-1.37794,32.73628],[-1.54244,32.95499],[-1.46249,33.0499],[-1.67067,33.27084],[-1.59508,33.59929],[-1.73494,33.71721],[-1.64666,34.10405],[-1.78042,34.39018],[-1.69788,34.48056],[-1.84569,34.61907],[-1.73707,34.74226],[-1.97469,34.886],[-1.97833,34.93218],[-2.04734,34.93218],[-2.21445,35.04378],[-2.21248,35.08532],[-2.27707,35.35051]],[[-2.92224,35.3401],[-2.92181,35.28599],[-2.92674,35.27313],[-2.93893,35.26737],[-2.95065,35.26576],[-2.95431,35.2728],[-2.96516,35.27967],[-2.96826,35.28296],[-2.96507,35.28801],[-2.97035,35.28852],[-2.96978,35.29459],[-2.96648,35.30475],[-2.96038,35.31609],[-2.92224,35.3401]],[[-3.90602,35.21494],[-3.90288,35.22024],[-3.88617,35.21406],[-3.88926,35.20841],[-3.90602,35.21494]],[[-4.30191,35.17419],[-4.29436,35.17149],[-4.30112,35.17058],[-4.30191,35.17419]],[[-2.41312,35.17111],[-2.44887,35.17075],[-2.44896,35.18777],[-2.41265,35.1877],[-2.41312,35.17111]],[[-5.38491,35.92591],[-5.27635,35.91222],[-5.27056,35.88794],[-5.34379,35.8711],[-5.35844,35.87375],[-5.37338,35.88417],[-5.38491,35.92591]]]]}},{type:"Feature",properties:{iso1A2:"MC",iso1A3:"MCO",iso1N3:"492",wikidata:"Q235",nameEn:"Monaco",groups:["155","150"],callingCodes:["377"]},geometry:{type:"MultiPolygon",coordinates:[[[[7.47823,43.73341],[7.4379,43.74963],[7.4389,43.75151],[7.43708,43.75197],[7.43624,43.75014],[7.43013,43.74895],[7.42809,43.74396],[7.42443,43.74087],[7.42299,43.74176],[7.42062,43.73977],[7.41233,43.73439],[7.41298,43.73311],[7.41291,43.73168],[7.41113,43.73156],[7.40903,43.7296],[7.42422,43.72209],[7.47823,43.73341]]]]}},{type:"Feature",properties:{iso1A2:"MD",iso1A3:"MDA",iso1N3:"498",wikidata:"Q217",nameEn:"Moldova",groups:["151","150"],callingCodes:["373"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.74422,48.45926],[27.6658,48.44034],[27.59027,48.46311],[27.5889,48.49224],[27.46942,48.454],[27.44333,48.41209],[27.37741,48.41026],[27.37604,48.44398],[27.32159,48.4434],[27.27855,48.37534],[27.13434,48.37288],[27.08078,48.43214],[27.0231,48.42485],[27.03821,48.37653],[26.93384,48.36558],[26.85556,48.41095],[26.71274,48.40388],[26.82809,48.31629],[26.79239,48.29071],[26.6839,48.35828],[26.62823,48.25804],[26.81161,48.25049],[26.87708,48.19919],[26.94265,48.1969],[26.98042,48.15752],[26.96119,48.13003],[27.04118,48.12522],[27.02985,48.09083],[27.15622,47.98538],[27.1618,47.92391],[27.29069,47.73722],[27.25519,47.71366],[27.32202,47.64009],[27.3979,47.59473],[27.47942,47.48113],[27.55731,47.46637],[27.60263,47.32507],[27.68706,47.28962],[27.73172,47.29248],[27.81892,47.1381],[28.09095,46.97621],[28.12173,46.82283],[28.24808,46.64305],[28.22281,46.50481],[28.25769,46.43334],[28.18902,46.35283],[28.19864,46.31869],[28.10937,46.22852],[28.13684,46.18099],[28.08612,46.01105],[28.13111,45.92819],[28.16568,45.6421],[28.08927,45.6051],[28.18741,45.47358],[28.21139,45.46895],[28.30201,45.54744],[28.41836,45.51715],[28.43072,45.48538],[28.51449,45.49982],[28.49252,45.56716],[28.54196,45.58062],[28.51587,45.6613],[28.47879,45.66994],[28.52823,45.73803],[28.70401,45.78019],[28.69852,45.81753],[28.78503,45.83475],[28.74383,45.96664],[28.98004,46.00385],[29.00613,46.04962],[28.94643,46.09176],[29.06656,46.19716],[28.94953,46.25852],[28.98478,46.31803],[29.004,46.31495],[28.9306,46.45699],[29.01241,46.46177],[29.02409,46.49582],[29.23547,46.55435],[29.24886,46.37912],[29.35357,46.49505],[29.49914,46.45889],[29.5939,46.35472],[29.6763,46.36041],[29.66359,46.4215],[29.74496,46.45605],[29.88329,46.35851],[29.94114,46.40114],[30.09103,46.38694],[30.16794,46.40967],[30.02511,46.45132],[29.88916,46.54302],[29.94409,46.56002],[29.9743,46.75325],[29.94522,46.80055],[29.98814,46.82358],[29.87405,46.88199],[29.75458,46.8604],[29.72986,46.92234],[29.57056,46.94766],[29.62137,47.05069],[29.61038,47.09932],[29.53044,47.07851],[29.49732,47.12878],[29.57696,47.13581],[29.54996,47.24962],[29.59665,47.25521],[29.5733,47.36508],[29.48678,47.36043],[29.47854,47.30366],[29.39889,47.30179],[29.3261,47.44664],[29.18603,47.43387],[29.11743,47.55001],[29.22414,47.60012],[29.22242,47.73607],[29.27255,47.79953],[29.20663,47.80367],[29.27804,47.88893],[29.19839,47.89261],[29.1723,47.99013],[28.9306,47.96255],[28.8414,48.03392],[28.85232,48.12506],[28.69896,48.13106],[28.53921,48.17453],[28.48428,48.0737],[28.42454,48.12047],[28.43701,48.15832],[28.38712,48.17567],[28.34009,48.13147],[28.30609,48.14018],[28.30586,48.1597],[28.34912,48.1787],[28.36996,48.20543],[28.35519,48.24957],[28.32508,48.23384],[28.2856,48.23202],[28.19314,48.20749],[28.17666,48.25963],[28.07504,48.23494],[28.09873,48.3124],[28.04527,48.32661],[27.95883,48.32368],[27.88391,48.36699],[27.87533,48.4037],[27.81902,48.41874],[27.79225,48.44244],[27.74422,48.45926]]]]}},{type:"Feature",properties:{iso1A2:"ME",iso1A3:"MNE",iso1N3:"499",wikidata:"Q236",nameEn:"Montenegro",groups:["039","150"],callingCodes:["382"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.22807,43.5264],[19.15685,43.53943],[19.13933,43.5282],[19.04934,43.50384],[19.01078,43.55806],[18.91379,43.50299],[18.95469,43.49367],[18.96053,43.45042],[19.01078,43.43854],[19.04071,43.397],[19.08673,43.31453],[19.08206,43.29668],[19.04233,43.30008],[19.00844,43.24988],[18.95001,43.29327],[18.95819,43.32899],[18.90911,43.36383],[18.83912,43.34795],[18.84794,43.33735],[18.85342,43.32426],[18.76538,43.29838],[18.6976,43.25243],[18.71747,43.2286],[18.66605,43.2056],[18.64735,43.14766],[18.66254,43.03928],[18.52232,43.01451],[18.49076,42.95553],[18.49661,42.89306],[18.4935,42.86433],[18.47633,42.85829],[18.45921,42.81682],[18.47324,42.74992],[18.56789,42.72074],[18.55221,42.69045],[18.54603,42.69171],[18.54841,42.68328],[18.57373,42.64429],[18.52232,42.62279],[18.55504,42.58409],[18.53751,42.57376],[18.49778,42.58409],[18.43735,42.55921],[18.44307,42.51077],[18.43588,42.48556],[18.52152,42.42302],[18.54128,42.39171],[18.45131,42.21682],[19.26406,41.74971],[19.37597,41.84849],[19.37451,41.8842],[19.33812,41.90669],[19.34601,41.95675],[19.37691,41.96977],[19.36867,42.02564],[19.37548,42.06835],[19.40687,42.10024],[19.28623,42.17745],[19.42,42.33019],[19.42352,42.36546],[19.4836,42.40831],[19.65972,42.62774],[19.73244,42.66299],[19.77375,42.58517],[19.74731,42.57422],[19.76549,42.50237],[19.82333,42.46581],[19.9324,42.51699],[20.00842,42.5109],[20.01834,42.54622],[20.07761,42.55582],[20.0969,42.65559],[20.02915,42.71147],[20.02088,42.74789],[20.04898,42.77701],[20.2539,42.76245],[20.27869,42.81945],[20.35692,42.8335],[20.34528,42.90676],[20.16415,42.97177],[20.14896,42.99058],[20.12325,42.96237],[20.05431,42.99571],[20.04729,43.02732],[19.98887,43.0538],[19.96549,43.11098],[19.92576,43.08539],[19.79255,43.11951],[19.76918,43.16044],[19.64063,43.19027],[19.62661,43.2286],[19.54598,43.25158],[19.52962,43.31623],[19.48171,43.32644],[19.44315,43.38846],[19.22229,43.47926],[19.22807,43.5264]]]]}},{type:"Feature",properties:{iso1A2:"MF",iso1A3:"MAF",iso1N3:"663",wikidata:"Q126125",nameEn:"Saint-Martin",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904]]]]}},{type:"Feature",properties:{iso1A2:"MG",iso1A3:"MDG",iso1N3:"450",wikidata:"Q1019",nameEn:"Madagascar",aliases:["RM"],groups:["014","202","002"],callingCodes:["261"]},geometry:{type:"MultiPolygon",coordinates:[[[[51.94557,-12.74579],[49.10033,-10.96054],[43.72277,-16.09877],[40.40841,-23.17181],[45.90777,-29.77366],[51.94557,-12.74579]]]]}},{type:"Feature",properties:{iso1A2:"MH",iso1A3:"MHL",iso1N3:"584",wikidata:"Q709",nameEn:"Marshall Islands",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["692"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[173.53711,5.70687],[169.29099,15.77133],[159.04653,10.59067],[169,3.9]]]]}},{type:"Feature",properties:{iso1A2:"MK",iso1A3:"MKD",iso1N3:"807",wikidata:"Q221",nameEn:"North Macedonia",groups:["039","150"],callingCodes:["389"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.34773,42.31725],[22.29275,42.34913],[22.29605,42.37477],[22.16384,42.32103],[22.02908,42.29848],[21.94405,42.34669],[21.91595,42.30392],[21.84654,42.3247],[21.77176,42.2648],[21.70111,42.23789],[21.58992,42.25915],[21.52145,42.24465],[21.50823,42.27156],[21.43882,42.2789],[21.43882,42.23609],[21.38428,42.24465],[21.30496,42.1418],[21.29913,42.13954],[21.31983,42.10993],[21.22728,42.08909],[21.16614,42.19815],[21.11491,42.20794],[20.75464,42.05229],[20.76786,41.91839],[20.68523,41.85318],[20.59524,41.8818],[20.55976,41.87068],[20.57144,41.7897],[20.53405,41.78099],[20.51301,41.72433],[20.52937,41.69292],[20.51769,41.65975],[20.55508,41.58113],[20.52103,41.56473],[20.45809,41.5549],[20.45331,41.51436],[20.49039,41.49277],[20.51301,41.442],[20.55976,41.4087],[20.52119,41.34381],[20.49432,41.33679],[20.51068,41.2323],[20.59715,41.13644],[20.58546,41.11179],[20.59832,41.09066],[20.63454,41.0889],[20.65558,41.08009],[20.71634,40.91781],[20.73504,40.9081],[20.81567,40.89662],[20.83671,40.92752],[20.94305,40.92399],[20.97693,40.90103],[20.97887,40.85475],[21.15262,40.85546],[21.21105,40.8855],[21.25779,40.86165],[21.35595,40.87578],[21.41555,40.9173],[21.53007,40.90759],[21.57448,40.86076],[21.69601,40.9429],[21.7556,40.92525],[21.91102,41.04786],[21.90869,41.09191],[22.06527,41.15617],[22.1424,41.12449],[22.17629,41.15969],[22.26744,41.16409],[22.42285,41.11921],[22.5549,41.13065],[22.58295,41.11568],[22.62852,41.14385],[22.65306,41.18168],[22.71266,41.13945],[22.74538,41.16321],[22.76408,41.32225],[22.81199,41.3398],[22.93334,41.34104],[22.96331,41.35782],[22.95513,41.63265],[23.03342,41.71034],[23.01239,41.76527],[22.96682,41.77137],[22.90254,41.87587],[22.86749,42.02275],[22.67701,42.06614],[22.51224,42.15457],[22.50289,42.19527],[22.47251,42.20393],[22.38136,42.30339],[22.34773,42.31725]]]]}},{type:"Feature",properties:{iso1A2:"ML",iso1A3:"MLI",iso1N3:"466",wikidata:"Q912",nameEn:"Mali",groups:["011","202","002"],callingCodes:["223"]},geometry:{type:"MultiPolygon",coordinates:[[[[-4.83423,24.99935],[-6.57191,25.0002],[-5.60725,16.49919],[-5.33435,16.33354],[-5.50165,15.50061],[-9.32979,15.50032],[-9.31106,15.69412],[-9.33314,15.7044],[-9.44673,15.60553],[-9.40447,15.4396],[-10.71721,15.4223],[-10.90932,15.11001],[-11.43483,15.62339],[-11.70705,15.51558],[-11.94903,14.76143],[-12.23936,14.76324],[-11.93043,13.84505],[-12.06897,13.71049],[-11.83345,13.33333],[-11.63025,13.39174],[-11.39935,12.97808],[-11.37536,12.40788],[-11.50006,12.17826],[-11.24136,12.01286],[-10.99758,12.24634],[-10.80355,12.1053],[-10.71897,11.91552],[-10.30604,12.24634],[-9.714,12.0226],[-9.63938,12.18312],[-9.32097,12.29009],[-9.38067,12.48446],[-9.13689,12.50875],[-8.94784,12.34842],[-8.80854,11.66715],[-8.40058,11.37466],[-8.66923,10.99397],[-8.35083,11.06234],[-8.2667,10.91762],[-8.32614,10.69273],[-8.22711,10.41722],[-8.10207,10.44649],[-7.9578,10.2703],[-7.97971,10.17117],[-7.92107,10.15577],[-7.63048,10.46334],[-7.54462,10.40921],[-7.52261,10.4655],[-7.44555,10.44602],[-7.3707,10.24677],[-7.13331,10.24877],[-7.0603,10.14711],[-7.00966,10.15794],[-6.97444,10.21644],[-7.01186,10.25111],[-6.93921,10.35291],[-6.68164,10.35074],[-6.63541,10.66893],[-6.52974,10.59104],[-6.42847,10.5694],[-6.40646,10.69922],[-6.325,10.68624],[-6.24795,10.74248],[-6.1731,10.46983],[-6.18851,10.24244],[-5.99478,10.19694],[-5.78124,10.43952],[-5.65135,10.46767],[-5.51058,10.43177],[-5.46643,10.56074],[-5.47083,10.75329],[-5.41579,10.84628],[-5.49284,11.07538],[-5.32994,11.13371],[-5.32553,11.21578],[-5.25949,11.24816],[-5.25509,11.36905],[-5.20665,11.43811],[-5.22867,11.60421],[-5.29251,11.61715],[-5.26389,11.75728],[-5.40258,11.8327],[-5.26389,11.84778],[-5.07897,11.97918],[-4.72893,12.01579],[-4.70692,12.06746],[-4.62987,12.06531],[-4.62546,12.13204],[-4.54841,12.1385],[-4.57703,12.19875],[-4.41412,12.31922],[-4.47356,12.71252],[-4.238,12.71467],[-4.21819,12.95722],[-4.34477,13.12927],[-3.96501,13.49778],[-3.90558,13.44375],[-3.96282,13.38164],[-3.7911,13.36665],[-3.54454,13.1781],[-3.4313,13.1588],[-3.43507,13.27272],[-3.23599,13.29035],[-3.28396,13.5422],[-3.26407,13.70699],[-2.88189,13.64921],[-2.90831,13.81174],[-2.84667,14.05532],[-2.66175,14.14713],[-2.47587,14.29671],[-2.10223,14.14878],[-1.9992,14.19011],[-1.97945,14.47709],[-1.68083,14.50023],[-1.32166,14.72774],[-1.05875,14.7921],[-0.72004,15.08655],[-0.24673,15.07805],[0.06588,14.96961],[0.23859,15.00135],[0.72632,14.95898],[0.96711,14.98275],[1.31275,15.27978],[3.01806,15.34571],[3.03134,15.42221],[3.50368,15.35934],[4.19893,16.39923],[4.21787,17.00118],[4.26762,17.00432],[4.26651,19.14224],[3.36082,18.9745],[3.12501,19.1366],[3.24648,19.81703],[1.20992,20.73533],[1.15698,21.12843],[-4.83423,24.99935]]]]}},{type:"Feature",properties:{iso1A2:"MM",iso1A3:"MMR",iso1N3:"104",wikidata:"Q836",nameEn:"Myanmar",aliases:["Burma","BU"],groups:["035","142"],callingCodes:["95"]},geometry:{type:"MultiPolygon",coordinates:[[[[92.62187,21.87037],[92.59775,21.6092],[92.68152,21.28454],[92.60187,21.24615],[92.55105,21.3856],[92.43158,21.37025],[92.37939,21.47764],[92.20087,21.337],[92.17752,21.17445],[92.26071,21.05697],[92.37665,20.72172],[92.28464,20.63179],[92.31348,20.57137],[92.4302,20.5688],[92.39837,20.38919],[92.61042,13.76986],[94.6371,13.81803],[97.63455,9.60854],[98.12555,9.44056],[98.33094,9.91973],[98.47298,9.95782],[98.52291,9.92389],[98.55174,9.92804],[98.7391,10.31488],[98.81944,10.52761],[98.77275,10.62548],[98.78511,10.68351],[98.86819,10.78336],[99.0069,10.85485],[98.99701,10.92962],[99.02337,10.97217],[99.06938,10.94857],[99.32756,11.28545],[99.31573,11.32081],[99.39485,11.3925],[99.47598,11.62434],[99.5672,11.62732],[99.64108,11.78948],[99.64891,11.82699],[99.53424,12.02317],[99.56445,12.14805],[99.47519,12.1353],[99.409,12.60603],[99.29254,12.68921],[99.18905,12.84799],[99.18748,12.9898],[99.10646,13.05804],[99.12225,13.19847],[99.20617,13.20575],[99.16695,13.72621],[98.97356,14.04868],[98.56762,14.37701],[98.24874,14.83013],[98.18821,15.13125],[98.22,15.21327],[98.30446,15.30667],[98.40522,15.25268],[98.41906,15.27103],[98.39351,15.34177],[98.4866,15.39154],[98.56027,15.33471],[98.58598,15.46821],[98.541,15.65406],[98.59853,15.87197],[98.57019,16.04578],[98.69585,16.13353],[98.8376,16.11706],[98.92656,16.36425],[98.84485,16.42354],[98.68074,16.27068],[98.63817,16.47424],[98.57912,16.55983],[98.5695,16.62826],[98.51113,16.64503],[98.51833,16.676],[98.51472,16.68521],[98.51579,16.69433],[98.51043,16.70107],[98.49713,16.69022],[98.50253,16.7139],[98.46994,16.73613],[98.53833,16.81934],[98.49603,16.8446],[98.52624,16.89979],[98.39441,17.06266],[98.34566,17.04822],[98.10439,17.33847],[98.11185,17.36829],[97.91829,17.54504],[97.76407,17.71595],[97.66794,17.88005],[97.73723,17.97912],[97.60841,18.23846],[97.64116,18.29778],[97.56219,18.33885],[97.50383,18.26844],[97.34522,18.54596],[97.36444,18.57138],[97.5258,18.4939],[97.76752,18.58097],[97.73836,18.88478],[97.66487,18.9371],[97.73654,18.9812],[97.73797,19.04261],[97.83479,19.09972],[97.84024,19.22217],[97.78606,19.26769],[97.84186,19.29526],[97.78769,19.39429],[97.88423,19.5041],[97.84715,19.55782],[98.04364,19.65755],[98.03314,19.80941],[98.13829,19.78541],[98.24884,19.67876],[98.51182,19.71303],[98.56065,19.67807],[98.83661,19.80931],[98.98679,19.7419],[99.0735,20.10298],[99.20328,20.12877],[99.416,20.08614],[99.52943,20.14811],[99.5569,20.20676],[99.46077,20.36198],[99.46008,20.39673],[99.68255,20.32077],[99.81096,20.33687],[99.86383,20.44371],[99.88211,20.44488],[99.88451,20.44596],[99.89168,20.44548],[99.89301,20.44311],[99.89692,20.44789],[99.90499,20.4487],[99.91616,20.44986],[99.95721,20.46301],[100.08404,20.36626],[100.1957,20.68247],[100.36375,20.82783],[100.51079,20.82194],[100.60112,20.8347],[100.64628,20.88279],[100.50974,20.88574],[100.55281,21.02796],[100.63578,21.05639],[100.72716,21.31786],[100.80173,21.2934],[101.00234,21.39612],[101.16198,21.52808],[101.15156,21.56129],[101.11744,21.77659],[100.87265,21.67396],[100.72143,21.51898],[100.57861,21.45637],[100.4811,21.46148],[100.42892,21.54325],[100.35201,21.53176],[100.25863,21.47043],[100.18447,21.51898],[100.1625,21.48704],[100.12542,21.50365],[100.10757,21.59945],[100.17486,21.65306],[100.12679,21.70539],[100.04956,21.66843],[99.98654,21.71064],[99.94003,21.82782],[99.99084,21.97053],[99.96612,22.05965],[99.85351,22.04183],[99.47585,22.13345],[99.33166,22.09656],[99.1552,22.15874],[99.19176,22.16983],[99.17318,22.18025],[99.28771,22.4105],[99.37972,22.50188],[99.38247,22.57544],[99.31243,22.73893],[99.45654,22.85726],[99.43537,22.94086],[99.54218,22.90014],[99.52214,23.08218],[99.34127,23.13099],[99.25741,23.09025],[99.04601,23.12215],[99.05975,23.16382],[98.88597,23.18656],[98.92515,23.29535],[98.93958,23.31414],[98.87573,23.33038],[98.92104,23.36946],[98.87683,23.48995],[98.82877,23.47908],[98.80294,23.5345],[98.88396,23.59555],[98.81775,23.694],[98.82933,23.72921],[98.79607,23.77947],[98.68209,23.80492],[98.67797,23.9644],[98.89632,24.10612],[98.87998,24.15624],[98.85319,24.13042],[98.59256,24.08371],[98.54476,24.13119],[98.20666,24.11406],[98.07806,24.07988],[98.06703,24.08028],[98.0607,24.07812],[98.05671,24.07961],[98.05302,24.07408],[98.04709,24.07616],[97.99583,24.04932],[97.98691,24.03897],[97.93951,24.01953],[97.90998,24.02094],[97.88616,24.00463],[97.88414,23.99405],[97.88814,23.98605],[97.89683,23.98389],[97.89676,23.97931],[97.8955,23.97758],[97.88811,23.97446],[97.86545,23.97723],[97.84328,23.97603],[97.79416,23.95663],[97.79456,23.94836],[97.72302,23.89288],[97.64667,23.84574],[97.5247,23.94032],[97.62363,24.00506],[97.72903,24.12606],[97.75305,24.16902],[97.72799,24.18883],[97.72998,24.2302],[97.76799,24.26365],[97.71941,24.29652],[97.66723,24.30027],[97.65624,24.33781],[97.7098,24.35658],[97.66998,24.45288],[97.60029,24.4401],[97.52757,24.43748],[97.56286,24.54535],[97.56525,24.72838],[97.54675,24.74202],[97.5542,24.74943],[97.56383,24.75535],[97.56648,24.76475],[97.64354,24.79171],[97.70181,24.84557],[97.73127,24.83015],[97.76481,24.8289],[97.79949,24.85655],[97.72903,24.91332],[97.72216,25.08508],[97.77023,25.11492],[97.83614,25.2715],[97.92541,25.20815],[98.14925,25.41547],[98.12591,25.50722],[98.18084,25.56298],[98.16848,25.62739],[98.25774,25.6051],[98.31268,25.55307],[98.40606,25.61129],[98.54064,25.85129],[98.63128,25.79937],[98.70818,25.86241],[98.60763,26.01512],[98.57085,26.11547],[98.63128,26.15492],[98.66884,26.09165],[98.7329,26.17218],[98.67797,26.24487],[98.72741,26.36183],[98.77547,26.61994],[98.7333,26.85615],[98.69582,27.56499],[98.43353,27.67086],[98.42529,27.55404],[98.32641,27.51385],[98.13964,27.9478],[98.15337,28.12114],[97.90069,28.3776],[97.79632,28.33168],[97.70705,28.5056],[97.56835,28.55628],[97.50518,28.49716],[97.47085,28.2688],[97.41729,28.29783],[97.34547,28.21385],[97.31292,28.06784],[97.35412,28.06663],[97.38845,28.01329],[97.35824,27.87256],[97.29919,27.92233],[96.90112,27.62149],[96.91431,27.45752],[97.17422,27.14052],[97.14675,27.09041],[96.89132,27.17474],[96.85287,27.2065],[96.88445,27.25046],[96.73888,27.36638],[96.55761,27.29928],[96.40779,27.29818],[96.15591,27.24572],[96.04949,27.19428],[95.93002,27.04149],[95.81603,27.01335],[95.437,26.7083],[95.30339,26.65372],[95.23513,26.68499],[95.05798,26.45408],[95.12801,26.38397],[95.11428,26.1019],[95.18556,26.07338],[94.80117,25.49359],[94.68032,25.47003],[94.57458,25.20318],[94.74212,25.13606],[94.73937,25.00545],[94.60204,24.70889],[94.5526,24.70764],[94.50729,24.59281],[94.45279,24.56656],[94.32362,24.27692],[94.30215,24.23752],[94.14081,23.83333],[93.92089,23.95812],[93.80279,23.92549],[93.75952,24.0003],[93.62871,24.00922],[93.50616,23.94432],[93.46633,23.97067],[93.41415,24.07854],[93.34735,24.10151],[93.32351,24.04468],[93.36059,23.93176],[93.3908,23.92925],[93.3908,23.7622],[93.43475,23.68299],[93.38805,23.4728],[93.39981,23.38828],[93.38781,23.36139],[93.36862,23.35426],[93.38478,23.13698],[93.2878,23.00464],[93.12988,23.05772],[93.134,22.92498],[93.09417,22.69459],[93.134,22.59573],[93.11477,22.54374],[93.13537,22.45873],[93.18206,22.43716],[93.19991,22.25425],[93.14224,22.24535],[93.15734,22.18687],[93.04885,22.20595],[92.99255,22.05965],[92.99804,21.98964],[92.93899,22.02656],[92.89504,21.95143],[92.86208,22.05456],[92.70416,22.16017],[92.67532,22.03547],[92.60949,21.97638],[92.62187,21.87037]]]]}},{type:"Feature",properties:{iso1A2:"MN",iso1A3:"MNG",iso1N3:"496",wikidata:"Q711",nameEn:"Mongolia",groups:["030","142"],callingCodes:["976"]},geometry:{type:"MultiPolygon",coordinates:[[[[102.14032,51.35566],[101.5044,51.50467],[101.39085,51.45753],[100.61116,51.73028],[99.89203,51.74903],[99.75578,51.90108],[99.27888,51.96876],[98.87768,52.14563],[98.74142,51.8637],[98.33222,51.71832],[98.22053,51.46579],[98.05257,51.46696],[97.83305,51.00248],[98.01472,50.86652],[97.9693,50.78044],[98.06393,50.61262],[98.31373,50.4996],[98.29481,50.33561],[97.85197,49.91339],[97.76871,49.99861],[97.56432,49.92801],[97.56811,49.84265],[97.24639,49.74737],[96.97388,49.88413],[95.80056,50.04239],[95.74757,49.97915],[95.02465,49.96941],[94.97166,50.04725],[94.6121,50.04239],[94.49477,50.17832],[94.39258,50.22193],[94.30823,50.57498],[92.99595,50.63183],[93.01109,50.79001],[92.44714,50.78762],[92.07173,50.69585],[91.86048,50.73734],[89.59711,49.90851],[89.70687,49.72535],[88.82499,49.44808],[88.42449,49.48821],[88.17223,49.46934],[88.15543,49.30314],[87.98977,49.18147],[87.81333,49.17354],[87.88171,48.95853],[87.73822,48.89582],[88.0788,48.71436],[87.96361,48.58478],[88.58939,48.34531],[88.58316,48.21893],[88.8011,48.11302],[88.93186,48.10263],[89.0711,47.98528],[89.55453,48.0423],[89.76624,47.82745],[90.06512,47.88177],[90.10871,47.7375],[90.33598,47.68303],[90.48854,47.41826],[90.48542,47.30438],[90.76108,46.99399],[90.84035,46.99525],[91.03649,46.72916],[91.0147,46.58171],[91.07696,46.57315],[90.89639,46.30711],[90.99672,46.14207],[91.03026,46.04194],[90.70907,45.73437],[90.65114,45.49314],[90.89169,45.19667],[91.64048,45.07408],[93.51161,44.95964],[94.10003,44.71016],[94.71959,44.35284],[95.01191,44.25274],[95.39772,44.2805],[95.32891,44.02407],[95.52594,43.99353],[95.89543,43.2528],[96.35658,42.90363],[96.37926,42.72055],[97.1777,42.7964],[99.50671,42.56535],[100.33297,42.68231],[100.84979,42.67087],[101.28833,42.58524],[101.80515,42.50074],[102.07645,42.22519],[102.42826,42.15137],[102.72403,42.14675],[103.3685,41.89696],[103.92804,41.78246],[104.52258,41.8706],[104.51667,41.66113],[104.91272,41.64619],[105.01119,41.58382],[105.24708,41.7442],[106.76517,42.28741],[107.24774,42.36107],[107.29755,42.41395],[107.49681,42.46221],[107.57258,42.40898],[108.23156,42.45532],[108.84489,42.40246],[109.00679,42.45302],[109.452,42.44842],[109.89402,42.63111],[110.08401,42.6411],[110.4327,42.78293],[111.0149,43.3289],[111.59087,43.51207],[111.79758,43.6637],[111.93776,43.68709],[111.96289,43.81596],[111.40498,44.3461],[111.76275,44.98032],[111.98695,45.09074],[112.4164,45.06858],[112.74662,44.86297],[113.63821,44.74326],[113.909,44.91444],[114.08071,44.92847],[114.5166,45.27189],[114.54801,45.38337],[114.74612,45.43585],[114.94546,45.37377],[115.35757,45.39106],[115.69688,45.45761],[115.91898,45.6227],[116.16989,45.68603],[116.27366,45.78637],[116.24012,45.8778],[116.26678,45.96479],[116.58612,46.30211],[116.75551,46.33083],[116.83166,46.38637],[117.07252,46.35818],[117.36609,46.36335],[117.41782,46.57862],[117.60748,46.59771],[117.69554,46.50991],[118.30534,46.73519],[118.78747,46.68689],[118.8337,46.77742],[118.89974,46.77139],[118.92616,46.72765],[119.00541,46.74273],[119.10448,46.65516],[119.24978,46.64761],[119.30261,46.6083],[119.37306,46.61132],[119.42827,46.63783],[119.65265,46.62342],[119.68127,46.59015],[119.77373,46.62947],[119.80455,46.67631],[119.89261,46.66423],[119.91242,46.90091],[119.85518,46.92196],[119.71209,47.19192],[119.62403,47.24575],[119.56019,47.24874],[119.54918,47.29505],[119.31964,47.42617],[119.35892,47.48104],[119.13995,47.53997],[119.12343,47.66458],[118.7564,47.76947],[118.55766,47.99277],[118.29654,48.00246],[118.22677,48.03853],[118.11009,48.04],[118.03676,48.00982],[117.80196,48.01661],[117.50181,47.77216],[117.37875,47.63627],[117.08918,47.82242],[116.87527,47.88836],[116.67405,47.89039],[116.4465,47.83662],[116.2527,47.87766],[116.08431,47.80693],[115.94296,47.67741],[115.57128,47.91988],[115.52082,48.15367],[115.811,48.25699],[115.78876,48.51781],[116.06565,48.81716],[116.03781,48.87014],[116.71193,49.83813],[116.62502,49.92919],[116.22402,50.04477],[115.73602,49.87688],[115.26068,49.97367],[114.9703,50.19254],[114.325,50.28098],[113.20216,49.83356],[113.02647,49.60772],[110.64493,49.1816],[110.39891,49.25083],[110.24373,49.16676],[109.51325,49.22859],[109.18017,49.34709],[108.53969,49.32325],[108.27937,49.53167],[107.95387,49.66659],[107.96116,49.93191],[107.36407,49.97612],[107.1174,50.04239],[107.00007,50.1977],[106.80326,50.30177],[106.58373,50.34044],[106.51122,50.34408],[106.49628,50.32436],[106.47156,50.31909],[106.07865,50.33474],[106.05562,50.40582],[105.32528,50.4648],[103.70343,50.13952],[102.71178,50.38873],[102.32194,50.67982],[102.14032,51.35566]]]]}},{type:"Feature",properties:{iso1A2:"MO",iso1A3:"MAC",iso1N3:"446",wikidata:"Q14773",nameEn:"Macau",aliases:["Macao"],country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["853"]},geometry:{type:"MultiPolygon",coordinates:[[[[113.54942,22.14519],[113.54839,22.10909],[113.57191,22.07696],[113.63011,22.10782],[113.60504,22.20464],[113.57123,22.20416],[113.56865,22.20973],[113.5508,22.21672],[113.54333,22.21688],[113.54093,22.21314],[113.53593,22.2137],[113.53301,22.21235],[113.53552,22.20607],[113.52659,22.18271],[113.54093,22.15497],[113.54942,22.14519]]]]}},{type:"Feature",properties:{iso1A2:"MP",iso1A3:"MNP",iso1N3:"580",wikidata:"Q16644",nameEn:"Northern Mariana Islands",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 670"]},geometry:{type:"MultiPolygon",coordinates:[[[[143.82485,13.92273],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]]]}},{type:"Feature",properties:{iso1A2:"MQ",iso1A3:"MTQ",iso1N3:"474",wikidata:"Q17054",nameEn:"Martinique",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["596"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"MR",iso1A3:"MRT",iso1N3:"478",wikidata:"Q1025",nameEn:"Mauritania",groups:["011","202","002"],callingCodes:["222"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.60725,16.49919],[-6.57191,25.0002],[-4.83423,24.99935],[-8.66674,27.31569],[-8.66721,25.99918],[-12.0002,25.9986],[-12.00251,23.4538],[-12.14969,23.41935],[-12.36213,23.3187],[-12.5741,23.28975],[-13.00412,23.02297],[-13.10753,22.89493],[-13.15313,22.75649],[-13.08438,22.53866],[-13.01525,21.33343],[-16.95474,21.33997],[-16.99806,21.12142],[-17.0357,21.05368],[-17.0396,20.9961],[-17.06781,20.92697],[-17.0695,20.85742],[-17.0471,20.76408],[-17.15288,16.07139],[-16.50854,16.09032],[-16.48967,16.0496],[-16.44814,16.09753],[-16.4429,16.20605],[-16.27016,16.51565],[-15.6509,16.50315],[-15.00557,16.64997],[-14.32144,16.61495],[-13.80075,16.13961],[-13.43135,16.09022],[-13.11029,15.52116],[-12.23936,14.76324],[-11.94903,14.76143],[-11.70705,15.51558],[-11.43483,15.62339],[-10.90932,15.11001],[-10.71721,15.4223],[-9.40447,15.4396],[-9.44673,15.60553],[-9.33314,15.7044],[-9.31106,15.69412],[-9.32979,15.50032],[-5.50165,15.50061],[-5.33435,16.33354],[-5.60725,16.49919]]]]}},{type:"Feature",properties:{iso1A2:"MS",iso1A3:"MSR",iso1N3:"500",wikidata:"Q13353",nameEn:"Montserrat",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 664"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647]]]]}},{type:"Feature",properties:{iso1A2:"MT",iso1A3:"MLT",iso1N3:"470",wikidata:"Q233",nameEn:"Malta",groups:["EU","039","150"],driveSide:"left",callingCodes:["356"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.70991,35.79901],[14.07544,36.41525],[13.27636,35.20764],[15.70991,35.79901]]]]}},{type:"Feature",properties:{iso1A2:"MU",iso1A3:"MUS",iso1N3:"480",wikidata:"Q1027",nameEn:"Mauritius",groups:["014","202","002"],driveSide:"left",callingCodes:["230"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.73473,-21.9174],[64.11105,-21.5783],[63.47388,-9.1938],[56.09755,-9.55401],[56.73473,-21.9174]]]]}},{type:"Feature",properties:{iso1A2:"MV",iso1A3:"MDV",iso1N3:"462",wikidata:"Q826",nameEn:"Maldives",groups:["034","142"],driveSide:"left",callingCodes:["960"]},geometry:{type:"MultiPolygon",coordinates:[[[[71.27292,7.36038],[73.37814,-3.88401],[74.6203,7.39289],[71.27292,7.36038]]]]}},{type:"Feature",properties:{iso1A2:"MW",iso1A3:"MWI",iso1N3:"454",wikidata:"Q1020",nameEn:"Malawi",groups:["014","202","002"],driveSide:"left",callingCodes:["265"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.48052,-9.62442],[33.31581,-9.48554],[33.14925,-9.49322],[32.99397,-9.36712],[32.95389,-9.40138],[33.00476,-9.5133],[33.00256,-9.63053],[33.05485,-9.61316],[33.10163,-9.66525],[33.12144,-9.58929],[33.2095,-9.61099],[33.31517,-9.82364],[33.36581,-9.81063],[33.37902,-9.9104],[33.31297,-10.05133],[33.53863,-10.20148],[33.54797,-10.36077],[33.70675,-10.56896],[33.47636,-10.78465],[33.28022,-10.84428],[33.25998,-10.88862],[33.39697,-11.15296],[33.29267,-11.3789],[33.29267,-11.43536],[33.23663,-11.40637],[33.24252,-11.59302],[33.32692,-11.59248],[33.33937,-11.91252],[33.25998,-12.14242],[33.3705,-12.34931],[33.47636,-12.32498],[33.54485,-12.35996],[33.37517,-12.54085],[33.28177,-12.54692],[33.18837,-12.61377],[33.05917,-12.59554],[32.94397,-12.76868],[32.96733,-12.88251],[33.02181,-12.88707],[32.98289,-13.12671],[33.0078,-13.19492],[32.86113,-13.47292],[32.84176,-13.52794],[32.73683,-13.57682],[32.68436,-13.55769],[32.66468,-13.60019],[32.68654,-13.64268],[32.7828,-13.64805],[32.84528,-13.71576],[32.76962,-13.77224],[32.79015,-13.80755],[32.88985,-13.82956],[32.99042,-13.95689],[33.02977,-14.05022],[33.07568,-13.98447],[33.16749,-13.93992],[33.24249,-14.00019],[33.66677,-14.61306],[33.7247,-14.4989],[33.88503,-14.51652],[33.92898,-14.47929],[34.08588,-14.48893],[34.18733,-14.43823],[34.22355,-14.43607],[34.34453,-14.3985],[34.35843,-14.38652],[34.39277,-14.39467],[34.4192,-14.43191],[34.44641,-14.47746],[34.45053,-14.49873],[34.47628,-14.53363],[34.48932,-14.53646],[34.49636,-14.55091],[34.52366,-14.5667],[34.53962,-14.59776],[34.55112,-14.64494],[34.53516,-14.67782],[34.52057,-14.68263],[34.54503,-14.74672],[34.567,-14.77345],[34.61522,-14.99583],[34.57503,-15.30619],[34.43126,-15.44778],[34.44981,-15.60864],[34.25195,-15.90321],[34.43126,-16.04737],[34.40344,-16.20923],[35.04805,-16.83167],[35.13771,-16.81687],[35.17017,-16.93521],[35.04805,-17.00027],[35.0923,-17.13235],[35.3062,-17.1244],[35.27065,-16.93817],[35.30929,-16.82871],[35.27219,-16.69402],[35.14235,-16.56812],[35.25828,-16.4792],[35.30157,-16.2211],[35.43355,-16.11371],[35.52365,-16.15414],[35.70107,-16.10147],[35.80487,-16.03907],[35.85303,-15.41913],[35.78799,-15.17428],[35.91812,-14.89514],[35.87212,-14.89478],[35.86945,-14.67481],[35.5299,-14.27714],[35.47989,-14.15594],[34.86229,-13.48958],[34.60253,-13.48487],[34.37831,-12.17408],[34.46088,-12.0174],[34.70739,-12.15652],[34.82903,-12.04837],[34.57917,-11.87849],[34.64241,-11.57499],[34.96296,-11.57354],[34.91153,-11.39799],[34.79375,-11.32245],[34.63305,-11.11731],[34.61161,-11.01611],[34.67047,-10.93796],[34.65946,-10.6828],[34.57581,-10.56271],[34.51911,-10.12279],[34.54499,-10.0678],[34.03865,-9.49398],[33.95829,-9.54066],[33.9638,-9.62206],[33.93298,-9.71647],[33.76677,-9.58516],[33.48052,-9.62442]]]]}},{type:"Feature",properties:{iso1A2:"MX",iso1A3:"MEX",iso1N3:"484",wikidata:"Q96",nameEn:"Mexico",groups:["013","003","419","019"],callingCodes:["52"]},geometry:{type:"MultiPolygon",coordinates:[[[[-117.1243,32.53427],[-118.48109,32.5991],[-120.12904,18.41089],[-92.37213,14.39277],[-92.2261,14.53423],[-92.1454,14.6804],[-92.18161,14.84147],[-92.1423,14.88647],[-92.1454,14.98143],[-92.0621,15.07406],[-92.20983,15.26077],[-91.73182,16.07371],[-90.44567,16.07573],[-90.40499,16.40524],[-90.61212,16.49832],[-90.69064,16.70697],[-91.04436,16.92175],[-91.43809,17.25373],[-90.99199,17.25192],[-90.98678,17.81655],[-89.14985,17.81563],[-89.15105,17.95104],[-89.03839,18.0067],[-88.8716,17.89535],[-88.71505,18.0707],[-88.48242,18.49164],[-88.3268,18.49048],[-88.29909,18.47591],[-88.26593,18.47617],[-88.03238,18.41778],[-88.03165,18.16657],[-87.90671,18.15213],[-87.87604,18.18313],[-87.86657,18.19971],[-87.85693,18.18266],[-87.84815,18.18511],[-86.92368,17.61462],[-85.9092,21.8218],[-96.92418,25.97377],[-97.13927,25.96583],[-97.35946,25.92189],[-97.37332,25.83854],[-97.42511,25.83969],[-97.45669,25.86874],[-97.49828,25.89877],[-97.52025,25.88518],[-97.66511,26.01708],[-97.95155,26.0625],[-97.97017,26.05232],[-98.24603,26.07191],[-98.27075,26.09457],[-98.30491,26.10475],[-98.35126,26.15129],[-99.00546,26.3925],[-99.03053,26.41249],[-99.08477,26.39849],[-99.53573,27.30926],[-99.49744,27.43746],[-99.482,27.47128],[-99.48045,27.49016],[-99.50208,27.50021],[-99.52955,27.49747],[-99.51478,27.55836],[-99.55409,27.61314],[-100.50029,28.66117],[-100.51222,28.70679],[-100.5075,28.74066],[-100.52313,28.75598],[-100.59809,28.88197],[-100.63689,28.90812],[-100.67294,29.09744],[-100.79696,29.24688],[-100.87982,29.296],[-100.94056,29.33371],[-100.94579,29.34523],[-100.96725,29.3477],[-101.01128,29.36947],[-101.05686,29.44738],[-101.47277,29.7744],[-102.60596,29.8192],[-103.15787,28.93865],[-104.37752,29.54255],[-104.39363,29.55396],[-104.3969,29.57105],[-104.5171,29.64671],[-104.77674,30.4236],[-106.00363,31.39181],[-106.09025,31.40569],[-106.20346,31.46305],[-106.23711,31.51262],[-106.24612,31.54193],[-106.28084,31.56173],[-106.30305,31.62154],[-106.33419,31.66303],[-106.34864,31.69663],[-106.3718,31.71165],[-106.38003,31.73151],[-106.41773,31.75196],[-106.43419,31.75478],[-106.45244,31.76523],[-106.46726,31.75998],[-106.47298,31.75054],[-106.48815,31.74769],[-106.50111,31.75714],[-106.50962,31.76155],[-106.51251,31.76922],[-106.52266,31.77509],[-106.529,31.784],[-108.20899,31.78534],[-108.20979,31.33316],[-109.05235,31.3333],[-111.07523,31.33232],[-112.34553,31.7357],[-114.82011,32.49609],[-114.79524,32.55731],[-114.81141,32.55543],[-114.80584,32.62028],[-114.76736,32.64094],[-114.71871,32.71894],[-115.88053,32.63624],[-117.1243,32.53427]]]]}},{type:"Feature",properties:{iso1A2:"MY",iso1A3:"MYS",iso1N3:"458",wikidata:"Q833",nameEn:"Malaysia",groups:["035","142"],driveSide:"left",callingCodes:["60"]},geometry:{type:"MultiPolygon",coordinates:[[[[114.08532,4.64632],[109.55486,8.10026],[104.81582,8.03101],[102.46318,7.22462],[102.09086,6.23546],[102.08127,6.22679],[102.07732,6.193],[102.09182,6.14161],[102.01835,6.05407],[101.99209,6.04075],[101.97114,6.01992],[101.9714,6.00575],[101.94712,5.98421],[101.92819,5.85511],[101.91776,5.84269],[101.89188,5.8386],[101.80144,5.74505],[101.75074,5.79091],[101.69773,5.75881],[101.58019,5.93534],[101.25524,5.78633],[101.25755,5.71065],[101.14062,5.61613],[100.98815,5.79464],[101.02708,5.91013],[101.087,5.9193],[101.12388,6.11411],[101.06165,6.14161],[101.12618,6.19431],[101.10313,6.25617],[100.85884,6.24929],[100.81045,6.45086],[100.74822,6.46231],[100.74361,6.50811],[100.66986,6.45086],[100.43027,6.52389],[100.42351,6.51762],[100.41791,6.5189],[100.41152,6.52299],[100.35413,6.54932],[100.31929,6.65413],[100.32607,6.65933],[100.32671,6.66526],[100.31884,6.66423],[100.31618,6.66781],[100.30828,6.66462],[100.29651,6.68439],[100.19511,6.72559],[100.12,6.42105],[100.0756,6.4045],[99.91873,6.50233],[99.50117,6.44501],[99.31854,5.99868],[99.75778,3.86466],[103.03657,1.30383],[103.56591,1.19719],[103.62738,1.35255],[103.67468,1.43166],[103.7219,1.46108],[103.74161,1.4502],[103.76395,1.45183],[103.81181,1.47953],[103.86383,1.46288],[103.89565,1.42841],[103.93384,1.42926],[104.00131,1.42405],[104.02277,1.4438],[104.04622,1.44691],[104.07348,1.43322],[104.08871,1.42015],[104.09162,1.39694],[104.08072,1.35998],[104.12282,1.27714],[104.34728,1.33529],[104.56723,1.44271],[105.01437,3.24936],[108.10426,5.42408],[109.71058,2.32059],[109.64506,2.08014],[109.62558,1.99182],[109.53794,1.91771],[109.57923,1.80624],[109.66397,1.79972],[109.66397,1.60425],[110.35354,0.98869],[110.49182,0.88088],[110.62374,0.873],[111.22979,1.08326],[111.55434,0.97864],[111.82846,0.99349],[111.94553,1.12016],[112.15679,1.17004],[112.2127,1.44135],[112.48648,1.56516],[113.021,1.57819],[113.01448,1.42832],[113.64677,1.23933],[114.03788,1.44787],[114.57892,1.5],[114.80706,1.92351],[114.80706,2.21665],[115.1721,2.49671],[115.11343,2.82879],[115.53713,3.14776],[115.58276,3.93499],[115.90217,4.37708],[117.25801,4.35108],[117.47313,4.18857],[117.67641,4.16535],[117.89538,4.16637],[118.07935,4.15511],[118.8663,4.44172],[118.75416,4.59798],[119.44841,5.09568],[119.34756,5.53889],[117.89159,6.25755],[117.43832,7.3895],[117.17735,7.52841],[116.79524,7.43869],[115.02521,5.35005],[115.16236,5.01011],[115.15092,4.87604],[115.20737,4.8256],[115.27819,4.63661],[115.2851,4.42295],[115.36346,4.33563],[115.31275,4.30806],[115.09978,4.39123],[115.07737,4.53418],[115.04064,4.63706],[115.02278,4.74137],[115.02955,4.82087],[115.05038,4.90275],[114.99417,4.88201],[114.96982,4.81146],[114.88841,4.81905],[114.8266,4.75062],[114.77303,4.72871],[114.83189,4.42387],[114.88039,4.4257],[114.78539,4.12205],[114.64211,4.00694],[114.49922,4.13108],[114.4416,4.27588],[114.32176,4.2552],[114.32176,4.34942],[114.26876,4.49878],[114.15813,4.57],[114.07448,4.58441],[114.08532,4.64632]]]]}},{type:"Feature",properties:{iso1A2:"MZ",iso1A3:"MOZ",iso1N3:"508",wikidata:"Q1029",nameEn:"Mozambique",groups:["014","202","002"],driveSide:"left",callingCodes:["258"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.74206,-10.25691],[40.44265,-10.4618],[40.00295,-10.80255],[39.58249,-10.96043],[39.24395,-11.17433],[38.88996,-11.16978],[38.47258,-11.4199],[38.21598,-11.27289],[37.93618,-11.26228],[37.8388,-11.3123],[37.76614,-11.53352],[37.3936,-11.68949],[36.80309,-11.56836],[36.62068,-11.72884],[36.19094,-11.70008],[36.19094,-11.57593],[35.82767,-11.41081],[35.63599,-11.55927],[34.96296,-11.57354],[34.64241,-11.57499],[34.57917,-11.87849],[34.82903,-12.04837],[34.70739,-12.15652],[34.46088,-12.0174],[34.37831,-12.17408],[34.60253,-13.48487],[34.86229,-13.48958],[35.47989,-14.15594],[35.5299,-14.27714],[35.86945,-14.67481],[35.87212,-14.89478],[35.91812,-14.89514],[35.78799,-15.17428],[35.85303,-15.41913],[35.80487,-16.03907],[35.70107,-16.10147],[35.52365,-16.15414],[35.43355,-16.11371],[35.30157,-16.2211],[35.25828,-16.4792],[35.14235,-16.56812],[35.27219,-16.69402],[35.30929,-16.82871],[35.27065,-16.93817],[35.3062,-17.1244],[35.0923,-17.13235],[35.04805,-17.00027],[35.17017,-16.93521],[35.13771,-16.81687],[35.04805,-16.83167],[34.40344,-16.20923],[34.43126,-16.04737],[34.25195,-15.90321],[34.44981,-15.60864],[34.43126,-15.44778],[34.57503,-15.30619],[34.61522,-14.99583],[34.567,-14.77345],[34.54503,-14.74672],[34.52057,-14.68263],[34.53516,-14.67782],[34.55112,-14.64494],[34.53962,-14.59776],[34.52366,-14.5667],[34.49636,-14.55091],[34.48932,-14.53646],[34.47628,-14.53363],[34.45053,-14.49873],[34.44641,-14.47746],[34.4192,-14.43191],[34.39277,-14.39467],[34.35843,-14.38652],[34.34453,-14.3985],[34.22355,-14.43607],[34.18733,-14.43823],[34.08588,-14.48893],[33.92898,-14.47929],[33.88503,-14.51652],[33.7247,-14.4989],[33.66677,-14.61306],[33.24249,-14.00019],[30.22098,-14.99447],[30.41902,-15.62269],[30.42568,-15.9962],[30.91597,-15.99924],[30.97761,-16.05848],[31.13171,-15.98019],[31.30563,-16.01193],[31.42451,-16.15154],[31.67988,-16.19595],[31.90223,-16.34388],[31.91324,-16.41569],[32.02772,-16.43892],[32.28529,-16.43892],[32.42838,-16.4727],[32.71017,-16.59932],[32.69917,-16.66893],[32.78943,-16.70267],[32.97655,-16.70689],[32.91051,-16.89446],[32.84113,-16.92259],[32.96554,-17.11971],[33.00517,-17.30477],[33.0426,-17.3468],[32.96554,-17.48964],[32.98536,-17.55891],[33.0492,-17.60298],[32.94133,-17.99705],[33.03159,-18.35054],[33.02278,-18.4696],[32.88629,-18.51344],[32.88629,-18.58023],[32.95013,-18.69079],[32.9017,-18.7992],[32.82465,-18.77419],[32.70137,-18.84712],[32.73439,-18.92628],[32.69917,-18.94293],[32.72118,-19.02204],[32.84006,-19.0262],[32.87088,-19.09279],[32.85107,-19.29238],[32.77966,-19.36098],[32.78282,-19.47513],[32.84446,-19.48343],[32.84666,-19.68462],[32.95013,-19.67219],[33.06461,-19.77787],[33.01178,-20.02007],[32.93032,-20.03868],[32.85987,-20.16686],[32.85987,-20.27841],[32.66174,-20.56106],[32.55167,-20.56312],[32.48122,-20.63319],[32.51644,-20.91929],[32.37115,-21.133],[32.48236,-21.32873],[32.41234,-21.31246],[31.38336,-22.36919],[31.30611,-22.422],[31.55779,-23.176],[31.56539,-23.47268],[31.67942,-23.60858],[31.70223,-23.72695],[31.77445,-23.90082],[31.87707,-23.95293],[31.90368,-24.18892],[31.9835,-24.29983],[32.03196,-25.10785],[32.01676,-25.38117],[31.97875,-25.46356],[32.00631,-25.65044],[31.92649,-25.84216],[31.974,-25.95387],[32.00916,-25.999],[32.08599,-26.00978],[32.10435,-26.15656],[32.07352,-26.40185],[32.13409,-26.5317],[32.13315,-26.84345],[32.19409,-26.84032],[32.22302,-26.84136],[32.29584,-26.852],[32.35222,-26.86027],[34.51034,-26.91792],[42.99868,-12.65261],[40.74206,-10.25691]]]]}},{type:"Feature",properties:{iso1A2:"NA",iso1A3:"NAM",iso1N3:"516",wikidata:"Q1030",nameEn:"Namibia",groups:["018","202","002"],driveSide:"left",callingCodes:["264"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.28743,-17.38814],[13.95896,-17.43141],[13.36212,-16.98048],[12.97145,-16.98567],[12.52111,-17.24495],[12.07076,-17.15165],[11.75063,-17.25013],[10.5065,-17.25284],[12.51595,-32.27486],[16.45332,-28.63117],[16.46592,-28.57126],[16.59922,-28.53246],[16.90446,-28.057],[17.15405,-28.08573],[17.4579,-28.68718],[18.99885,-28.89165],[19.99882,-28.42622],[19.99817,-24.76768],[19.99912,-21.99991],[20.99751,-22.00026],[20.99904,-18.31743],[21.45556,-18.31795],[23.0996,-18.00075],[23.29618,-17.99855],[23.61088,-18.4881],[24.19416,-18.01919],[24.40577,-17.95726],[24.57485,-18.07151],[24.6303,-17.9863],[24.71887,-17.9218],[24.73364,-17.89338],[24.95586,-17.79674],[25.05895,-17.84452],[25.16882,-17.78253],[25.26433,-17.79571],[25.00198,-17.58221],[24.70864,-17.49501],[24.5621,-17.52963],[24.38712,-17.46818],[24.32811,-17.49082],[24.23619,-17.47489],[23.47474,-17.62877],[21.42741,-18.02787],[21.14283,-17.94318],[18.84226,-17.80375],[18.39229,-17.38927],[14.28743,-17.38814]]]]}},{type:"Feature",properties:{iso1A2:"NC",iso1A3:"NCL",iso1N3:"540",wikidata:"Q33788",nameEn:"New Caledonia",country:"FR",groups:["054","009"],callingCodes:["687"]},geometry:{type:"MultiPolygon",coordinates:[[[[158.65519,-23.4036],[174.90025,-23.53966],[162.93363,-17.28904],[157.83842,-18.82563],[158.65519,-23.4036]]]]}},{type:"Feature",properties:{iso1A2:"NE",iso1A3:"NER",iso1N3:"562",wikidata:"Q1032",nameEn:"Niger",aliases:["RN"],groups:["011","202","002"],callingCodes:["227"]},geometry:{type:"MultiPolygon",coordinates:[[[[14.22918,22.61719],[13.5631,23.16574],[11.96886,23.51735],[7.48273,20.87258],[7.38361,20.79165],[5.8153,19.45101],[4.26651,19.14224],[4.26762,17.00432],[4.21787,17.00118],[4.19893,16.39923],[3.50368,15.35934],[3.03134,15.42221],[3.01806,15.34571],[1.31275,15.27978],[0.96711,14.98275],[0.72632,14.95898],[0.23859,15.00135],[0.16936,14.51654],[0.38051,14.05575],[0.61924,13.68491],[0.77377,13.6866],[0.77637,13.64442],[0.99514,13.5668],[1.02813,13.46635],[1.20088,13.38951],[1.24429,13.39373],[1.28509,13.35488],[1.24516,13.33968],[1.21217,13.37853],[1.18873,13.31771],[0.99253,13.37515],[0.99167,13.10727],[2.26349,12.41915],[2.05785,12.35539],[2.39723,11.89473],[2.45824,11.98672],[2.39657,12.10952],[2.37783,12.24804],[2.6593,12.30631],[2.83978,12.40585],[3.25352,12.01467],[3.31613,11.88495],[3.48187,11.86092],[3.59375,11.70269],[3.61075,11.69181],[3.67988,11.75429],[3.67122,11.80865],[3.63063,11.83042],[3.61955,11.91847],[3.67775,11.97599],[3.63136,12.11826],[3.66364,12.25884],[3.65111,12.52223],[3.94339,12.74979],[4.10006,12.98862],[4.14367,13.17189],[4.14186,13.47586],[4.23456,13.47725],[4.4668,13.68286],[4.87425,13.78],[4.9368,13.7345],[5.07396,13.75052],[5.21026,13.73627],[5.27797,13.75474],[5.35437,13.83567],[5.52957,13.8845],[6.15771,13.64564],[6.27411,13.67835],[6.43053,13.6006],[6.69617,13.34057],[6.94445,12.99825],[7.0521,13.00076],[7.12676,13.02445],[7.22399,13.1293],[7.39241,13.09717],[7.81085,13.34902],[8.07997,13.30847],[8.25185,13.20369],[8.41853,13.06166],[8.49493,13.07519],[8.60431,13.01768],[8.64251,12.93985],[8.97413,12.83661],[9.65995,12.80614],[10.00373,13.18171],[10.19993,13.27129],[10.46731,13.28819],[10.66004,13.36422],[11.4535,13.37773],[11.88236,13.2527],[12.04209,13.14452],[12.16189,13.10056],[12.19315,13.12423],[12.47095,13.06673],[12.58033,13.27805],[12.6793,13.29157],[12.87376,13.48919],[13.05085,13.53984],[13.19844,13.52802],[13.33213,13.71195],[13.6302,13.71094],[13.47559,14.40881],[13.48259,14.46704],[13.68573,14.55276],[13.67878,14.64013],[13.809,14.72915],[13.78991,14.87519],[13.86301,15.04043],[14.37425,15.72591],[15.50373,16.89649],[15.6032,18.77402],[15.75098,19.93002],[15.99632,20.35364],[15.6721,20.70069],[15.59841,20.74039],[15.56004,20.79488],[15.55382,20.86507],[15.57248,20.92138],[15.62515,20.95395],[15.28332,21.44557],[15.20213,21.49365],[15.19692,21.99339],[14.99751,23.00539],[14.22918,22.61719]]]]}},{type:"Feature",properties:{iso1A2:"NF",iso1A3:"NFK",iso1N3:"574",wikidata:"Q31057",nameEn:"Norfolk Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["672 3"]},geometry:{type:"MultiPolygon",coordinates:[[[[169.82316,-28.16667],[166.29505,-28.29175],[167.94076,-30.60745],[169.82316,-28.16667]]]]}},{type:"Feature",properties:{iso1A2:"NG",iso1A3:"NGA",iso1N3:"566",wikidata:"Q1033",nameEn:"Nigeria",groups:["011","202","002"],callingCodes:["234"]},geometry:{type:"MultiPolygon",coordinates:[[[[6.15771,13.64564],[5.52957,13.8845],[5.35437,13.83567],[5.27797,13.75474],[5.21026,13.73627],[5.07396,13.75052],[4.9368,13.7345],[4.87425,13.78],[4.4668,13.68286],[4.23456,13.47725],[4.14186,13.47586],[4.14367,13.17189],[4.10006,12.98862],[3.94339,12.74979],[3.65111,12.52223],[3.66364,12.25884],[3.63136,12.11826],[3.67775,11.97599],[3.61955,11.91847],[3.63063,11.83042],[3.67122,11.80865],[3.67988,11.75429],[3.61075,11.69181],[3.59375,11.70269],[3.49175,11.29765],[3.71505,11.13015],[3.84243,10.59316],[3.78292,10.40538],[3.6844,10.46351],[3.57275,10.27185],[3.66908,10.18136],[3.54429,9.87739],[3.35383,9.83641],[3.32099,9.78032],[3.34726,9.70696],[3.25093,9.61632],[3.13928,9.47167],[3.14147,9.28375],[3.08017,9.10006],[2.77907,9.06924],[2.67523,7.87825],[2.73095,7.7755],[2.73405,7.5423],[2.78668,7.5116],[2.79442,7.43486],[2.74489,7.42565],[2.76965,7.13543],[2.71702,6.95722],[2.74024,6.92802],[2.73405,6.78508],[2.78823,6.76356],[2.78204,6.70514],[2.7325,6.64057],[2.74334,6.57291],[2.70464,6.50831],[2.70566,6.38038],[2.74181,6.13349],[5.87055,3.78489],[8.34397,4.30689],[8.60302,4.87353],[8.78027,5.1243],[8.92029,5.58403],[8.83687,5.68483],[8.88156,5.78857],[8.84209,5.82562],[9.51757,6.43874],[9.70674,6.51717],[9.77824,6.79088],[9.86314,6.77756],[10.15135,7.03781],[10.21466,6.88996],[10.53639,6.93432],[10.57214,7.16345],[10.59746,7.14719],[10.60789,7.06885],[10.83727,6.9358],[10.8179,6.83377],[10.94302,6.69325],[11.09644,6.68437],[11.09495,6.51717],[11.42041,6.53789],[11.42264,6.5882],[11.51499,6.60892],[11.57755,6.74059],[11.55818,6.86186],[11.63117,6.9905],[11.87396,7.09398],[11.84864,7.26098],[11.93205,7.47812],[12.01844,7.52981],[11.99908,7.67302],[12.20909,7.97553],[12.19271,8.10826],[12.24782,8.17904],[12.26123,8.43696],[12.4489,8.52536],[12.44146,8.6152],[12.68722,8.65938],[12.71701,8.7595],[12.79,8.75361],[12.81085,8.91992],[12.90022,9.11411],[12.91958,9.33905],[12.85628,9.36698],[13.02385,9.49334],[13.22642,9.57266],[13.25472,9.76795],[13.29941,9.8296],[13.25025,9.86042],[13.24132,9.91031],[13.27409,9.93232],[13.286,9.9822],[13.25323,10.00127],[13.25025,10.03647],[13.34111,10.12299],[13.43644,10.13326],[13.5705,10.53183],[13.54964,10.61236],[13.73434,10.9255],[13.70753,10.94451],[13.7403,11.00593],[13.78945,11.00154],[13.97489,11.30258],[14.17821,11.23831],[14.6124,11.51283],[14.64591,11.66166],[14.55207,11.72001],[14.61612,11.7798],[14.6474,12.17466],[14.4843,12.35223],[14.22215,12.36533],[14.17523,12.41916],[14.20204,12.53405],[14.08251,13.0797],[13.6302,13.71094],[13.33213,13.71195],[13.19844,13.52802],[13.05085,13.53984],[12.87376,13.48919],[12.6793,13.29157],[12.58033,13.27805],[12.47095,13.06673],[12.19315,13.12423],[12.16189,13.10056],[12.04209,13.14452],[11.88236,13.2527],[11.4535,13.37773],[10.66004,13.36422],[10.46731,13.28819],[10.19993,13.27129],[10.00373,13.18171],[9.65995,12.80614],[8.97413,12.83661],[8.64251,12.93985],[8.60431,13.01768],[8.49493,13.07519],[8.41853,13.06166],[8.25185,13.20369],[8.07997,13.30847],[7.81085,13.34902],[7.39241,13.09717],[7.22399,13.1293],[7.12676,13.02445],[7.0521,13.00076],[6.94445,12.99825],[6.69617,13.34057],[6.43053,13.6006],[6.27411,13.67835],[6.15771,13.64564]]]]}},{type:"Feature",properties:{iso1A2:"NI",iso1A3:"NIC",iso1N3:"558",wikidata:"Q811",nameEn:"Nicaragua",groups:["013","003","419","019"],callingCodes:["505"]},geometry:{type:"MultiPolygon",coordinates:[[[[-83.13724,15.00002],[-83.49268,15.01158],[-83.62101,14.89448],[-83.89551,14.76697],[-84.10584,14.76353],[-84.48373,14.63249],[-84.70119,14.68078],[-84.82596,14.82212],[-84.90082,14.80489],[-85.1575,14.53934],[-85.18602,14.24929],[-85.32149,14.2562],[-85.45762,14.11304],[-85.73964,13.9698],[-85.75477,13.8499],[-86.03458,13.99181],[-86.00685,14.08474],[-86.14801,14.04317],[-86.35219,13.77157],[-86.76812,13.79605],[-86.71267,13.30348],[-86.87066,13.30641],[-86.93383,13.18677],[-86.93197,13.05313],[-87.03785,12.98682],[-87.06306,13.00892],[-87.37107,12.98646],[-87.55124,13.12523],[-87.7346,13.13228],[-88.11443,12.63306],[-86.14524,11.09059],[-85.71223,11.06868],[-85.60529,11.22607],[-84.92439,10.9497],[-84.68197,11.07568],[-83.90838,10.71161],[-83.66597,10.79916],[-83.68276,11.01562],[-82.56142,11.91792],[-82.06974,14.49418],[-83.04763,15.03256],[-83.13724,15.00002]]]]}},{type:"Feature",properties:{iso1A2:"NL",iso1A3:"NLD",iso1N3:"528",wikidata:"Q55",nameEn:"Netherlands",groups:["EU","155","150"],callingCodes:["31"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.45168,54.20039],[2.56575,51.85301],[3.36263,51.37112],[3.38696,51.33436],[3.35847,51.31572],[3.38289,51.27331],[3.41704,51.25933],[3.43488,51.24135],[3.52698,51.2458],[3.51502,51.28697],[3.58939,51.30064],[3.78999,51.25766],[3.78783,51.2151],[3.90125,51.20371],[3.97889,51.22537],[4.01957,51.24504],[4.05165,51.24171],[4.16721,51.29348],[4.24024,51.35371],[4.21923,51.37443],[4.33265,51.37687],[4.34086,51.35738],[4.39292,51.35547],[4.43777,51.36989],[4.38064,51.41965],[4.39747,51.43316],[4.38122,51.44905],[4.47736,51.4778],[4.5388,51.48184],[4.54675,51.47265],[4.52846,51.45002],[4.53521,51.4243],[4.57489,51.4324],[4.65442,51.42352],[4.72935,51.48424],[4.74578,51.48937],[4.77321,51.50529],[4.78803,51.50284],[4.84139,51.4799],[4.82409,51.44736],[4.82946,51.4213],[4.78314,51.43319],[4.76577,51.43046],[4.77229,51.41337],[4.78941,51.41102],[4.84988,51.41502],[4.90016,51.41404],[4.92152,51.39487],[5.00393,51.44406],[5.0106,51.47167],[5.03281,51.48679],[5.04774,51.47022],[5.07891,51.4715],[5.10456,51.43163],[5.07102,51.39469],[5.13105,51.34791],[5.13377,51.31592],[5.16222,51.31035],[5.2002,51.32243],[5.24244,51.30495],[5.22542,51.26888],[5.23814,51.26064],[5.26461,51.26693],[5.29716,51.26104],[5.33886,51.26314],[5.347,51.27502],[5.41672,51.26248],[5.4407,51.28169],[5.46519,51.2849],[5.48476,51.30053],[5.515,51.29462],[5.5569,51.26544],[5.5603,51.22249],[5.65145,51.19788],[5.65528,51.18736],[5.70344,51.1829],[5.74617,51.18928],[5.77735,51.17845],[5.77697,51.1522],[5.82564,51.16753],[5.85508,51.14445],[5.80798,51.11661],[5.8109,51.10861],[5.83226,51.10585],[5.82921,51.09328],[5.79903,51.09371],[5.79835,51.05834],[5.77258,51.06196],[5.75961,51.03113],[5.77688,51.02483],[5.76242,50.99703],[5.71864,50.96092],[5.72875,50.95428],[5.74752,50.96202],[5.75927,50.95601],[5.74644,50.94723],[5.72545,50.92312],[5.72644,50.91167],[5.71626,50.90796],[5.69858,50.91046],[5.67886,50.88142],[5.64504,50.87107],[5.64009,50.84742],[5.65259,50.82309],[5.70118,50.80764],[5.68995,50.79641],[5.70107,50.7827],[5.68091,50.75804],[5.69469,50.75529],[5.72216,50.76398],[5.73904,50.75674],[5.74356,50.7691],[5.76533,50.78159],[5.77513,50.78308],[5.80673,50.7558],[5.84548,50.76542],[5.84888,50.75448],[5.88734,50.77092],[5.89129,50.75125],[5.89132,50.75124],[5.95942,50.7622],[5.97545,50.75441],[6.01976,50.75398],[6.02624,50.77453],[5.97497,50.79992],[5.98404,50.80988],[6.00462,50.80065],[6.02328,50.81694],[6.01921,50.84435],[6.05623,50.8572],[6.05702,50.85179],[6.07431,50.84674],[6.07693,50.86025],[6.08805,50.87223],[6.07486,50.89307],[6.09297,50.92066],[6.01615,50.93367],[6.02697,50.98303],[5.95282,50.98728],[5.90296,50.97356],[5.90493,51.00198],[5.87849,51.01969],[5.86735,51.05182],[5.9134,51.06736],[5.9541,51.03496],[5.98292,51.07469],[6.16706,51.15677],[6.17384,51.19589],[6.07889,51.17038],[6.07889,51.24432],[6.16977,51.33169],[6.22674,51.36135],[6.22641,51.39948],[6.20654,51.40049],[6.21724,51.48568],[6.18017,51.54096],[6.09055,51.60564],[6.11759,51.65609],[6.02767,51.6742],[6.04091,51.71821],[5.95003,51.7493],[5.98665,51.76944],[5.94568,51.82786],[5.99848,51.83195],[6.06705,51.86136],[6.10337,51.84829],[6.16902,51.84094],[6.11551,51.89769],[6.15349,51.90439],[6.21443,51.86801],[6.29872,51.86801],[6.30593,51.84998],[6.40704,51.82771],[6.38815,51.87257],[6.47179,51.85395],[6.50231,51.86313],[6.58556,51.89386],[6.68386,51.91861],[6.72319,51.89518],[6.82357,51.96711],[6.83035,51.9905],[6.68128,52.05052],[6.76117,52.11895],[6.83984,52.11728],[6.97189,52.20329],[6.9897,52.2271],[7.03729,52.22695],[7.06365,52.23789],[7.02703,52.27941],[7.07044,52.37805],[7.03417,52.40237],[6.99041,52.47235],[6.94293,52.43597],[6.69507,52.488],[6.71641,52.62905],[6.77307,52.65375],[7.04557,52.63318],[7.07253,52.81083],[7.21694,53.00742],[7.17898,53.13817],[7.22681,53.18165],[7.21679,53.20058],[7.19052,53.31866],[7.00198,53.32672],[6.91025,53.44221],[5.45168,54.20039]],[[4.93295,51.44945],[4.95244,51.45207],[4.9524,51.45014],[4.93909,51.44632],[4.93295,51.44945]],[[4.91493,51.4353],[4.91935,51.43634],[4.92227,51.44252],[4.91811,51.44621],[4.92287,51.44741],[4.92811,51.4437],[4.92566,51.44273],[4.92815,51.43856],[4.92879,51.44161],[4.93544,51.44634],[4.94025,51.44193],[4.93416,51.44185],[4.93471,51.43861],[4.94265,51.44003],[4.93986,51.43064],[4.92952,51.42984],[4.92652,51.43329],[4.91493,51.4353]]]]}},{type:"Feature",properties:{iso1A2:"NO",iso1A3:"NOR",iso1N3:"578",wikidata:"Q20",nameEn:"Norway",groups:["154","150"],callingCodes:["47"]},geometry:{type:"MultiPolygon",coordinates:[[[[10.40861,58.38489],[10.64958,58.89391],[11.08911,58.98745],[11.15367,59.07862],[11.34459,59.11672],[11.4601,58.99022],[11.45199,58.89604],[11.65732,58.90177],[11.8213,59.24985],[11.69297,59.59442],[11.92112,59.69531],[11.87121,59.86039],[12.15641,59.8926],[12.36317,59.99259],[12.52003,60.13846],[12.59133,60.50559],[12.2277,61.02442],[12.69115,61.06584],[12.86939,61.35427],[12.57707,61.56547],[12.40595,61.57226],[12.14746,61.7147],[12.29187,62.25699],[12.07085,62.6297],[12.19919,63.00104],[11.98529,63.27487],[12.19919,63.47935],[12.14928,63.59373],[12.74105,64.02171],[13.23411,64.09087],[13.98222,64.00953],[14.16051,64.18725],[14.11117,64.46674],[13.64276,64.58402],[14.50926,65.31786],[14.53778,66.12399],[15.05113,66.15572],[15.49318,66.28509],[15.37197,66.48217],[16.35589,67.06419],[16.39154,67.21653],[16.09922,67.4364],[16.12774,67.52106],[16.38441,67.52923],[16.7409,67.91037],[17.30416,68.11591],[17.90787,67.96537],[18.13836,68.20874],[18.1241,68.53721],[18.39503,68.58672],[18.63032,68.50849],[18.97255,68.52416],[19.93508,68.35911],[20.22027,68.48759],[19.95647,68.55546],[20.22027,68.67246],[20.33435,68.80174],[20.28444,68.93283],[20.0695,69.04469],[20.55258,69.06069],[20.72171,69.11874],[21.05775,69.0356],[21.11099,69.10291],[20.98641,69.18809],[21.00732,69.22755],[21.27827,69.31281],[21.63833,69.27485],[22.27276,68.89514],[22.38367,68.71561],[22.53321,68.74393],[23.13064,68.64684],[23.68017,68.70276],[23.781,68.84514],[24.02299,68.81601],[24.18432,68.73936],[24.74898,68.65143],[24.90023,68.55579],[24.93048,68.61102],[25.10189,68.63307],[25.12206,68.78684],[25.42455,68.90328],[25.61613,68.89602],[25.75729,68.99383],[25.69679,69.27039],[25.96904,69.68397],[26.40261,69.91377],[26.64461,69.96565],[27.05802,69.92069],[27.57226,70.06215],[27.95542,70.0965],[27.97558,69.99671],[28.32849,69.88605],[28.36883,69.81658],[29.12697,69.69193],[29.31664,69.47994],[28.8629,69.22395],[28.81248,69.11997],[28.91738,69.04774],[29.0444,69.0119],[29.26623,69.13794],[29.27631,69.2811],[29.97205,69.41623],[30.16363,69.65244],[30.52662,69.54699],[30.95011,69.54699],[30.84095,69.80584],[31.59909,70.16571],[32.07813,72.01005],[18.46509,71.28681],[-0.3751,61.32236],[7.28637,57.35913],[10.40861,58.38489]]]]}},{type:"Feature",properties:{iso1A2:"NP",iso1A3:"NPL",iso1N3:"524",wikidata:"Q837",nameEn:"Nepal",groups:["034","142"],driveSide:"left",callingCodes:["977"]},geometry:{type:"MultiPolygon",coordinates:[[[[88.13378,27.88015],[87.82681,27.95248],[87.72718,27.80938],[87.56996,27.84517],[87.11696,27.84104],[87.03757,27.94835],[86.75582,28.04182],[86.74181,28.10638],[86.56265,28.09569],[86.51609,27.96623],[86.42736,27.91122],[86.22966,27.9786],[86.18607,28.17364],[86.088,28.09264],[86.08333,28.02121],[86.12069,27.93047],[86.06309,27.90021],[85.94946,27.9401],[85.97813,27.99023],[85.90743,28.05144],[85.84672,28.18187],[85.74864,28.23126],[85.71907,28.38064],[85.69105,28.38475],[85.60854,28.25045],[85.59765,28.30529],[85.4233,28.32996],[85.38127,28.28336],[85.10729,28.34092],[85.18668,28.54076],[85.19135,28.62825],[85.06059,28.68562],[84.85511,28.58041],[84.62317,28.73887],[84.47528,28.74023],[84.2231,28.89571],[84.24801,29.02783],[84.18107,29.23451],[83.97559,29.33091],[83.82303,29.30513],[83.63156,29.16249],[83.44787,29.30513],[83.28131,29.56813],[83.07116,29.61957],[82.73024,29.81695],[82.5341,29.9735],[82.38622,30.02608],[82.16984,30.0692],[82.19475,30.16884],[82.10757,30.23745],[82.10135,30.35439],[81.99082,30.33423],[81.62033,30.44703],[81.41018,30.42153],[81.39928,30.21862],[81.33355,30.15303],[81.2623,30.14596],[81.29032,30.08806],[81.24362,30.0126],[81.12842,30.01395],[81.03953,30.20059],[80.93695,30.18229],[80.8778,30.13384],[80.67076,29.95732],[80.60226,29.95732],[80.56957,29.88176],[80.56247,29.86661],[80.48997,29.79566],[80.43458,29.80466],[80.41554,29.79451],[80.36803,29.73865],[80.38428,29.68513],[80.41858,29.63581],[80.37939,29.57098],[80.24322,29.44299],[80.31428,29.30784],[80.28626,29.20327],[80.24112,29.21414],[80.26602,29.13938],[80.23178,29.11626],[80.18085,29.13649],[80.05743,28.91479],[80.06957,28.82763],[80.12125,28.82346],[80.37188,28.63371],[80.44504,28.63098],[80.52443,28.54897],[80.50575,28.6706],[80.55142,28.69182],[80.89648,28.47237],[81.08507,28.38346],[81.19847,28.36284],[81.32923,28.13521],[81.38683,28.17638],[81.48179,28.12148],[81.47867,28.08303],[81.91223,27.84995],[81.97214,27.93322],[82.06554,27.92222],[82.46405,27.6716],[82.70378,27.72122],[82.74119,27.49838],[82.93261,27.50328],[82.94938,27.46036],[83.19413,27.45632],[83.27197,27.38309],[83.2673,27.36235],[83.29999,27.32778],[83.35136,27.33885],[83.38872,27.39276],[83.39495,27.4798],[83.61288,27.47013],[83.85595,27.35797],[83.86182,27.4241],[83.93306,27.44939],[84.02229,27.43836],[84.10791,27.52399],[84.21376,27.45218],[84.25735,27.44941],[84.29315,27.39],[84.62161,27.33885],[84.69166,27.21294],[84.64496,27.04669],[84.793,26.9968],[84.82913,27.01989],[84.85754,26.98984],[84.96687,26.95599],[84.97186,26.9149],[85.00536,26.89523],[85.05592,26.88991],[85.02635,26.85381],[85.15883,26.86966],[85.19291,26.86909],[85.18046,26.80519],[85.21159,26.75933],[85.34302,26.74954],[85.47752,26.79292],[85.56471,26.84133],[85.5757,26.85955],[85.59461,26.85161],[85.61621,26.86721],[85.66239,26.84822],[85.73483,26.79613],[85.72315,26.67471],[85.76907,26.63076],[85.83126,26.61134],[85.85126,26.60866],[85.8492,26.56667],[86.02729,26.66756],[86.13596,26.60651],[86.22513,26.58863],[86.26235,26.61886],[86.31564,26.61925],[86.49726,26.54218],[86.54258,26.53819],[86.57073,26.49825],[86.61313,26.48658],[86.62686,26.46891],[86.69124,26.45169],[86.74025,26.42386],[86.76797,26.45892],[86.82898,26.43919],[86.94543,26.52076],[86.95912,26.52076],[87.01559,26.53228],[87.04691,26.58685],[87.0707,26.58571],[87.09147,26.45039],[87.14751,26.40542],[87.18863,26.40558],[87.24682,26.4143],[87.26587,26.40592],[87.26568,26.37294],[87.34568,26.34787],[87.37314,26.40815],[87.46566,26.44058],[87.51571,26.43106],[87.55274,26.40596],[87.59175,26.38342],[87.66803,26.40294],[87.67893,26.43501],[87.76004,26.40711],[87.7918,26.46737],[87.84193,26.43663],[87.89085,26.48565],[87.90115,26.44923],[88.00895,26.36029],[88.09414,26.43732],[88.09963,26.54195],[88.16452,26.64111],[88.1659,26.68177],[88.19107,26.75516],[88.12302,26.95324],[88.13422,26.98705],[88.11719,26.98758],[87.9887,27.11045],[88.01587,27.21388],[88.01646,27.21612],[88.07277,27.43007],[88.04008,27.49223],[88.19107,27.79285],[88.1973,27.85067],[88.13378,27.88015]]]]}},{type:"Feature",properties:{iso1A2:"NR",iso1A3:"NRU",iso1N3:"520",wikidata:"Q697",nameEn:"Nauru",groups:["057","009"],driveSide:"left",callingCodes:["674"]},geometry:{type:"MultiPolygon",coordinates:[[[[166.95155,0.14829],[166.21778,-0.7977],[167.60042,-0.88259],[166.95155,0.14829]]]]}},{type:"Feature",properties:{iso1A2:"NU",iso1A3:"NIU",iso1N3:"570",wikidata:"Q34020",nameEn:"Niue",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["683"]},geometry:{type:"MultiPolygon",coordinates:[[[[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228]]]]}},{type:"Feature",properties:{iso1A2:"NZ",iso1A3:"NZL",iso1N3:"554",wikidata:"Q664",nameEn:"New Zealand",groups:["053","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-180,-24.21376],[-179.93224,-45.18423],[-155.99562,-45.16785],[-180,-24.21376]]],[[[161.96603,-56.07661],[179.49541,-50.04657],[179.49541,-36.79303],[169.6687,-29.09191],[161.96603,-56.07661]]]]}},{type:"Feature",properties:{iso1A2:"OM",iso1A3:"OMN",iso1N3:"512",wikidata:"Q842",nameEn:"Oman",groups:["145","142"],callingCodes:["968"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.82555,25.7713],[56.79239,26.41236],[56.68954,26.76645],[56.2644,26.58649],[55.81777,26.18798],[56.08666,26.05038],[56.15498,26.06828],[56.19334,25.9795],[56.13963,25.82765],[56.17416,25.77239],[56.13579,25.73524],[56.14826,25.66351],[56.18363,25.65508],[56.20473,25.61119],[56.25365,25.60211],[56.26636,25.60643],[56.25341,25.61443],[56.26534,25.62825],[56.82555,25.7713]]],[[[56.26062,25.33108],[56.23362,25.31253],[56.25008,25.28843],[56.24465,25.27505],[56.20838,25.25668],[56.20872,25.24104],[56.24341,25.22867],[56.27628,25.23404],[56.34438,25.26653],[56.35172,25.30681],[56.3111,25.30107],[56.3005,25.31815],[56.26062,25.33108]],[[56.28423,25.26344],[56.27086,25.26128],[56.2716,25.27916],[56.28102,25.28486],[56.29379,25.2754],[56.28423,25.26344]]],[[[61.45114,22.55394],[56.86325,25.03856],[56.3227,24.97284],[56.34873,24.93205],[56.30269,24.88334],[56.20568,24.85063],[56.20062,24.78565],[56.13684,24.73699],[56.06128,24.74457],[56.03535,24.81161],[55.97836,24.87673],[55.97467,24.89639],[56.05106,24.87461],[56.05715,24.95727],[55.96316,25.00857],[55.90849,24.96771],[55.85094,24.96858],[55.81116,24.9116],[55.81348,24.80102],[55.83408,24.77858],[55.83271,24.68567],[55.76461,24.5287],[55.83271,24.41521],[55.83395,24.32776],[55.80747,24.31069],[55.79145,24.27914],[55.76781,24.26209],[55.75939,24.26114],[55.75382,24.2466],[55.75257,24.23466],[55.76558,24.23227],[55.77658,24.23476],[55.83367,24.20193],[55.95472,24.2172],[56.01799,24.07426],[55.8308,24.01633],[55.73301,24.05994],[55.48677,23.94946],[55.57358,23.669],[55.22634,23.10378],[55.2137,22.71065],[55.66469,21.99658],[54.99756,20.00083],[52.00311,19.00083],[52.78009,17.35124],[52.74267,17.29519],[52.81185,17.28568],[53.09917,16.67084],[53.32998,16.16312],[56.66759,17.24021],[61.45114,22.55394]]]]}},{type:"Feature",properties:{iso1A2:"PA",iso1A3:"PAN",iso1N3:"591",wikidata:"Q804",nameEn:"Panama",groups:["013","003","419","019"],callingCodes:["507"]},geometry:{type:"MultiPolygon",coordinates:[[[[-77.32389,8.81247],[-77.58292,9.22278],[-78.79327,9.93766],[-82.51044,9.65379],[-82.56507,9.57279],[-82.61345,9.49881],[-82.66667,9.49746],[-82.77206,9.59573],[-82.87919,9.62645],[-82.84871,9.4973],[-82.93516,9.46741],[-82.93516,9.07687],[-82.72126,8.97125],[-82.88253,8.83331],[-82.91377,8.774],[-82.92068,8.74832],[-82.8794,8.6981],[-82.82739,8.60153],[-82.83975,8.54755],[-82.83322,8.52464],[-82.8382,8.48117],[-82.8679,8.44042],[-82.93056,8.43465],[-83.05209,8.33394],[-82.9388,8.26634],[-82.88641,8.10219],[-82.89137,8.05755],[-82.89978,8.04083],[-82.94503,7.93865],[-82.13751,6.97312],[-78.06168,7.07793],[-77.89178,7.22681],[-77.81426,7.48319],[-77.72157,7.47612],[-77.72514,7.72348],[-77.57185,7.51147],[-77.17257,7.97422],[-77.45064,8.49991],[-77.32389,8.81247]]]]}},{type:"Feature",properties:{iso1A2:"PE",iso1A3:"PER",iso1N3:"604",wikidata:"Q419",nameEn:"Peru",groups:["005","419","019"],callingCodes:["51"]},geometry:{type:"MultiPolygon",coordinates:[[[[-74.26675,-0.97229],[-74.42701,-0.50218],[-75.18513,-0.0308],[-75.25764,-0.11943],[-75.40192,-0.17196],[-75.61997,-0.10012],[-75.60169,-0.18708],[-75.53615,-0.19213],[-75.22862,-0.60048],[-75.22862,-0.95588],[-75.3872,-0.9374],[-75.57429,-1.55961],[-76.05203,-2.12179],[-76.6324,-2.58397],[-77.94147,-3.05454],[-78.19369,-3.36431],[-78.14324,-3.47653],[-78.22642,-3.51113],[-78.24589,-3.39907],[-78.34362,-3.38633],[-78.68394,-4.60754],[-78.85149,-4.66795],[-79.01659,-5.01481],[-79.1162,-4.97774],[-79.26248,-4.95167],[-79.59402,-4.46848],[-79.79722,-4.47558],[-80.13945,-4.29786],[-80.39256,-4.48269],[-80.46386,-4.41516],[-80.32114,-4.21323],[-80.45023,-4.20938],[-80.4822,-4.05477],[-80.46386,-4.01342],[-80.13232,-3.90317],[-80.19926,-3.68894],[-80.18741,-3.63994],[-80.19848,-3.59249],[-80.21642,-3.5888],[-80.20535,-3.51667],[-80.22629,-3.501],[-80.23651,-3.48652],[-80.24586,-3.48677],[-80.24475,-3.47846],[-80.24123,-3.46124],[-80.20647,-3.431],[-80.30602,-3.39149],[-84.52388,-3.36941],[-85.71054,-21.15413],[-70.59118,-18.35072],[-70.378,-18.3495],[-70.31267,-18.31258],[-70.16394,-18.31737],[-69.96732,-18.25992],[-69.81607,-18.12582],[-69.75305,-17.94605],[-69.82868,-17.72048],[-69.79087,-17.65563],[-69.66483,-17.65083],[-69.46897,-17.4988],[-69.46863,-17.37466],[-69.62883,-17.28142],[-69.16896,-16.72233],[-69.00853,-16.66769],[-69.04027,-16.57214],[-68.98358,-16.42165],[-68.79464,-16.33272],[-68.96238,-16.194],[-69.09986,-16.22693],[-69.20291,-16.16668],[-69.40336,-15.61358],[-69.14856,-15.23478],[-69.36254,-14.94634],[-68.88135,-14.18639],[-69.05265,-13.68546],[-68.8864,-13.40792],[-68.85615,-12.87769],[-68.65044,-12.50689],[-68.98115,-11.8979],[-69.57156,-10.94555],[-69.57835,-10.94051],[-69.90896,-10.92744],[-70.38791,-11.07096],[-70.51395,-10.92249],[-70.64134,-11.0108],[-70.62487,-9.80666],[-70.55429,-9.76692],[-70.58453,-9.58303],[-70.53373,-9.42628],[-71.23394,-9.9668],[-72.14742,-9.98049],[-72.31883,-9.5184],[-72.72216,-9.41397],[-73.21498,-9.40904],[-72.92886,-9.04074],[-73.76576,-7.89884],[-73.65485,-7.77897],[-73.96938,-7.58465],[-73.77011,-7.28944],[-73.73986,-6.87919],[-73.12983,-6.43852],[-73.24579,-6.05764],[-72.83973,-5.14765],[-72.64391,-5.0391],[-71.87003,-4.51661],[-70.96814,-4.36915],[-70.77601,-4.15717],[-70.33236,-4.15214],[-70.19582,-4.3607],[-70.11305,-4.27281],[-70.00888,-4.37833],[-69.94708,-4.2431],[-70.3374,-3.79505],[-70.52393,-3.87553],[-70.71396,-3.7921],[-70.04609,-2.73906],[-70.94377,-2.23142],[-71.75223,-2.15058],[-72.92587,-2.44514],[-73.65312,-1.26222],[-74.26675,-0.97229]]]]}},{type:"Feature",properties:{iso1A2:"PF",iso1A3:"PYF",iso1N3:"258",wikidata:"Q30971",nameEn:"French Polynesia",country:"FR",groups:["061","009"],callingCodes:["689"]},geometry:{type:"MultiPolygon",coordinates:[[[[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261]]]]}},{type:"Feature",properties:{iso1A2:"PG",iso1A3:"PNG",iso1N3:"598",wikidata:"Q691",nameEn:"Papua New Guinea",groups:["054","009"],driveSide:"left",callingCodes:["675"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.03157,2.12829],[140.99813,-6.3233],[140.85295,-6.72996],[141.01763,-6.90181],[141.00782,-9.1242],[140.88922,-9.34945],[142.0601,-9.56571],[142.0953,-9.23534],[142.1462,-9.19923],[142.23304,-9.19253],[142.31447,-9.24611],[142.5723,-9.35994],[142.81927,-9.31709],[144.30183,-9.48146],[155.22803,-12.9001],[154.74815,-7.33315],[155.60735,-6.92266],[155.69784,-6.92661],[155.92557,-6.84664],[156.03993,-6.65703],[156.03296,-6.55528],[160.43769,-4.17974],[141.03157,2.12829]]]]}},{type:"Feature",properties:{iso1A2:"PH",iso1A3:"PHL",iso1N3:"608",wikidata:"Q928",nameEn:"Philippines",aliases:["PI","RP"],groups:["035","142"],callingCodes:["63"]},geometry:{type:"MultiPolygon",coordinates:[[[[129.19694,7.84182],[121.8109,21.77688],[120.69238,21.52331],[118.82252,14.67191],[115.39742,10.92666],[116.79524,7.43869],[117.17735,7.52841],[117.43832,7.3895],[117.89159,6.25755],[119.34756,5.53889],[119.44841,5.09568],[118.75416,4.59798],[118.8663,4.44172],[118.07935,4.15511],[118.41402,3.99509],[124.97752,4.82064],[129.19694,7.84182]]]]}},{type:"Feature",properties:{iso1A2:"PK",iso1A3:"PAK",iso1N3:"586",wikidata:"Q843",nameEn:"Pakistan",groups:["034","142"],driveSide:"left",callingCodes:["92"]},geometry:{type:"MultiPolygon",coordinates:[[[[75.72737,36.7529],[75.45562,36.71971],[75.40481,36.95382],[75.13839,37.02622],[74.56453,37.03023],[74.53739,36.96224],[74.43389,37.00977],[74.04856,36.82648],[73.82685,36.91421],[72.6323,36.84601],[72.18135,36.71838],[71.80267,36.49924],[71.60491,36.39429],[71.19505,36.04134],[71.37969,35.95865],[71.55273,35.71483],[71.49917,35.6267],[71.65435,35.4479],[71.54294,35.31037],[71.5541,35.28776],[71.67495,35.21262],[71.52938,35.09023],[71.55273,35.02615],[71.49917,35.00478],[71.50329,34.97328],[71.29472,34.87728],[71.28356,34.80882],[71.08718,34.69034],[71.11602,34.63047],[71.0089,34.54568],[71.02401,34.44835],[71.17662,34.36769],[71.12815,34.26619],[71.13078,34.16503],[71.09453,34.13524],[71.09307,34.11961],[71.06933,34.10564],[71.07345,34.06242],[70.88119,33.97933],[70.54336,33.9463],[69.90203,34.04194],[69.87307,33.9689],[69.85671,33.93719],[70.00503,33.73528],[70.14236,33.71701],[70.14785,33.6553],[70.20141,33.64387],[70.17062,33.53535],[70.32775,33.34496],[70.13686,33.21064],[70.07369,33.22557],[70.02563,33.14282],[69.85259,33.09451],[69.79766,33.13247],[69.71526,33.09911],[69.57656,33.09911],[69.49004,33.01509],[69.49854,32.88843],[69.5436,32.8768],[69.47082,32.85834],[69.38018,32.76601],[69.43649,32.7302],[69.44747,32.6678],[69.38155,32.56601],[69.2868,32.53938],[69.23599,32.45946],[69.27932,32.29119],[69.27032,32.14141],[69.3225,31.93186],[69.20577,31.85957],[69.11514,31.70782],[69.00939,31.62249],[68.95995,31.64822],[68.91078,31.59687],[68.79997,31.61665],[68.6956,31.75687],[68.57475,31.83158],[68.44222,31.76446],[68.27605,31.75863],[68.25614,31.80357],[68.1655,31.82691],[68.00071,31.6564],[67.86887,31.63536],[67.72056,31.52304],[67.58323,31.52772],[67.62374,31.40473],[67.7748,31.4188],[67.78854,31.33203],[67.29964,31.19586],[67.03323,31.24519],[67.04147,31.31561],[66.83273,31.26867],[66.72561,31.20526],[66.68166,31.07597],[66.58175,30.97532],[66.42645,30.95309],[66.39194,30.9408],[66.28413,30.57001],[66.34869,30.404],[66.23609,30.06321],[66.36042,29.9583],[66.24175,29.85181],[65.04005,29.53957],[64.62116,29.58903],[64.19796,29.50407],[64.12966,29.39157],[63.5876,29.50456],[62.47751,29.40782],[60.87231,29.86514],[61.31508,29.38903],[61.53765,29.00507],[61.65978,28.77937],[61.93581,28.55284],[62.40259,28.42703],[62.59499,28.24842],[62.79412,28.28108],[62.7638,28.02992],[62.84905,27.47627],[62.79684,27.34381],[62.80604,27.22412],[63.19649,27.25674],[63.32283,27.14437],[63.25005,27.08692],[63.25005,26.84212],[63.18688,26.83844],[63.1889,26.65072],[62.77352,26.64099],[62.31484,26.528],[62.21304,26.26601],[62.05117,26.31647],[61.89391,26.26251],[61.83831,26.07249],[61.83968,25.7538],[61.683,25.66638],[61.6433,25.27541],[61.57592,25.0492],[61.5251,24.57287],[68.11329,23.53945],[68.20763,23.85849],[68.39339,23.96838],[68.74643,23.97027],[68.7416,24.31904],[68.90914,24.33156],[68.97781,24.26021],[69.07806,24.29777],[69.19341,24.25646],[69.29778,24.28712],[69.59579,24.29777],[69.73335,24.17007],[70.03428,24.172],[70.11712,24.30915],[70.5667,24.43787],[70.57906,24.27774],[70.71502,24.23517],[70.88393,24.27398],[70.85784,24.30903],[70.94985,24.3791],[71.04461,24.34657],[71.12838,24.42662],[71.00341,24.46038],[70.97594,24.60904],[71.09405,24.69017],[70.94002,24.92843],[70.89148,25.15064],[70.66695,25.39314],[70.67382,25.68186],[70.60378,25.71898],[70.53649,25.68928],[70.37444,25.67443],[70.2687,25.71156],[70.0985,25.93238],[70.08193,26.08094],[70.17532,26.24118],[70.17532,26.55362],[70.05584,26.60398],[69.88555,26.56836],[69.50904,26.74892],[69.58519,27.18109],[70.03136,27.56627],[70.12502,27.8057],[70.37307,28.01208],[70.60927,28.02178],[70.79054,27.68423],[71.89921,27.96035],[71.9244,28.11555],[72.20329,28.3869],[72.29495,28.66367],[72.40402,28.78283],[72.94272,29.02487],[73.01337,29.16422],[73.05886,29.1878],[73.28094,29.56646],[73.3962,29.94707],[73.58665,30.01848],[73.80299,30.06969],[73.97225,30.19829],[73.95736,30.28466],[73.88993,30.36305],[74.5616,31.04153],[74.67971,31.05479],[74.6852,31.12771],[74.60006,31.13711],[74.60281,31.10419],[74.56023,31.08303],[74.51629,31.13829],[74.53223,31.30321],[74.59773,31.4136],[74.64713,31.45605],[74.59319,31.50197],[74.61517,31.55698],[74.57498,31.60382],[74.47771,31.72227],[74.58907,31.87824],[74.79919,31.95983],[74.86236,32.04485],[74.9269,32.0658],[75.00793,32.03786],[75.25649,32.10187],[75.38046,32.26836],[75.28259,32.36556],[75.03265,32.49538],[74.97634,32.45367],[74.84725,32.49075],[74.68362,32.49298],[74.67431,32.56676],[74.65251,32.56416],[74.64424,32.60985],[74.69542,32.66792],[74.65345,32.71225],[74.7113,32.84219],[74.64675,32.82604],[74.6289,32.75561],[74.45312,32.77755],[74.41467,32.90563],[74.31227,32.92795],[74.34875,32.97823],[74.31854,33.02891],[74.17571,33.07495],[74.15374,33.13477],[74.02144,33.18908],[74.01366,33.25199],[74.08782,33.26232],[74.17983,33.3679],[74.18121,33.4745],[74.10115,33.56392],[74.03576,33.56718],[73.97367,33.64061],[73.98968,33.66155],[73.96423,33.73071],[74.00891,33.75437],[74.05898,33.82089],[74.14001,33.83002],[74.26086,33.92237],[74.25262,34.01577],[74.21554,34.03853],[73.91341,34.01235],[73.88732,34.05105],[73.90677,34.10504],[73.98208,34.2522],[73.90517,34.35317],[73.8475,34.32935],[73.74862,34.34183],[73.74999,34.3781],[73.88732,34.48911],[73.89419,34.54568],[73.93951,34.57169],[73.93401,34.63386],[73.96423,34.68244],[74.12897,34.70073],[74.31239,34.79626],[74.58083,34.77386],[74.6663,34.703],[75.01479,34.64629],[75.38009,34.55021],[75.75438,34.51827],[76.04614,34.67566],[76.15463,34.6429],[76.47186,34.78965],[76.67648,34.76371],[76.74377,34.84039],[76.74514,34.92488],[76.87193,34.96906],[76.99251,34.93349],[77.11796,35.05419],[76.93465,35.39866],[76.85088,35.39754],[76.75475,35.52617],[76.77323,35.66062],[76.50961,35.8908],[76.33453,35.84296],[76.14913,35.82848],[76.15325,35.9264],[75.93028,36.13136],[76.00906,36.17511],[76.0324,36.41198],[75.92391,36.56986],[75.72737,36.7529]]]]}},{type:"Feature",properties:{iso1A2:"PL",iso1A3:"POL",iso1N3:"616",wikidata:"Q36",nameEn:"Poland",groups:["EU","151","150"],callingCodes:["48"]},geometry:{type:"MultiPolygon",coordinates:[[[[18.57853,55.25302],[14.20811,54.12784],[14.22634,53.9291],[14.20647,53.91671],[14.18544,53.91258],[14.20823,53.90776],[14.21323,53.8664],[14.27249,53.74464],[14.26782,53.69866],[14.2836,53.67721],[14.27133,53.66613],[14.28477,53.65955],[14.2853,53.63392],[14.31904,53.61581],[14.30416,53.55499],[14.3273,53.50587],[14.35209,53.49506],[14.4215,53.27724],[14.44133,53.27427],[14.45125,53.26241],[14.40662,53.21098],[14.37853,53.20405],[14.36696,53.16444],[14.38679,53.13669],[14.35044,53.05829],[14.25954,53.00264],[14.14056,52.95786],[14.15873,52.87715],[14.12256,52.84311],[14.13806,52.82392],[14.22071,52.81175],[14.61073,52.59847],[14.6289,52.57136],[14.60081,52.53116],[14.63056,52.48993],[14.54423,52.42568],[14.55228,52.35264],[14.56378,52.33838],[14.58149,52.28007],[14.70139,52.25038],[14.71319,52.22144],[14.68344,52.19612],[14.70616,52.16927],[14.67683,52.13936],[14.6917,52.10283],[14.72971,52.09167],[14.76026,52.06624],[14.71339,52.00337],[14.70488,51.97679],[14.7139,51.95643],[14.71836,51.95606],[14.72163,51.95188],[14.7177,51.94048],[14.70601,51.92944],[14.6933,51.9044],[14.6588,51.88359],[14.59089,51.83302],[14.60493,51.80473],[14.64625,51.79472],[14.66386,51.73282],[14.69065,51.70842],[14.75392,51.67445],[14.75759,51.62318],[14.7727,51.61263],[14.71125,51.56209],[14.73047,51.54606],[14.72652,51.53902],[14.73219,51.52922],[14.94749,51.47155],[14.9652,51.44793],[14.96899,51.38367],[14.98008,51.33449],[15.04288,51.28387],[15.01242,51.21285],[15.0047,51.16874],[14.99311,51.16249],[14.99414,51.15813],[15.00083,51.14974],[14.99646,51.14365],[14.99079,51.14284],[14.99689,51.12205],[14.98229,51.11354],[14.97938,51.07742],[14.95529,51.04552],[14.92942,50.99744],[14.89252,50.94999],[14.89681,50.9422],[14.81664,50.88148],[14.82803,50.86966],[14.99852,50.86817],[15.01088,50.97984],[14.96419,50.99108],[15.02433,51.0242],[15.03895,51.0123],[15.06218,51.02269],[15.10152,51.01095],[15.11937,50.99021],[15.16744,51.01959],[15.1743,50.9833],[15.2361,50.99886],[15.27043,50.97724],[15.2773,50.8907],[15.36656,50.83956],[15.3803,50.77187],[15.43798,50.80833],[15.73186,50.73885],[15.81683,50.75666],[15.87331,50.67188],[15.97219,50.69799],[16.0175,50.63009],[15.98317,50.61528],[16.02437,50.60046],[16.10265,50.66405],[16.20839,50.63096],[16.23174,50.67101],[16.33611,50.66579],[16.44597,50.58041],[16.34572,50.49575],[16.31413,50.50274],[16.19526,50.43291],[16.21585,50.40627],[16.22821,50.41054],[16.28118,50.36891],[16.30289,50.38292],[16.36495,50.37679],[16.3622,50.34875],[16.39379,50.3207],[16.42674,50.32509],[16.56407,50.21009],[16.55446,50.16613],[16.63137,50.1142],[16.7014,50.09659],[16.8456,50.20834],[16.98018,50.24172],[17.00353,50.21449],[17.02825,50.23118],[16.99803,50.25753],[17.02138,50.27772],[16.99803,50.30316],[16.94448,50.31281],[16.90877,50.38642],[16.85933,50.41093],[16.89229,50.45117],[17.1224,50.39494],[17.14498,50.38117],[17.19579,50.38817],[17.19991,50.3654],[17.27681,50.32246],[17.34273,50.32947],[17.34548,50.2628],[17.3702,50.28123],[17.58889,50.27837],[17.67764,50.28977],[17.69292,50.32859],[17.74648,50.29966],[17.72176,50.25665],[17.76296,50.23382],[17.70528,50.18812],[17.59404,50.16437],[17.66683,50.10275],[17.6888,50.12037],[17.7506,50.07896],[17.77669,50.02253],[17.86886,49.97452],[18.00191,50.01723],[18.04585,50.01194],[18.04585,50.03311],[18.00396,50.04954],[18.03212,50.06574],[18.07898,50.04535],[18.10628,50.00223],[18.20241,49.99958],[18.21752,49.97309],[18.27107,49.96779],[18.27794,49.93863],[18.31914,49.91565],[18.33278,49.92415],[18.33562,49.94747],[18.41604,49.93498],[18.53423,49.89906],[18.54495,49.9079],[18.54299,49.92537],[18.57697,49.91565],[18.57045,49.87849],[18.60341,49.86256],[18.57183,49.83334],[18.61278,49.7618],[18.61368,49.75426],[18.62645,49.75002],[18.62943,49.74603],[18.62676,49.71983],[18.69817,49.70473],[18.72838,49.68163],[18.80479,49.6815],[18.84786,49.5446],[18.84521,49.51672],[18.94536,49.52143],[18.97283,49.49914],[18.9742,49.39557],[19.18019,49.41165],[19.25435,49.53391],[19.36009,49.53747],[19.37795,49.574],[19.45348,49.61583],[19.52626,49.57311],[19.53313,49.52856],[19.57845,49.46077],[19.64162,49.45184],[19.6375,49.40897],[19.72127,49.39288],[19.78581,49.41701],[19.82237,49.27806],[19.75286,49.20751],[19.86409,49.19316],[19.90529,49.23532],[19.98494,49.22904],[20.08238,49.1813],[20.13738,49.31685],[20.21977,49.35265],[20.31453,49.34817],[20.31728,49.39914],[20.39939,49.3896],[20.46422,49.41612],[20.5631,49.375],[20.61666,49.41791],[20.72274,49.41813],[20.77971,49.35383],[20.9229,49.29626],[20.98733,49.30774],[21.09799,49.37176],[21.041,49.41791],[21.12477,49.43666],[21.19756,49.4054],[21.27858,49.45988],[21.43376,49.41433],[21.62328,49.4447],[21.77983,49.35443],[21.82927,49.39467],[21.96385,49.3437],[22.04427,49.22136],[22.56155,49.08865],[22.89122,49.00725],[22.86336,49.10513],[22.72009,49.20288],[22.748,49.32759],[22.69444,49.49378],[22.64534,49.53094],[22.78304,49.65543],[22.80261,49.69098],[22.83179,49.69875],[22.99329,49.84249],[23.28221,50.0957],[23.67635,50.33385],[23.71382,50.38248],[23.79445,50.40481],[23.99563,50.41289],[24.03668,50.44507],[24.07048,50.5071],[24.0996,50.60752],[24.0595,50.71625],[23.95925,50.79271],[23.99254,50.83847],[24.0952,50.83262],[24.14524,50.86128],[24.04576,50.90196],[23.92217,51.00836],[23.90376,51.07697],[23.80678,51.18405],[23.63858,51.32182],[23.69905,51.40871],[23.62751,51.50512],[23.56236,51.53673],[23.57053,51.55938],[23.53198,51.74298],[23.62691,51.78208],[23.61523,51.92066],[23.68733,51.9906],[23.64066,52.07626],[23.61,52.11264],[23.54314,52.12148],[23.47859,52.18215],[23.20071,52.22848],[23.18196,52.28812],[23.34141,52.44845],[23.45112,52.53774],[23.58296,52.59868],[23.73615,52.6149],[23.93763,52.71332],[23.91805,52.94016],[23.94689,52.95919],[23.92184,53.02079],[23.87548,53.0831],[23.91393,53.16469],[23.85657,53.22923],[23.81995,53.24131],[23.62004,53.60942],[23.51284,53.95052],[23.48261,53.98855],[23.52702,54.04622],[23.49196,54.14764],[23.45223,54.17775],[23.42418,54.17911],[23.39525,54.21672],[23.3494,54.25155],[23.24656,54.25701],[23.15938,54.29894],[23.15526,54.31076],[23.13905,54.31567],[23.104,54.29794],[23.04323,54.31567],[23.05726,54.34565],[22.99649,54.35927],[23.00584,54.38514],[22.83756,54.40827],[22.79705,54.36264],[21.41123,54.32395],[20.63871,54.3706],[19.8038,54.44203],[19.64312,54.45423],[18.57853,55.25302]]]]}},{type:"Feature",properties:{iso1A2:"PM",iso1A3:"SPM",iso1N3:"666",wikidata:"Q34617",nameEn:"Saint Pierre and Miquelon",country:"FR",groups:["021","003","019"],callingCodes:["508"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.72993,46.65575],[-55.90758,46.6223],[-56.27503,47.39728],[-56.72993,46.65575]]]]}},{type:"Feature",properties:{iso1A2:"PN",iso1A3:"PCN",iso1N3:"612",wikidata:"Q35672",nameEn:"Pitcairn Islands",country:"GB",groups:["061","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325],[-133.59543,-28.4709]]]]}},{type:"Feature",properties:{iso1A2:"PR",iso1A3:"PRI",iso1N3:"630",wikidata:"Q1183",nameEn:"Puerto Rico",country:"US",groups:["029","003","419","019"],roadSpeedUnit:"mph",callingCodes:["1 787","1 939"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.27974,17.56928],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927],[-65.27974,17.56928]]]]}},{type:"Feature",properties:{iso1A2:"PS",iso1A3:"PSE",iso1N3:"275",wikidata:"Q23792",nameEn:"Palestine",country:"IL",groups:["145","142"],callingCodes:["970"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.052,31.46619],[34.21853,31.32363],[34.23572,31.2966],[34.24012,31.29591],[34.26742,31.21998],[34.29417,31.24194],[34.36523,31.28963],[34.37381,31.30598],[34.36505,31.36404],[34.40077,31.40926],[34.48892,31.48365],[34.56797,31.54197],[34.48681,31.59711],[34.29262,31.70393],[34.052,31.46619]]],[[[35.47672,31.49578],[35.55941,31.76535],[35.52758,31.9131],[35.54375,31.96587],[35.52012,32.04076],[35.57111,32.21877],[35.55807,32.38674],[35.42078,32.41562],[35.41048,32.43706],[35.41598,32.45593],[35.42034,32.46009],[35.40224,32.50136],[35.35212,32.52047],[35.30685,32.51024],[35.29306,32.50947],[35.25049,32.52453],[35.2244,32.55289],[35.15937,32.50466],[35.10882,32.4757],[35.10024,32.47856],[35.09236,32.47614],[35.08564,32.46948],[35.07059,32.4585],[35.05423,32.41754],[35.05311,32.4024],[35.0421,32.38242],[35.05142,32.3667],[35.04243,32.35008],[35.01772,32.33863],[35.01119,32.28684],[35.02939,32.2671],[35.01841,32.23981],[34.98885,32.20758],[34.95703,32.19522],[34.96009,32.17503],[34.99039,32.14626],[34.98507,32.12606],[34.99437,32.10962],[34.9863,32.09551],[35.00261,32.027],[34.98682,31.96935],[35.00124,31.93264],[35.03489,31.92448],[35.03978,31.89276],[35.03489,31.85919],[34.99712,31.85569],[34.9724,31.83352],[35.01978,31.82944],[35.05617,31.85685],[35.07677,31.85627],[35.14174,31.81325],[35.18603,31.80901],[35.18169,31.82542],[35.19461,31.82687],[35.21469,31.81835],[35.216,31.83894],[35.21128,31.863],[35.20381,31.86716],[35.20673,31.88151],[35.20791,31.8821],[35.20945,31.8815],[35.21016,31.88237],[35.21276,31.88153],[35.2136,31.88241],[35.22014,31.88264],[35.22294,31.87889],[35.22567,31.86745],[35.22817,31.8638],[35.2249,31.85433],[35.2304,31.84222],[35.24816,31.8458],[35.25753,31.8387],[35.251,31.83085],[35.26404,31.82567],[35.25573,31.81362],[35.26058,31.79064],[35.25225,31.7678],[35.26319,31.74846],[35.25182,31.73945],[35.24981,31.72543],[35.2438,31.7201],[35.24315,31.71244],[35.23972,31.70896],[35.22392,31.71899],[35.21937,31.71578],[35.20538,31.72388],[35.18023,31.72067],[35.16478,31.73242],[35.15474,31.73352],[35.15119,31.73634],[35.13931,31.73012],[35.12933,31.7325],[35.11895,31.71454],[35.10782,31.71594],[35.08226,31.69107],[35.00879,31.65426],[34.95249,31.59813],[34.9415,31.55601],[34.94356,31.50743],[34.93258,31.47816],[34.89756,31.43891],[34.87833,31.39321],[34.88932,31.37093],[34.92571,31.34337],[35.02459,31.35979],[35.13033,31.3551],[35.22921,31.37445],[35.39675,31.49572],[35.47672,31.49578]]]]}},{type:"Feature",properties:{iso1A2:"PT",iso1A3:"PRT",iso1N3:"620",wikidata:"Q45",nameEn:"Portugal",groups:["EU","039","150"],callingCodes:["351"]},geometry:{type:"MultiPolygon",coordinates:[[[[-6.19128,41.57638],[-6.29863,41.66432],[-6.44204,41.68258],[-6.49907,41.65823],[-6.54633,41.68623],[-6.56426,41.74219],[-6.51374,41.8758],[-6.56752,41.88429],[-6.5447,41.94371],[-6.58544,41.96674],[-6.61967,41.94008],[-6.75004,41.94129],[-6.76959,41.98734],[-6.81196,41.99097],[-6.82174,41.94493],[-6.94396,41.94403],[-6.95537,41.96553],[-6.98144,41.9728],[-7.01078,41.94977],[-7.07596,41.94977],[-7.08574,41.97401],[-7.14115,41.98855],[-7.18549,41.97515],[-7.18677,41.88793],[-7.32366,41.8406],[-7.37092,41.85031],[-7.42864,41.80589],[-7.42854,41.83262],[-7.44759,41.84451],[-7.45566,41.86488],[-7.49803,41.87095],[-7.52737,41.83939],[-7.62188,41.83089],[-7.58603,41.87944],[-7.65774,41.88308],[-7.69848,41.90977],[-7.84188,41.88065],[-7.88055,41.84571],[-7.88751,41.92553],[-7.90707,41.92432],[-7.92336,41.8758],[-7.9804,41.87337],[-8.01136,41.83453],[-8.0961,41.81024],[-8.16455,41.81753],[-8.16944,41.87944],[-8.19551,41.87459],[-8.2185,41.91237],[-8.16232,41.9828],[-8.08796,42.01398],[-8.08847,42.05767],[-8.11729,42.08537],[-8.18178,42.06436],[-8.19406,42.12141],[-8.18947,42.13853],[-8.1986,42.15402],[-8.22406,42.1328],[-8.24681,42.13993],[-8.2732,42.12396],[-8.29809,42.106],[-8.32161,42.10218],[-8.33912,42.08358],[-8.36353,42.09065],[-8.38323,42.07683],[-8.40143,42.08052],[-8.42512,42.07199],[-8.44123,42.08218],[-8.48185,42.0811],[-8.52837,42.07658],[-8.5252,42.06264],[-8.54563,42.0537],[-8.58086,42.05147],[-8.59493,42.05708],[-8.63791,42.04691],[-8.64626,42.03668],[-8.65832,42.02972],[-8.6681,41.99703],[-8.69071,41.98862],[-8.7478,41.96282],[-8.74606,41.9469],[-8.75712,41.92833],[-8.81794,41.90375],[-8.87157,41.86488],[-9.14112,41.86623],[-36.43765,41.39418],[-15.92339,29.50503],[-7.37282,36.96896],[-7.39769,37.16868],[-7.41133,37.20314],[-7.41854,37.23813],[-7.43227,37.25152],[-7.43974,37.38913],[-7.46878,37.47127],[-7.51759,37.56119],[-7.41981,37.75729],[-7.33441,37.81193],[-7.27314,37.90145],[-7.24544,37.98884],[-7.12648,38.00296],[-7.10366,38.04404],[-7.05966,38.01966],[-7.00375,38.01914],[-6.93418,38.21454],[-7.09389,38.17227],[-7.15581,38.27597],[-7.32529,38.44336],[-7.265,38.61674],[-7.26174,38.72107],[-7.03848,38.87221],[-7.051,38.907],[-6.95211,39.0243],[-6.97004,39.07619],[-7.04011,39.11919],[-7.10692,39.10275],[-7.14929,39.11287],[-7.12811,39.17101],[-7.23566,39.20132],[-7.23403,39.27579],[-7.3149,39.34857],[-7.2927,39.45847],[-7.49477,39.58794],[-7.54121,39.66717],[-7.33507,39.64569],[-7.24707,39.66576],[-7.01613,39.66877],[-6.97492,39.81488],[-6.91463,39.86618],[-6.86737,40.01986],[-6.94233,40.10716],[-7.00589,40.12087],[-7.02544,40.18564],[-7.00426,40.23169],[-6.86085,40.26776],[-6.86085,40.2976],[-6.80218,40.33239],[-6.78426,40.36468],[-6.84618,40.42177],[-6.84944,40.46394],[-6.7973,40.51723],[-6.80218,40.55067],[-6.84292,40.56801],[-6.79567,40.65955],[-6.82826,40.74603],[-6.82337,40.84472],[-6.79892,40.84842],[-6.80707,40.88047],[-6.84292,40.89771],[-6.8527,40.93958],[-6.9357,41.02888],[-6.913,41.03922],[-6.88843,41.03027],[-6.84781,41.02692],[-6.80942,41.03629],[-6.79241,41.05397],[-6.75655,41.10187],[-6.77319,41.13049],[-6.69711,41.1858],[-6.68286,41.21641],[-6.65046,41.24725],[-6.55937,41.24417],[-6.38551,41.35274],[-6.38553,41.38655],[-6.3306,41.37677],[-6.26777,41.48796],[-6.19128,41.57638]]]]}},{type:"Feature",properties:{iso1A2:"PW",iso1A3:"PLW",iso1N3:"585",wikidata:"Q695",nameEn:"Palau",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["680"]},geometry:{type:"MultiPolygon",coordinates:[[[[128.97621,3.08804],[134.40878,1.79674],[136.27107,6.73747],[136.04605,12.45908],[128.97621,3.08804]]]]}},{type:"Feature",properties:{iso1A2:"PY",iso1A3:"PRY",iso1N3:"600",wikidata:"Q733",nameEn:"Paraguay",groups:["005","419","019"],callingCodes:["595"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.16225,-20.16193],[-58.23216,-19.80058],[-59.06965,-19.29148],[-60.00638,-19.2981],[-61.73723,-19.63958],[-61.93912,-20.10053],[-62.26883,-20.55311],[-62.2757,-21.06657],[-62.64455,-22.25091],[-62.51761,-22.37684],[-62.22768,-22.55807],[-61.9756,-23.0507],[-61.0782,-23.62932],[-60.99754,-23.80934],[-60.28163,-24.04436],[-60.03367,-24.00701],[-59.45482,-24.34787],[-59.33886,-24.49935],[-58.33055,-24.97099],[-58.25492,-24.92528],[-57.80821,-25.13863],[-57.57431,-25.47269],[-57.87176,-25.93604],[-58.1188,-26.16704],[-58.3198,-26.83443],[-58.65321,-27.14028],[-58.59549,-27.29973],[-58.04205,-27.2387],[-56.85337,-27.5165],[-56.18313,-27.29851],[-55.89195,-27.3467],[-55.74475,-27.44485],[-55.59094,-27.32444],[-55.62322,-27.1941],[-55.39611,-26.97679],[-55.25243,-26.93808],[-55.16948,-26.96068],[-55.06351,-26.80195],[-55.00584,-26.78754],[-54.80868,-26.55669],[-54.70732,-26.45099],[-54.69333,-26.37705],[-54.67359,-25.98607],[-54.60664,-25.9691],[-54.62063,-25.91213],[-54.59398,-25.59224],[-54.59509,-25.53696],[-54.60196,-25.48397],[-54.62033,-25.46026],[-54.4423,-25.13381],[-54.28207,-24.07305],[-54.32807,-24.01865],[-54.6238,-23.83078],[-55.02691,-23.97317],[-55.0518,-23.98666],[-55.12292,-23.99669],[-55.41784,-23.9657],[-55.44117,-23.9185],[-55.43585,-23.87157],[-55.5555,-23.28237],[-55.52288,-23.2595],[-55.5446,-23.22811],[-55.63849,-22.95122],[-55.62493,-22.62765],[-55.68742,-22.58407],[-55.6986,-22.56268],[-55.72366,-22.5519],[-55.741,-22.52018],[-55.74941,-22.46436],[-55.8331,-22.29008],[-56.23206,-22.25347],[-56.45893,-22.08072],[-56.5212,-22.11556],[-56.6508,-22.28387],[-57.98625,-22.09157],[-57.94642,-21.73799],[-57.88239,-21.6868],[-57.93492,-21.65505],[-57.84536,-20.93155],[-58.16225,-20.16193]]]]}},{type:"Feature",properties:{iso1A2:"QA",iso1A3:"QAT",iso1N3:"634",wikidata:"Q846",nameEn:"Qatar",groups:["145","142"],callingCodes:["974"]},geometry:{type:"MultiPolygon",coordinates:[[[[50.92992,24.54396],[51.09638,24.46907],[51.29972,24.50747],[51.39468,24.62785],[51.58834,24.66608],[51.83108,24.71675],[51.83682,26.70231],[50.93865,26.30758],[50.81266,25.88946],[50.86149,25.6965],[50.7801,25.595],[50.80824,25.54641],[50.57069,25.57887],[50.8133,24.74049],[50.92992,24.54396]]]]}},{type:"Feature",properties:{iso1A2:"RE",iso1A3:"REU",iso1N3:"638",wikidata:"Q17070",nameEn:"Réunion",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.37984,-21.23941],[56.73473,-21.9174],[56.62373,-20.2711],[53.37984,-21.23941]]]]}},{type:"Feature",properties:{iso1A2:"RO",iso1A3:"ROU",iso1N3:"642",wikidata:"Q218",nameEn:"Romania",groups:["EU","151","150"],callingCodes:["40"]},geometry:{type:"MultiPolygon",coordinates:[[[[27.15622,47.98538],[27.02985,48.09083],[27.04118,48.12522],[26.96119,48.13003],[26.98042,48.15752],[26.94265,48.1969],[26.87708,48.19919],[26.81161,48.25049],[26.62823,48.25804],[26.55202,48.22445],[26.33504,48.18418],[26.17711,47.99246],[26.05901,47.9897],[25.77723,47.93919],[25.63878,47.94924],[25.23778,47.89403],[25.11144,47.75203],[24.88896,47.7234],[24.81893,47.82031],[24.70632,47.84428],[24.61994,47.95062],[24.43578,47.97131],[24.34926,47.9244],[24.22566,47.90231],[24.11281,47.91487],[24.06466,47.95317],[24.02999,47.95087],[24.00801,47.968],[23.98553,47.96076],[23.96337,47.96672],[23.94192,47.94868],[23.89352,47.94512],[23.8602,47.9329],[23.80904,47.98142],[23.75188,47.99705],[23.66262,47.98786],[23.63894,48.00293],[23.5653,48.00499],[23.52803,48.01818],[23.4979,47.96858],[23.33577,48.0237],[23.27397,48.08245],[23.15999,48.12188],[23.1133,48.08061],[23.08858,48.00716],[23.0158,47.99338],[22.92241,48.02002],[22.94301,47.96672],[22.89849,47.95851],[22.77991,47.87211],[22.76617,47.8417],[22.67247,47.7871],[22.46559,47.76583],[22.41979,47.7391],[22.31816,47.76126],[22.00917,47.50492],[22.03389,47.42508],[22.01055,47.37767],[21.94463,47.38046],[21.78395,47.11104],[21.648,47.03902],[21.68645,46.99595],[21.59581,46.91628],[21.59307,46.86935],[21.52028,46.84118],[21.48935,46.7577],[21.5151,46.72147],[21.43926,46.65109],[21.33214,46.63035],[21.26929,46.4993],[21.28061,46.44941],[21.16872,46.30118],[21.06572,46.24897],[20.86797,46.28884],[20.74574,46.25467],[20.76085,46.21002],[20.63863,46.12728],[20.49718,46.18721],[20.45377,46.14405],[20.35573,46.16629],[20.28324,46.1438],[20.26068,46.12332],[20.35862,45.99356],[20.54818,45.89939],[20.65645,45.82801],[20.70069,45.7493],[20.77416,45.75601],[20.78446,45.78522],[20.82364,45.77738],[20.80361,45.65875],[20.76798,45.60969],[20.83321,45.53567],[20.77217,45.49788],[20.86026,45.47295],[20.87948,45.42743],[21.09894,45.30144],[21.17612,45.32566],[21.20392,45.2677],[21.29398,45.24148],[21.48278,45.19557],[21.51299,45.15345],[21.4505,45.04294],[21.35855,45.01941],[21.54938,44.9327],[21.56328,44.89502],[21.48202,44.87199],[21.44013,44.87613],[21.35643,44.86364],[21.38802,44.78133],[21.55007,44.77304],[21.60019,44.75208],[21.61942,44.67059],[21.67504,44.67107],[21.71692,44.65349],[21.7795,44.66165],[21.99364,44.63395],[22.08016,44.49844],[22.13234,44.47444],[22.18315,44.48179],[22.30844,44.6619],[22.45301,44.7194],[22.61917,44.61489],[22.69196,44.61587],[22.76749,44.54446],[22.70981,44.51852],[22.61368,44.55719],[22.56493,44.53419],[22.54021,44.47836],[22.45436,44.47258],[22.56012,44.30712],[22.68166,44.28206],[22.67173,44.21564],[23.04988,44.07694],[23.01674,44.01946],[22.87873,43.9844],[22.83753,43.88055],[22.85314,43.84452],[23.05288,43.79494],[23.26772,43.84843],[23.4507,43.84936],[23.61687,43.79289],[23.73978,43.80627],[24.18149,43.68218],[24.35364,43.70211],[24.50264,43.76314],[24.62281,43.74082],[24.73542,43.68523],[24.96682,43.72693],[25.10718,43.6831],[25.17144,43.70261],[25.39528,43.61866],[25.72792,43.69263],[25.94911,43.85745],[26.05584,43.90925],[26.10115,43.96908],[26.38764,44.04356],[26.62712,44.05698],[26.95141,44.13555],[27.26845,44.12602],[27.39757,44.0141],[27.60834,44.01206],[27.64542,44.04958],[27.73468,43.95326],[27.92008,44.00761],[27.99558,43.84193],[28.23293,43.76],[29.24336,43.70874],[30.04414,45.08461],[29.69272,45.19227],[29.65428,45.25629],[29.68175,45.26885],[29.59798,45.38857],[29.42632,45.44545],[29.24779,45.43388],[28.96077,45.33164],[28.94292,45.28045],[28.81383,45.3384],[28.78911,45.24179],[28.71358,45.22631],[28.5735,45.24759],[28.34554,45.32102],[28.28504,45.43907],[28.21139,45.46895],[28.18741,45.47358],[28.08927,45.6051],[28.16568,45.6421],[28.13111,45.92819],[28.08612,46.01105],[28.13684,46.18099],[28.10937,46.22852],[28.19864,46.31869],[28.18902,46.35283],[28.25769,46.43334],[28.22281,46.50481],[28.24808,46.64305],[28.12173,46.82283],[28.09095,46.97621],[27.81892,47.1381],[27.73172,47.29248],[27.68706,47.28962],[27.60263,47.32507],[27.55731,47.46637],[27.47942,47.48113],[27.3979,47.59473],[27.32202,47.64009],[27.25519,47.71366],[27.29069,47.73722],[27.1618,47.92391],[27.15622,47.98538]]]]}},{type:"Feature",properties:{iso1A2:"RS",iso1A3:"SRB",iso1N3:"688",wikidata:"Q403",nameEn:"Serbia",groups:["039","150"],callingCodes:["381"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.66007,46.19005],[19.56113,46.16824],[19.52473,46.1171],[19.28826,45.99694],[19.14543,45.9998],[19.10388,46.04015],[19.0791,45.96458],[19.01284,45.96529],[18.99712,45.93537],[18.81394,45.91329],[18.85783,45.85493],[18.90305,45.71863],[18.96691,45.66731],[18.88776,45.57253],[18.94562,45.53712],[19.07471,45.53086],[19.08364,45.48804],[18.99918,45.49333],[18.97446,45.37528],[19.10774,45.29547],[19.28208,45.23813],[19.41941,45.23475],[19.43589,45.17137],[19.19144,45.17863],[19.14063,45.12972],[19.07952,45.14668],[19.1011,45.01191],[19.05205,44.97692],[19.15573,44.95409],[19.06853,44.89915],[19.02871,44.92541],[18.98957,44.90645],[19.01994,44.85493],[19.18183,44.92055],[19.36722,44.88164],[19.32543,44.74058],[19.26388,44.65412],[19.16699,44.52197],[19.13369,44.52521],[19.12278,44.50132],[19.14837,44.45253],[19.14681,44.41463],[19.11785,44.40313],[19.10749,44.39421],[19.10704,44.38249],[19.10365,44.37795],[19.10298,44.36924],[19.11865,44.36712],[19.1083,44.3558],[19.11547,44.34218],[19.13556,44.338],[19.13332,44.31492],[19.16741,44.28648],[19.18328,44.28383],[19.20508,44.2917],[19.23306,44.26097],[19.26945,44.26957],[19.32464,44.27185],[19.34773,44.23244],[19.3588,44.18353],[19.40927,44.16722],[19.43905,44.13088],[19.47338,44.15034],[19.48386,44.14332],[19.47321,44.1193],[19.51167,44.08158],[19.55999,44.06894],[19.57467,44.04716],[19.61991,44.05254],[19.61836,44.01464],[19.56498,43.99922],[19.52515,43.95573],[19.38439,43.96611],[19.24363,44.01502],[19.23465,43.98764],[19.3986,43.79668],[19.5176,43.71403],[19.50455,43.58385],[19.42696,43.57987],[19.41941,43.54056],[19.36653,43.60921],[19.33426,43.58833],[19.2553,43.5938],[19.24774,43.53061],[19.22807,43.5264],[19.22229,43.47926],[19.44315,43.38846],[19.48171,43.32644],[19.52962,43.31623],[19.54598,43.25158],[19.62661,43.2286],[19.64063,43.19027],[19.76918,43.16044],[19.79255,43.11951],[19.92576,43.08539],[19.96549,43.11098],[19.98887,43.0538],[20.04729,43.02732],[20.05431,42.99571],[20.12325,42.96237],[20.14896,42.99058],[20.16415,42.97177],[20.34528,42.90676],[20.35692,42.8335],[20.40594,42.84853],[20.43734,42.83157],[20.53484,42.8885],[20.48692,42.93208],[20.59929,43.01067],[20.64557,43.00826],[20.69515,43.09641],[20.59929,43.20492],[20.68688,43.21335],[20.73811,43.25068],[20.82145,43.26769],[20.88685,43.21697],[20.83727,43.17842],[20.96287,43.12416],[21.00749,43.13984],[21.05378,43.10707],[21.08952,43.13471],[21.14465,43.11089],[21.16734,42.99694],[21.2041,43.02277],[21.23877,43.00848],[21.23534,42.95523],[21.2719,42.8994],[21.32974,42.90424],[21.36941,42.87397],[21.44047,42.87276],[21.39045,42.74888],[21.47498,42.74695],[21.59154,42.72643],[21.58755,42.70418],[21.6626,42.67813],[21.75025,42.70125],[21.79413,42.65923],[21.75672,42.62695],[21.7327,42.55041],[21.70522,42.54176],[21.7035,42.51899],[21.62556,42.45106],[21.64209,42.41081],[21.62887,42.37664],[21.59029,42.38042],[21.57021,42.3647],[21.53467,42.36809],[21.5264,42.33634],[21.56772,42.30946],[21.58992,42.25915],[21.70111,42.23789],[21.77176,42.2648],[21.84654,42.3247],[21.91595,42.30392],[21.94405,42.34669],[22.02908,42.29848],[22.16384,42.32103],[22.29605,42.37477],[22.29275,42.34913],[22.34773,42.31725],[22.45919,42.33822],[22.47498,42.3915],[22.51961,42.3991],[22.55669,42.50144],[22.43983,42.56851],[22.4997,42.74144],[22.43309,42.82057],[22.54302,42.87774],[22.74826,42.88701],[22.78397,42.98253],[22.89521,43.03625],[22.98104,43.11199],[23.00806,43.19279],[22.89727,43.22417],[22.82036,43.33665],[22.53397,43.47225],[22.47582,43.6558],[22.41043,43.69566],[22.35558,43.81281],[22.41449,44.00514],[22.61688,44.06534],[22.61711,44.16938],[22.67173,44.21564],[22.68166,44.28206],[22.56012,44.30712],[22.45436,44.47258],[22.54021,44.47836],[22.56493,44.53419],[22.61368,44.55719],[22.70981,44.51852],[22.76749,44.54446],[22.69196,44.61587],[22.61917,44.61489],[22.45301,44.7194],[22.30844,44.6619],[22.18315,44.48179],[22.13234,44.47444],[22.08016,44.49844],[21.99364,44.63395],[21.7795,44.66165],[21.71692,44.65349],[21.67504,44.67107],[21.61942,44.67059],[21.60019,44.75208],[21.55007,44.77304],[21.38802,44.78133],[21.35643,44.86364],[21.44013,44.87613],[21.48202,44.87199],[21.56328,44.89502],[21.54938,44.9327],[21.35855,45.01941],[21.4505,45.04294],[21.51299,45.15345],[21.48278,45.19557],[21.29398,45.24148],[21.20392,45.2677],[21.17612,45.32566],[21.09894,45.30144],[20.87948,45.42743],[20.86026,45.47295],[20.77217,45.49788],[20.83321,45.53567],[20.76798,45.60969],[20.80361,45.65875],[20.82364,45.77738],[20.78446,45.78522],[20.77416,45.75601],[20.70069,45.7493],[20.65645,45.82801],[20.54818,45.89939],[20.35862,45.99356],[20.26068,46.12332],[20.09713,46.17315],[20.03533,46.14509],[20.01816,46.17696],[19.93508,46.17553],[19.81491,46.1313],[19.66007,46.19005]]]]}},{type:"Feature",properties:{iso1A2:"RU",iso1A3:"RUS",iso1N3:"643",wikidata:"Q159",nameEn:"Russia",groups:["151","150"],callingCodes:["7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-179.99933,64.74703],[-172.76104,63.77445],[-169.03888,65.48473],[-168.95635,65.98512],[-168.25765,71.99091],[-179.9843,71.90735],[-179.99933,64.74703]]],[[[39.81147,43.06294],[40.0078,43.38551],[40.00853,43.40578],[40.01552,43.42025],[40.01007,43.42411],[40.03312,43.44262],[40.04445,43.47776],[40.10657,43.57344],[40.65957,43.56212],[41.64935,43.22331],[42.40563,43.23226],[42.66667,43.13917],[42.75889,43.19651],[43.03322,43.08883],[43.0419,43.02413],[43.81453,42.74297],[43.73119,42.62043],[43.95517,42.55396],[44.54202,42.75699],[44.70002,42.74679],[44.80941,42.61277],[44.88754,42.74934],[45.15318,42.70598],[45.36501,42.55268],[45.78692,42.48358],[45.61676,42.20768],[46.42738,41.91323],[46.5332,41.87389],[46.58924,41.80547],[46.75269,41.8623],[46.8134,41.76252],[47.00955,41.63583],[46.99554,41.59743],[47.03757,41.55434],[47.10762,41.59044],[47.34579,41.27884],[47.49004,41.26366],[47.54504,41.20275],[47.62288,41.22969],[47.75831,41.19455],[47.87973,41.21798],[48.07587,41.49957],[48.22064,41.51472],[48.2878,41.56221],[48.40277,41.60441],[48.42301,41.65444],[48.55078,41.77917],[48.5867,41.84306],[48.80971,41.95365],[49.2134,44.84989],[49.88945,46.04554],[49.32259,46.26944],[49.16518,46.38542],[48.54988,46.56267],[48.51142,46.69268],[49.01136,46.72716],[48.52326,47.4102],[48.45173,47.40818],[48.15348,47.74545],[47.64973,47.76559],[47.41689,47.83687],[47.38731,47.68176],[47.12107,47.83687],[47.11516,48.27188],[46.49011,48.43019],[46.78392,48.95352],[46.91104,48.99715],[47.01458,49.07085],[47.04416,49.17152],[46.98795,49.23531],[46.78398,49.34026],[46.9078,49.86707],[47.18319,49.93721],[47.34589,50.09308],[47.30448,50.30894],[47.58551,50.47867],[48.10044,50.09242],[48.24519,49.86099],[48.42564,49.82283],[48.68352,49.89546],[48.90782,50.02281],[48.57946,50.63278],[48.86936,50.61589],[49.12673,50.78639],[49.41959,50.85927],[49.39001,51.09396],[49.76866,51.11067],[49.97277,51.2405],[50.26859,51.28677],[50.59695,51.61859],[51.26254,51.68466],[51.301,51.48799],[51.77431,51.49536],[51.8246,51.67916],[52.36119,51.74161],[52.54329,51.48444],[53.46165,51.49445],[53.69299,51.23466],[54.12248,51.11542],[54.46331,50.85554],[54.41894,50.61214],[54.55797,50.52006],[54.71476,50.61214],[54.56685,51.01958],[54.72067,51.03261],[55.67774,50.54508],[56.11398,50.7471],[56.17906,50.93204],[57.17302,51.11253],[57.44221,50.88354],[57.74986,50.93017],[57.75578,51.13852],[58.3208,51.15151],[58.87974,50.70852],[59.48928,50.64216],[59.51886,50.49937],[59.81172,50.54451],[60.01288,50.8163],[60.17262,50.83312],[60.31914,50.67705],[60.81833,50.6629],[61.4431,50.80679],[61.56889,51.23679],[61.6813,51.25716],[61.55114,51.32746],[61.50677,51.40687],[60.95655,51.48615],[60.92401,51.61124],[60.5424,51.61675],[60.36787,51.66815],[60.50986,51.7964],[60.09867,51.87135],[59.99809,51.98263],[60.19925,51.99173],[60.48915,52.15175],[60.72581,52.15538],[60.78201,52.22067],[61.05417,52.35096],[60.98021,52.50068],[60.84709,52.52228],[60.84118,52.63912],[60.71693,52.66245],[60.71989,52.75923],[61.05842,52.92217],[61.23462,53.03227],[62.0422,52.96105],[62.12799,52.99133],[62.14574,53.09626],[61.19024,53.30536],[61.14291,53.41481],[61.29082,53.50992],[61.37957,53.45887],[61.57185,53.50112],[61.55706,53.57144],[60.90626,53.62937],[61.22574,53.80268],[61.14283,53.90063],[60.99796,53.93699],[61.26863,53.92797],[61.3706,54.08464],[61.47603,54.08048],[61.56941,53.95703],[61.65318,54.02445],[62.03913,53.94768],[62.00966,54.04134],[62.38535,54.03961],[62.45931,53.90737],[62.56876,53.94047],[62.58651,54.05871],[63.80604,54.27079],[63.91224,54.20013],[64.02715,54.22679],[63.97686,54.29763],[64.97216,54.4212],[65.11033,54.33028],[65.24663,54.35721],[65.20174,54.55216],[68.21308,54.98645],[68.26661,55.09226],[68.19206,55.18823],[68.90865,55.38148],[69.34224,55.36344],[69.74917,55.35545],[70.19179,55.1476],[70.76493,55.3027],[70.96009,55.10558],[71.08288,54.71253],[71.24185,54.64965],[71.08706,54.33376],[71.10379,54.13326],[71.96141,54.17736],[72.17477,54.36303],[72.43415,53.92685],[72.71026,54.1161],[73.37963,53.96132],[73.74778,54.07194],[73.68921,53.86522],[73.25412,53.61532],[73.39218,53.44623],[75.07405,53.80831],[75.43398,53.98652],[75.3668,54.07439],[76.91052,54.4677],[76.82266,54.1798],[76.44076,54.16017],[76.54243,53.99329],[77.90383,53.29807],[79.11255,52.01171],[80.08138,50.77658],[80.4127,50.95581],[80.44819,51.20855],[80.80318,51.28262],[81.16999,51.15662],[81.06091,50.94833],[81.41248,50.97524],[81.46581,50.77658],[81.94999,50.79307],[82.55443,50.75412],[83.14607,51.00796],[83.8442,50.87375],[84.29385,50.27257],[84.99198,50.06793],[85.24047,49.60239],[86.18709,49.50259],[86.63674,49.80136],[86.79056,49.74787],[86.61307,49.60239],[86.82606,49.51796],[87.03071,49.25142],[87.31465,49.23603],[87.28386,49.11626],[87.478,49.07403],[87.48983,49.13794],[87.81333,49.17354],[87.98977,49.18147],[88.15543,49.30314],[88.17223,49.46934],[88.42449,49.48821],[88.82499,49.44808],[89.70687,49.72535],[89.59711,49.90851],[91.86048,50.73734],[92.07173,50.69585],[92.44714,50.78762],[93.01109,50.79001],[92.99595,50.63183],[94.30823,50.57498],[94.39258,50.22193],[94.49477,50.17832],[94.6121,50.04239],[94.97166,50.04725],[95.02465,49.96941],[95.74757,49.97915],[95.80056,50.04239],[96.97388,49.88413],[97.24639,49.74737],[97.56811,49.84265],[97.56432,49.92801],[97.76871,49.99861],[97.85197,49.91339],[98.29481,50.33561],[98.31373,50.4996],[98.06393,50.61262],[97.9693,50.78044],[98.01472,50.86652],[97.83305,51.00248],[98.05257,51.46696],[98.22053,51.46579],[98.33222,51.71832],[98.74142,51.8637],[98.87768,52.14563],[99.27888,51.96876],[99.75578,51.90108],[99.89203,51.74903],[100.61116,51.73028],[101.39085,51.45753],[101.5044,51.50467],[102.14032,51.35566],[102.32194,50.67982],[102.71178,50.38873],[103.70343,50.13952],[105.32528,50.4648],[106.05562,50.40582],[106.07865,50.33474],[106.47156,50.31909],[106.49628,50.32436],[106.51122,50.34408],[106.58373,50.34044],[106.80326,50.30177],[107.00007,50.1977],[107.1174,50.04239],[107.36407,49.97612],[107.96116,49.93191],[107.95387,49.66659],[108.27937,49.53167],[108.53969,49.32325],[109.18017,49.34709],[109.51325,49.22859],[110.24373,49.16676],[110.39891,49.25083],[110.64493,49.1816],[113.02647,49.60772],[113.20216,49.83356],[114.325,50.28098],[114.9703,50.19254],[115.26068,49.97367],[115.73602,49.87688],[116.22402,50.04477],[116.62502,49.92919],[116.71193,49.83813],[117.07142,49.68482],[117.27597,49.62544],[117.48208,49.62324],[117.82343,49.52696],[118.61623,49.93809],[119.11003,50.00276],[119.27996,50.13348],[119.38598,50.35162],[119.13553,50.37412],[120.10963,51.671],[120.65907,51.93544],[120.77337,52.20805],[120.61346,52.32447],[120.71673,52.54099],[120.46454,52.63811],[120.04049,52.58773],[120.0451,52.7359],[120.85633,53.28499],[121.39213,53.31888],[122.35063,53.49565],[122.85966,53.47395],[123.26989,53.54843],[123.86158,53.49391],[124.46078,53.21881],[125.17522,53.20225],[125.6131,53.07229],[126.558,52.13738],[126.44606,51.98254],[126.68349,51.70607],[126.90369,51.3238],[126.93135,51.0841],[127.14586,50.91152],[127.28165,50.72075],[127.36335,50.58306],[127.28765,50.46585],[127.36009,50.43787],[127.37384,50.28393],[127.60515,50.23503],[127.49299,50.01251],[127.53516,49.84306],[127.83476,49.5748],[128.72896,49.58676],[129.11153,49.36813],[129.23232,49.40353],[129.35317,49.3481],[129.40398,49.44194],[129.50685,49.42398],[129.67598,49.29596],[129.85416,49.11067],[130.2355,48.86741],[130.43232,48.90844],[130.66946,48.88251],[130.52147,48.61745],[130.84462,48.30942],[130.65103,48.10052],[130.90915,47.90623],[130.95985,47.6957],[131.09871,47.6852],[131.2635,47.73325],[131.90448,47.68011],[132.57309,47.71741],[132.66989,47.96491],[134.49516,48.42884],[134.75328,48.36763],[134.67098,48.1564],[134.55508,47.98651],[134.7671,47.72051],[134.50898,47.4812],[134.20016,47.33458],[134.03538,46.75668],[133.84104,46.46681],[133.91496,46.4274],[133.88097,46.25066],[133.68047,46.14697],[133.72695,46.05576],[133.67569,45.9759],[133.60442,45.90053],[133.48457,45.86203],[133.41083,45.57723],[133.19419,45.51913],[133.09279,45.25693],[133.12293,45.1332],[132.96373,45.0212],[132.83978,45.05916],[131.99417,45.2567],[131.86903,45.33636],[131.76532,45.22609],[131.66852,45.2196],[131.68466,45.12374],[131.48415,44.99513],[130.95639,44.85154],[131.1108,44.70266],[131.30365,44.04262],[131.25484,44.03131],[131.23583,43.96085],[131.26176,43.94011],[131.21105,43.82383],[131.19492,43.53047],[131.29402,43.46695],[131.30324,43.39498],[131.19031,43.21385],[131.20414,43.13654],[131.10274,43.04734],[131.135,42.94114],[131.02668,42.91246],[131.02438,42.86518],[130.66524,42.84753],[130.44361,42.76205],[130.40213,42.70788],[130.56576,42.68925],[130.62107,42.58413],[130.55143,42.52158],[130.56835,42.43281],[130.60805,42.4317],[130.64181,42.41422],[130.66367,42.38024],[130.65022,42.32281],[131.95041,41.5445],[140.9182,45.92937],[145.82343,44.571],[145.23667,43.76813],[153.94307,38.42848],[180,62.52334],[180,71.53642],[155.31937,81.93282],[36.48095,82.16765],[32.07813,72.01005],[31.59909,70.16571],[30.84095,69.80584],[30.95011,69.54699],[30.52662,69.54699],[30.16363,69.65244],[29.97205,69.41623],[29.27631,69.2811],[29.26623,69.13794],[29.0444,69.0119],[28.91738,69.04774],[28.45957,68.91417],[28.78224,68.86696],[28.43941,68.53366],[28.62982,68.19816],[29.34179,68.06655],[29.66955,67.79872],[30.02041,67.67523],[29.91155,67.51507],[28.9839,66.94139],[29.91155,66.13863],[30.16363,65.66935],[29.97205,65.70256],[29.74013,65.64025],[29.84096,65.56945],[29.68972,65.31803],[29.61914,65.23791],[29.8813,65.22101],[29.84096,65.1109],[29.61914,65.05993],[29.68972,64.80789],[30.05271,64.79072],[30.12329,64.64862],[30.01238,64.57513],[30.06279,64.35782],[30.4762,64.25728],[30.55687,64.09036],[30.25437,63.83364],[29.98213,63.75795],[30.49637,63.46666],[31.23244,63.22239],[31.29294,63.09035],[31.58535,62.91642],[31.38369,62.66284],[31.10136,62.43042],[29.01829,61.17448],[28.82816,61.1233],[28.47974,60.93365],[27.77352,60.52722],[27.71177,60.3893],[27.44953,60.22766],[26.32936,60.00121],[26.90044,59.63819],[27.85643,59.58538],[28.04187,59.47017],[28.19061,59.39962],[28.21137,59.38058],[28.20537,59.36491],[28.19284,59.35791],[28.14215,59.28934],[28.00689,59.28351],[27.90911,59.24353],[27.87978,59.18097],[27.80482,59.1116],[27.74429,58.98351],[27.36366,58.78381],[27.55489,58.39525],[27.48541,58.22615],[27.62393,58.09462],[27.67282,57.92627],[27.81841,57.89244],[27.78526,57.83963],[27.56689,57.83356],[27.50171,57.78842],[27.52615,57.72843],[27.3746,57.66834],[27.40393,57.62125],[27.31919,57.57672],[27.34698,57.52242],[27.56832,57.53728],[27.52453,57.42826],[27.86101,57.29402],[27.66511,56.83921],[27.86101,56.88204],[28.04768,56.59004],[28.13526,56.57989],[28.10069,56.524],[28.19057,56.44637],[28.16599,56.37806],[28.23716,56.27588],[28.15217,56.16964],[28.30571,56.06035],[28.36888,56.05805],[28.37987,56.11399],[28.43068,56.09407],[28.5529,56.11705],[28.68337,56.10173],[28.63668,56.07262],[28.73418,55.97131],[29.08299,56.03427],[29.21717,55.98971],[29.44692,55.95978],[29.3604,55.75862],[29.51283,55.70294],[29.61446,55.77716],[29.80672,55.79569],[29.97975,55.87281],[30.12136,55.8358],[30.27776,55.86819],[30.30987,55.83592],[30.48257,55.81066],[30.51346,55.78982],[30.51037,55.76568],[30.63344,55.73079],[30.67464,55.64176],[30.72957,55.66268],[30.7845,55.58514],[30.86003,55.63169],[30.93419,55.6185],[30.95204,55.50667],[30.90123,55.46621],[30.93144,55.3914],[30.8257,55.3313],[30.81946,55.27931],[30.87944,55.28223],[30.97369,55.17134],[31.02071,55.06167],[31.00972,55.02783],[30.94243,55.03964],[30.9081,55.02232],[30.95754,54.98609],[30.93144,54.9585],[30.81759,54.94064],[30.8264,54.90062],[30.75165,54.80699],[30.95479,54.74346],[30.97127,54.71967],[31.0262,54.70698],[30.98226,54.68872],[30.99187,54.67046],[31.19339,54.66947],[31.21399,54.63113],[31.08543,54.50361],[31.22945,54.46585],[31.3177,54.34067],[31.30791,54.25315],[31.57002,54.14535],[31.89599,54.0837],[31.88744,54.03653],[31.85019,53.91801],[31.77028,53.80015],[31.89137,53.78099],[32.12621,53.81586],[32.36663,53.7166],[32.45717,53.74039],[32.50112,53.68594],[32.40499,53.6656],[32.47777,53.5548],[32.74968,53.45597],[32.73257,53.33494],[32.51725,53.28431],[32.40773,53.18856],[32.15368,53.07594],[31.82373,53.10042],[31.787,53.18033],[31.62496,53.22886],[31.56316,53.19432],[31.40523,53.21406],[31.36403,53.13504],[31.3915,53.09712],[31.33519,53.08805],[31.32283,53.04101],[31.24147,53.031],[31.35667,52.97854],[31.592,52.79011],[31.57277,52.71613],[31.50406,52.69707],[31.63869,52.55361],[31.56316,52.51518],[31.61397,52.48843],[31.62084,52.33849],[31.57971,52.32146],[31.70735,52.26711],[31.6895,52.1973],[31.77877,52.18636],[31.7822,52.11406],[31.81722,52.09955],[31.85018,52.11305],[31.96141,52.08015],[31.92159,52.05144],[32.08813,52.03319],[32.23331,52.08085],[32.2777,52.10266],[32.34044,52.1434],[32.33083,52.23685],[32.38988,52.24946],[32.3528,52.32842],[32.54781,52.32423],[32.69475,52.25535],[32.85405,52.27888],[32.89937,52.2461],[33.18913,52.3754],[33.51323,52.35779],[33.48027,52.31499],[33.55718,52.30324],[33.78789,52.37204],[34.05239,52.20132],[34.11199,52.14087],[34.09413,52.00835],[34.41136,51.82793],[34.42922,51.72852],[34.07765,51.67065],[34.17599,51.63253],[34.30562,51.5205],[34.22048,51.4187],[34.33446,51.363],[34.23009,51.26429],[34.31661,51.23936],[34.38802,51.2746],[34.6613,51.25053],[34.6874,51.18],[34.82472,51.17483],[34.97304,51.2342],[35.14058,51.23162],[35.12685,51.16191],[35.20375,51.04723],[35.31774,51.08434],[35.40837,51.04119],[35.32598,50.94524],[35.39307,50.92145],[35.41367,50.80227],[35.47704,50.77274],[35.48116,50.66405],[35.39464,50.64751],[35.47463,50.49247],[35.58003,50.45117],[35.61711,50.35707],[35.73659,50.35489],[35.80388,50.41356],[35.8926,50.43829],[36.06893,50.45205],[36.20763,50.3943],[36.30101,50.29088],[36.47817,50.31457],[36.58371,50.28563],[36.56655,50.2413],[36.64571,50.218],[36.69377,50.26982],[36.91762,50.34963],[37.08468,50.34935],[37.48204,50.46079],[37.47243,50.36277],[37.62486,50.29966],[37.62879,50.24481],[37.61113,50.21976],[37.75807,50.07896],[37.79515,50.08425],[37.90776,50.04194],[38.02999,49.94482],[38.02999,49.90592],[38.21675,49.98104],[38.18517,50.08161],[38.32524,50.08866],[38.35408,50.00664],[38.65688,49.97176],[38.68677,50.00904],[38.73311,49.90238],[38.90477,49.86787],[38.9391,49.79524],[39.1808,49.88911],[39.27968,49.75976],[39.44496,49.76067],[39.59142,49.73758],[39.65047,49.61761],[39.84548,49.56064],[40.13249,49.61672],[40.16683,49.56865],[40.03636,49.52321],[40.03087,49.45452],[40.1141,49.38798],[40.14912,49.37681],[40.18331,49.34996],[40.22176,49.25683],[40.01988,49.1761],[39.93437,49.05709],[39.6836,49.05121],[39.6683,48.99454],[39.71353,48.98959],[39.72649,48.9754],[39.74874,48.98675],[39.78368,48.91596],[39.98967,48.86901],[40.03636,48.91957],[40.08168,48.87443],[39.97182,48.79398],[39.79466,48.83739],[39.73104,48.7325],[39.71765,48.68673],[39.67226,48.59368],[39.79764,48.58668],[39.84548,48.57821],[39.86196,48.46633],[39.88794,48.44226],[39.94847,48.35055],[39.84136,48.33321],[39.84273,48.30947],[39.90041,48.3049],[39.91465,48.26743],[39.95248,48.29972],[39.9693,48.29904],[39.97325,48.31399],[39.99241,48.31768],[40.00752,48.22445],[39.94847,48.22811],[39.83724,48.06501],[39.88256,48.04482],[39.77544,48.04206],[39.82213,47.96396],[39.73935,47.82876],[38.87979,47.87719],[38.79628,47.81109],[38.76379,47.69346],[38.35062,47.61631],[38.28679,47.53552],[38.28954,47.39255],[38.22225,47.30788],[38.33074,47.30508],[38.32112,47.2585],[38.23049,47.2324],[38.22955,47.12069],[38.3384,46.98085],[38.12112,46.86078],[37.62608,46.82615],[35.23066,45.79231],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633],[32.99857,44.48323],[33.66142,43.9825],[39.81147,43.06294]]],[[[21.46766,55.21115],[21.38446,55.29348],[21.35465,55.28427],[21.26425,55.24456],[20.95181,55.27994],[20.60454,55.40986],[18.57853,55.25302],[19.64312,54.45423],[19.8038,54.44203],[20.63871,54.3706],[21.41123,54.32395],[22.79705,54.36264],[22.7253,54.41732],[22.70208,54.45312],[22.67788,54.532],[22.71293,54.56454],[22.68021,54.58486],[22.7522,54.63525],[22.74225,54.64339],[22.75467,54.6483],[22.73397,54.66604],[22.73631,54.72952],[22.87317,54.79492],[22.85083,54.88711],[22.76422,54.92521],[22.68723,54.9811],[22.65451,54.97037],[22.60075,55.01863],[22.58907,55.07085],[22.47688,55.04408],[22.31562,55.0655],[22.14267,55.05345],[22.11697,55.02131],[22.06087,55.02935],[22.02582,55.05078],[22.03984,55.07888],[21.99543,55.08691],[21.96505,55.07353],[21.85521,55.09493],[21.64954,55.1791],[21.55605,55.20311],[21.51095,55.18507],[21.46766,55.21115]]]]}},{type:"Feature",properties:{iso1A2:"RW",iso1A3:"RWA",iso1N3:"646",wikidata:"Q1037",nameEn:"Rwanda",groups:["014","202","002"],callingCodes:["250"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.47194,-1.0555],[30.35212,-1.06896],[30.16369,-1.34303],[29.912,-1.48269],[29.82657,-1.31187],[29.59061,-1.39016],[29.53062,-1.40499],[29.45038,-1.5054],[29.36322,-1.50887],[29.24323,-1.66826],[29.24458,-1.69663],[29.11847,-1.90576],[29.17562,-2.12278],[29.105,-2.27043],[29.00051,-2.29001],[28.95642,-2.37321],[28.89601,-2.37321],[28.86826,-2.41888],[28.86846,-2.44866],[28.89132,-2.47557],[28.89342,-2.49017],[28.88846,-2.50493],[28.87497,-2.50887],[28.86209,-2.5231],[28.86193,-2.53185],[28.87943,-2.55165],[28.89288,-2.55848],[28.90226,-2.62385],[28.89793,-2.66111],[28.94346,-2.69124],[29.00357,-2.70596],[29.04081,-2.7416],[29.0562,-2.58632],[29.32234,-2.6483],[29.36805,-2.82933],[29.88237,-2.75105],[29.95911,-2.33348],[30.14034,-2.43626],[30.42933,-2.31064],[30.54501,-2.41404],[30.83915,-2.35795],[30.89303,-2.08223],[30.80802,-1.91477],[30.84079,-1.64652],[30.71974,-1.43244],[30.57123,-1.33264],[30.50889,-1.16412],[30.45116,-1.10641],[30.47194,-1.0555]]]]}},{type:"Feature",properties:{iso1A2:"SA",iso1A3:"SAU",iso1N3:"682",wikidata:"Q851",nameEn:"Saudi Arabia",groups:["145","142"],callingCodes:["966"]},geometry:{type:"MultiPolygon",coordinates:[[[[40.01521,32.05667],[39.29903,32.23259],[38.99233,31.99721],[36.99791,31.50081],[37.99354,30.49998],[37.66395,30.33245],[37.4971,29.99949],[36.75083,29.86903],[36.50005,29.49696],[36.07081,29.18469],[34.95987,29.35727],[34.88293,29.37455],[34.46254,27.99552],[34.51305,27.70027],[37.8565,22.00903],[39.63762,18.37348],[41.37609,16.19728],[42.15205,16.40211],[42.76801,16.40371],[42.94625,16.39721],[42.94351,16.49467],[42.97215,16.51093],[43.11601,16.53166],[43.15274,16.67248],[43.22066,16.65179],[43.21325,16.74416],[43.25857,16.75304],[43.26303,16.79479],[43.24801,16.80613],[43.22956,16.80613],[43.22012,16.83932],[43.18338,16.84852],[43.1398,16.90696],[43.19328,16.94703],[43.1813,16.98438],[43.18233,17.02673],[43.23967,17.03428],[43.17787,17.14717],[43.20156,17.25901],[43.32653,17.31179],[43.22533,17.38343],[43.29185,17.53224],[43.43005,17.56148],[43.70631,17.35762],[44.50126,17.47475],[46.31018,17.20464],[46.76494,17.29151],[47.00571,16.94765],[47.48245,17.10808],[47.58351,17.50366],[48.19996,18.20584],[49.04884,18.59899],[52.00311,19.00083],[54.99756,20.00083],[55.66469,21.99658],[55.2137,22.71065],[55.13599,22.63334],[52.56622,22.94341],[51.59617,24.12041],[51.58871,24.27256],[51.41644,24.39615],[51.58834,24.66608],[51.39468,24.62785],[51.29972,24.50747],[51.09638,24.46907],[50.92992,24.54396],[50.8133,24.74049],[50.57069,25.57887],[50.302,25.87592],[50.26923,26.08243],[50.38162,26.53976],[50.71771,26.73086],[50.37726,27.89227],[49.98877,27.87827],[49.00421,28.81495],[48.42991,28.53628],[47.70561,28.5221],[47.59863,28.66798],[47.58376,28.83382],[47.46202,29.0014],[46.5527,29.10283],[46.42415,29.05947],[44.72255,29.19736],[42.97796,30.48295],[42.97601,30.72204],[40.01521,32.05667]]]]}},{type:"Feature",properties:{iso1A2:"SB",iso1A3:"SLB",iso1N3:"090",wikidata:"Q685",nameEn:"Solomon Islands",groups:["054","009"],driveSide:"left",callingCodes:["677"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-12.72535],[160.43769,-4.17974],[156.03296,-6.55528],[156.03993,-6.65703],[155.92557,-6.84664],[155.69784,-6.92661],[155.60735,-6.92266],[154.74815,-7.33315],[160.04026,-13.08769],[174,-12.72535]]]]}},{type:"Feature",properties:{iso1A2:"SC",iso1A3:"SYC",iso1N3:"690",wikidata:"Q1042",nameEn:"Seychelles",groups:["014","202","002"],driveSide:"left",callingCodes:["248"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.75112,-10.38913],[54.83239,-10.93575],[66.3222,5.65313],[43.75112,-10.38913]]]]}},{type:"Feature",properties:{iso1A2:"SD",iso1A3:"SDN",iso1N3:"729",wikidata:"Q1049",nameEn:"Sudan",groups:["015","002"],callingCodes:["249"]},geometry:{type:"MultiPolygon",coordinates:[[[[37.8565,22.00903],[34.0765,22.00501],[33.99686,21.76784],[33.57251,21.72406],[33.17563,22.00405],[24.99885,21.99535],[24.99794,19.99661],[23.99715,20.00038],[23.99539,19.49944],[23.99997,15.69575],[23.62785,15.7804],[23.38812,15.69649],[23.10792,15.71297],[22.93201,15.55107],[22.92579,15.47007],[22.99584,15.40105],[22.99584,15.22989],[22.66115,14.86308],[22.70474,14.69149],[22.38562,14.58907],[22.44944,14.24986],[22.55997,14.23024],[22.5553,14.11704],[22.22995,13.96754],[22.08674,13.77863],[22.29689,13.3731],[22.1599,13.19281],[22.02914,13.13976],[21.94819,13.05637],[21.81432,12.81362],[21.89371,12.68001],[21.98711,12.63292],[22.15679,12.66634],[22.22684,12.74682],[22.46345,12.61925],[22.38873,12.45514],[22.50548,12.16769],[22.48369,12.02766],[22.64092,12.07485],[22.54907,11.64372],[22.7997,11.40424],[22.93124,11.41645],[22.97249,11.21955],[22.87758,10.91915],[23.02221,10.69235],[23.3128,10.45214],[23.67164,9.86923],[23.69155,9.67566],[24.09319,9.66572],[24.12744,9.73784],[24.49389,9.79962],[24.84653,9.80643],[24.97739,9.9081],[25.05688,10.06776],[25.0918,10.33718],[25.78141,10.42599],[25.93163,10.38159],[25.93241,10.17941],[26.21338,9.91545],[26.35815,9.57946],[26.70685,9.48735],[27.14427,9.62858],[27.90704,9.61323],[28.99983,9.67155],[29.06988,9.74826],[29.53844,9.75133],[29.54,10.07949],[29.94629,10.29245],[30.00389,10.28633],[30.53005,9.95992],[30.82893,9.71451],[30.84605,9.7498],[31.28504,9.75287],[31.77539,10.28939],[31.99177,10.65065],[32.46967,11.04662],[32.39358,11.18207],[32.39578,11.70208],[32.10079,11.95203],[32.73921,11.95203],[32.73921,12.22757],[33.25876,12.22111],[33.13988,11.43248],[33.26977,10.83632],[33.24645,10.77913],[33.52294,10.64382],[33.66604,10.44254],[33.80913,10.32994],[33.90159,10.17179],[33.96984,10.15446],[33.99185,9.99623],[33.96323,9.80972],[33.9082,9.762],[33.87958,9.49937],[34.10229,9.50238],[34.08717,9.55243],[34.13186,9.7492],[34.20484,9.9033],[34.22718,10.02506],[34.32102,10.11599],[34.34783,10.23914],[34.2823,10.53508],[34.4372,10.781],[34.59062,10.89072],[34.77383,10.74588],[34.77532,10.69027],[34.86618,10.74588],[34.86916,10.78832],[34.97491,10.86147],[34.97789,10.91559],[34.93172,10.95946],[35.01215,11.19626],[34.95704,11.24448],[35.09556,11.56278],[35.05832,11.71158],[35.11492,11.85156],[35.24302,11.91132],[35.70476,12.67101],[36.01458,12.72478],[36.14268,12.70879],[36.16651,12.88019],[36.13374,12.92665],[36.24545,13.36759],[36.38993,13.56459],[36.48824,13.83954],[36.44653,13.95666],[36.54376,14.25597],[36.44337,15.14963],[36.54276,15.23478],[36.69761,15.75323],[36.76371,15.80831],[36.92193,16.23451],[36.99777,17.07172],[37.42694,17.04041],[37.50967,17.32199],[38.13362,17.53906],[38.37133,17.66269],[38.45916,17.87167],[38.57727,17.98125],[39.63762,18.37348],[37.8565,22.00903]]]]}},{type:"Feature",properties:{iso1A2:"SE",iso1A3:"SWE",iso1N3:"752",wikidata:"Q34",nameEn:"Sweden",groups:["EU","154","150"],callingCodes:["46"]},geometry:{type:"MultiPolygon",coordinates:[[[[24.15791,65.85385],[23.90497,66.15802],[23.71339,66.21299],[23.64982,66.30603],[23.67591,66.3862],[23.63776,66.43568],[23.85959,66.56434],[23.89488,66.772],[23.98059,66.79585],[23.98563,66.84149],[23.56214,67.17038],[23.58735,67.20752],[23.54701,67.25435],[23.75372,67.29914],[23.75372,67.43688],[23.39577,67.46974],[23.54701,67.59306],[23.45627,67.85297],[23.65793,67.9497],[23.40081,68.05545],[23.26469,68.15134],[23.15377,68.14759],[23.10336,68.26551],[22.73028,68.40881],[22.00429,68.50692],[21.03001,68.88969],[20.90649,68.89696],[20.85104,68.93142],[20.91658,68.96764],[20.78802,69.03087],[20.55258,69.06069],[20.0695,69.04469],[20.28444,68.93283],[20.33435,68.80174],[20.22027,68.67246],[19.95647,68.55546],[20.22027,68.48759],[19.93508,68.35911],[18.97255,68.52416],[18.63032,68.50849],[18.39503,68.58672],[18.1241,68.53721],[18.13836,68.20874],[17.90787,67.96537],[17.30416,68.11591],[16.7409,67.91037],[16.38441,67.52923],[16.12774,67.52106],[16.09922,67.4364],[16.39154,67.21653],[16.35589,67.06419],[15.37197,66.48217],[15.49318,66.28509],[15.05113,66.15572],[14.53778,66.12399],[14.50926,65.31786],[13.64276,64.58402],[14.11117,64.46674],[14.16051,64.18725],[13.98222,64.00953],[13.23411,64.09087],[12.74105,64.02171],[12.14928,63.59373],[12.19919,63.47935],[11.98529,63.27487],[12.19919,63.00104],[12.07085,62.6297],[12.29187,62.25699],[12.14746,61.7147],[12.40595,61.57226],[12.57707,61.56547],[12.86939,61.35427],[12.69115,61.06584],[12.2277,61.02442],[12.59133,60.50559],[12.52003,60.13846],[12.36317,59.99259],[12.15641,59.8926],[11.87121,59.86039],[11.92112,59.69531],[11.69297,59.59442],[11.8213,59.24985],[11.65732,58.90177],[11.45199,58.89604],[11.4601,58.99022],[11.34459,59.11672],[11.15367,59.07862],[11.08911,58.98745],[10.64958,58.89391],[10.40861,58.38489],[12.16597,56.60205],[12.07466,56.29488],[12.65312,56.04345],[12.6372,55.91371],[12.88472,55.63369],[12.60345,55.42675],[12.84405,55.13257],[14.28399,55.1553],[14.89259,55.5623],[15.79951,55.54655],[19.64795,57.06466],[19.84909,57.57876],[20.5104,59.15546],[19.08191,60.19152],[19.23413,60.61414],[20.15877,63.06556],[24.14112,65.39731],[24.15107,65.81427],[24.14798,65.83466],[24.15791,65.85385]]]]}},{type:"Feature",properties:{iso1A2:"SG",iso1A3:"SGP",iso1N3:"702",wikidata:"Q334",nameEn:"Singapore",groups:["035","142"],driveSide:"left",callingCodes:["65"]},geometry:{type:"MultiPolygon",coordinates:[[[[104.00131,1.42405],[103.93384,1.42926],[103.89565,1.42841],[103.86383,1.46288],[103.81181,1.47953],[103.76395,1.45183],[103.74161,1.4502],[103.7219,1.46108],[103.67468,1.43166],[103.62738,1.35255],[103.56591,1.19719],[103.66049,1.18825],[103.74084,1.12902],[104.03085,1.26954],[104.12282,1.27714],[104.08072,1.35998],[104.09162,1.39694],[104.08871,1.42015],[104.07348,1.43322],[104.04622,1.44691],[104.02277,1.4438],[104.00131,1.42405]]]]}},{type:"Feature",properties:{iso1A2:"SH",iso1A3:"SHN",iso1N3:"654",wikidata:"Q34497",nameEn:"Saint Helena, Ascension and Tristan da Cunha",country:"GB",groups:["011","202","002"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.48367,-36.6746],[-11.55782,-36.60319],[-11.48092,-37.8367],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"SI",iso1A3:"SVN",iso1N3:"705",wikidata:"Q215",nameEn:"Slovenia",groups:["EU","039","150"],callingCodes:["386"]},geometry:{type:"MultiPolygon",coordinates:[[[[16.50139,46.56684],[16.39217,46.63673],[16.38594,46.6549],[16.41863,46.66238],[16.42641,46.69228],[16.37816,46.69975],[16.30966,46.7787],[16.31303,46.79838],[16.3408,46.80641],[16.34547,46.83836],[16.2941,46.87137],[16.2365,46.87775],[16.21892,46.86961],[16.15711,46.85434],[16.14365,46.8547],[16.10983,46.867],[16.05786,46.83927],[15.99054,46.82772],[15.99126,46.78199],[15.98432,46.74991],[15.99769,46.7266],[16.02808,46.71094],[16.04347,46.68694],[16.04036,46.6549],[15.99988,46.67947],[15.98512,46.68463],[15.94864,46.68769],[15.87691,46.7211],[15.8162,46.71897],[15.78518,46.70712],[15.76771,46.69863],[15.73823,46.70011],[15.72279,46.69548],[15.69523,46.69823],[15.67411,46.70735],[15.6543,46.70616],[15.6543,46.69228],[15.6365,46.6894],[15.63255,46.68069],[15.62317,46.67947],[15.59826,46.68908],[15.54533,46.66985],[15.55333,46.64988],[15.54431,46.6312],[15.46906,46.61321],[15.45514,46.63697],[15.41235,46.65556],[15.23711,46.63994],[15.14215,46.66131],[15.01451,46.641],[14.98024,46.6009],[14.96002,46.63459],[14.92283,46.60848],[14.87129,46.61],[14.86419,46.59411],[14.83549,46.56614],[14.81836,46.51046],[14.72185,46.49974],[14.66892,46.44936],[14.5942,46.43434],[14.56463,46.37208],[14.52176,46.42617],[14.45877,46.41717],[14.42608,46.44614],[14.314,46.43327],[14.28326,46.44315],[14.15989,46.43327],[14.12097,46.47724],[14.04002,46.49117],[14.00422,46.48474],[13.89837,46.52331],[13.7148,46.5222],[13.68684,46.43881],[13.59777,46.44137],[13.5763,46.42613],[13.5763,46.40915],[13.47019,46.3621],[13.43418,46.35992],[13.44808,46.33507],[13.37671,46.29668],[13.42218,46.20758],[13.47587,46.22725],[13.56114,46.2054],[13.56682,46.18703],[13.64451,46.18966],[13.66472,46.17392],[13.64053,46.13587],[13.57072,46.09022],[13.50104,46.05986],[13.49568,46.04839],[13.50998,46.04498],[13.49702,46.01832],[13.47474,46.00546],[13.50104,45.98078],[13.52963,45.96588],[13.56759,45.96991],[13.58903,45.99009],[13.62074,45.98388],[13.63458,45.98947],[13.64307,45.98326],[13.6329,45.94894],[13.63815,45.93607],[13.61931,45.91782],[13.60857,45.89907],[13.59565,45.89446],[13.58644,45.88173],[13.57563,45.8425],[13.58858,45.83503],[13.59784,45.8072],[13.66986,45.79955],[13.8235,45.7176],[13.83332,45.70855],[13.83422,45.68703],[13.87933,45.65207],[13.9191,45.6322],[13.8695,45.60835],[13.86771,45.59898],[13.84106,45.58185],[13.78445,45.5825],[13.74587,45.59811],[13.7198,45.59352],[13.6076,45.64761],[13.45644,45.59464],[13.56979,45.4895],[13.62902,45.45898],[13.67398,45.4436],[13.7785,45.46787],[13.81742,45.43729],[13.88124,45.42637],[13.90771,45.45149],[13.97309,45.45258],[13.99488,45.47551],[13.96063,45.50825],[14.00578,45.52352],[14.07116,45.48752],[14.20348,45.46896],[14.22371,45.50388],[14.24239,45.50607],[14.26611,45.48239],[14.27681,45.4902],[14.32487,45.47142],[14.36693,45.48642],[14.49769,45.54424],[14.5008,45.60852],[14.53816,45.6205],[14.57397,45.67165],[14.60977,45.66403],[14.59576,45.62812],[14.69694,45.57366],[14.68605,45.53006],[14.71718,45.53442],[14.80124,45.49515],[14.81992,45.45913],[14.90554,45.47769],[14.92266,45.52788],[15.02385,45.48533],[15.05187,45.49079],[15.16862,45.42309],[15.27758,45.46678],[15.33051,45.45258],[15.38188,45.48752],[15.30249,45.53224],[15.29837,45.5841],[15.27747,45.60504],[15.31027,45.6303],[15.34695,45.63382],[15.34214,45.64702],[15.38952,45.63682],[15.4057,45.64727],[15.34919,45.71623],[15.30872,45.69014],[15.25423,45.72275],[15.40836,45.79491],[15.47531,45.79802],[15.47325,45.8253],[15.52234,45.82195],[15.57952,45.84953],[15.64185,45.82915],[15.66662,45.84085],[15.70411,45.8465],[15.68232,45.86819],[15.68383,45.88867],[15.67967,45.90455],[15.70636,45.92116],[15.70327,46.00015],[15.71246,46.01196],[15.72977,46.04682],[15.62317,46.09103],[15.6083,46.11992],[15.59909,46.14761],[15.64904,46.19229],[15.6434,46.21396],[15.67395,46.22478],[15.75436,46.21969],[15.75479,46.20336],[15.78817,46.21719],[15.79284,46.25811],[15.97965,46.30652],[16.07616,46.3463],[16.07314,46.36458],[16.05065,46.3833],[16.05281,46.39141],[16.14859,46.40547],[16.18824,46.38282],[16.30233,46.37837],[16.30162,46.40437],[16.27329,46.41467],[16.27398,46.42875],[16.25124,46.48067],[16.23961,46.49653],[16.26759,46.50566],[16.26733,46.51505],[16.29793,46.5121],[16.37193,46.55008],[16.38771,46.53608],[16.44036,46.5171],[16.5007,46.49644],[16.52604,46.47831],[16.59527,46.47524],[16.52604,46.5051],[16.52885,46.53303],[16.50139,46.56684]]]]}},{type:"Feature",properties:{iso1A2:"SJ",iso1A3:"SJM",iso1N3:"744",wikidata:"Q842829",nameEn:"Svalbard and Jan Mayen",country:"NO",groups:["154","150"],callingCodes:["47 79"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.49892,77.24208],[32.07813,72.01005],[36.85549,84.09565],[-7.49892,77.24208]]],[[[-9.18243,72.23144],[-10.71459,70.09565],[-5.93364,70.76368],[-9.18243,72.23144]]]]}},{type:"Feature",properties:{iso1A2:"SK",iso1A3:"SVK",iso1N3:"703",wikidata:"Q214",nameEn:"Slovakia",groups:["EU","151","150"],callingCodes:["421"]},geometry:{type:"MultiPolygon",coordinates:[[[[19.82237,49.27806],[19.78581,49.41701],[19.72127,49.39288],[19.6375,49.40897],[19.64162,49.45184],[19.57845,49.46077],[19.53313,49.52856],[19.52626,49.57311],[19.45348,49.61583],[19.37795,49.574],[19.36009,49.53747],[19.25435,49.53391],[19.18019,49.41165],[18.9742,49.39557],[18.97283,49.49914],[18.94536,49.52143],[18.84521,49.51672],[18.74761,49.492],[18.67757,49.50895],[18.6144,49.49824],[18.57183,49.51162],[18.53063,49.49022],[18.54848,49.47059],[18.44686,49.39467],[18.4084,49.40003],[18.4139,49.36517],[18.36446,49.3267],[18.18456,49.28909],[18.15022,49.24518],[18.1104,49.08624],[18.06885,49.03157],[17.91814,49.01784],[17.87831,48.92679],[17.77944,48.92318],[17.73126,48.87885],[17.7094,48.86721],[17.5295,48.81117],[17.45671,48.85004],[17.3853,48.80936],[17.29054,48.85546],[17.19355,48.87602],[17.11202,48.82925],[17.00215,48.70887],[16.93955,48.60371],[16.94611,48.53614],[16.85204,48.44968],[16.8497,48.38321],[16.83588,48.3844],[16.83317,48.38138],[16.84243,48.35258],[16.90903,48.32519],[16.89461,48.31332],[16.97701,48.17385],[17.02919,48.13996],[17.05735,48.14179],[17.09168,48.09366],[17.07039,48.0317],[17.16001,48.00636],[17.23699,48.02094],[17.71215,47.7548],[18.02938,47.75665],[18.29305,47.73541],[18.56496,47.76588],[18.66521,47.76772],[18.74074,47.8157],[18.8506,47.82308],[18.76821,47.87469],[18.76134,47.97499],[18.82176,48.04206],[19.01952,48.07052],[19.23924,48.0595],[19.28182,48.08336],[19.47957,48.09437],[19.52489,48.19791],[19.63338,48.25006],[19.92452,48.1283],[20.24312,48.2784],[20.29943,48.26104],[20.5215,48.53336],[20.83248,48.5824],[21.11516,48.49546],[21.44063,48.58456],[21.6068,48.50365],[21.67134,48.3989],[21.72525,48.34628],[21.8279,48.33321],[21.83339,48.36242],[22.14689,48.4005],[22.16023,48.56548],[22.21379,48.6218],[22.34151,48.68893],[22.42934,48.92857],[22.48296,48.99172],[22.54338,49.01424],[22.56155,49.08865],[22.04427,49.22136],[21.96385,49.3437],[21.82927,49.39467],[21.77983,49.35443],[21.62328,49.4447],[21.43376,49.41433],[21.27858,49.45988],[21.19756,49.4054],[21.12477,49.43666],[21.041,49.41791],[21.09799,49.37176],[20.98733,49.30774],[20.9229,49.29626],[20.77971,49.35383],[20.72274,49.41813],[20.61666,49.41791],[20.5631,49.375],[20.46422,49.41612],[20.39939,49.3896],[20.31728,49.39914],[20.31453,49.34817],[20.21977,49.35265],[20.13738,49.31685],[20.08238,49.1813],[19.98494,49.22904],[19.90529,49.23532],[19.86409,49.19316],[19.75286,49.20751],[19.82237,49.27806]]]]}},{type:"Feature",properties:{iso1A2:"SL",iso1A3:"SLE",iso1N3:"694",wikidata:"Q1044",nameEn:"Sierra Leone",groups:["011","202","002"],callingCodes:["232"]},geometry:{type:"MultiPolygon",coordinates:[[[[-10.27575,8.48711],[-10.37257,8.48941],[-10.54891,8.31174],[-10.63934,8.35326],[-10.70565,8.29235],[-10.61422,8.5314],[-10.47707,8.67669],[-10.56197,8.81225],[-10.5783,9.06386],[-10.74484,9.07998],[-10.6534,9.29919],[-11.2118,10.00098],[-11.89624,9.99763],[-11.91023,9.93927],[-12.12634,9.87203],[-12.24262,9.92386],[-12.47254,9.86834],[-12.76788,9.3133],[-12.94095,9.26335],[-13.08953,9.0409],[-13.18586,9.0925],[-13.29911,9.04245],[-14.36218,8.64107],[-12.15048,6.15992],[-11.50429,6.92704],[-11.4027,6.97746],[-11.29417,7.21576],[-10.60422,7.7739],[-10.60492,8.04072],[-10.57523,8.04829],[-10.51554,8.1393],[-10.45023,8.15627],[-10.35227,8.15223],[-10.29839,8.21283],[-10.31635,8.28554],[-10.30084,8.30008],[-10.27575,8.48711]]]]}},{type:"Feature",properties:{iso1A2:"SM",iso1A3:"SMR",iso1N3:"674",wikidata:"Q238",nameEn:"San Marino",groups:["039","150"],callingCodes:["378"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45648,43.89369],[12.48771,43.89706],[12.49429,43.90973],[12.49247,43.91774],[12.49724,43.92248],[12.50269,43.92363],[12.50496,43.93017],[12.51553,43.94096],[12.51427,43.94897],[12.50655,43.95796],[12.50875,43.96198],[12.50622,43.97131],[12.51109,43.97201],[12.51064,43.98165],[12.5154,43.98508],[12.51463,43.99122],[12.50678,43.99113],[12.49406,43.98492],[12.47853,43.98052],[12.46205,43.97463],[12.44684,43.96597],[12.43662,43.95698],[12.42005,43.9578],[12.41414,43.95273],[12.40415,43.95485],[12.40506,43.94325],[12.41165,43.93769],[12.41551,43.92984],[12.40733,43.92379],[12.41233,43.90956],[12.40935,43.9024],[12.41641,43.89991],[12.44184,43.90498],[12.45648,43.89369]]]]}},{type:"Feature",properties:{iso1A2:"SN",iso1A3:"SEN",iso1N3:"686",wikidata:"Q1041",nameEn:"Senegal",groups:["011","202","002"],callingCodes:["221"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.32144,16.61495],[-15.00557,16.64997],[-15.6509,16.50315],[-16.27016,16.51565],[-16.4429,16.20605],[-16.44814,16.09753],[-16.48967,16.0496],[-16.50854,16.09032],[-17.15288,16.07139],[-18.35085,14.63444],[-17.43598,13.59273],[-15.47902,13.58758],[-15.36504,13.79313],[-14.93719,13.80173],[-14.34721,13.46578],[-13.8955,13.59126],[-13.79409,13.34472],[-14.36795,13.23033],[-15.14917,13.57989],[-15.26908,13.37768],[-15.80478,13.34832],[-15.80355,13.16729],[-16.69343,13.16791],[-16.74676,13.06025],[-17.43966,13.04579],[-17.4623,11.92379],[-16.70562,12.34803],[-16.38191,12.36449],[-16.20591,12.46157],[-15.67302,12.42974],[-15.17582,12.6847],[-13.70523,12.68013],[-13.05296,12.64003],[-13.06603,12.49342],[-12.87336,12.51892],[-12.35415,12.32758],[-11.91331,12.42008],[-11.46267,12.44559],[-11.37536,12.40788],[-11.39935,12.97808],[-11.63025,13.39174],[-11.83345,13.33333],[-12.06897,13.71049],[-11.93043,13.84505],[-12.23936,14.76324],[-13.11029,15.52116],[-13.43135,16.09022],[-13.80075,16.13961],[-14.32144,16.61495]]]]}},{type:"Feature",properties:{iso1A2:"SO",iso1A3:"SOM",iso1N3:"706",wikidata:"Q1045",nameEn:"Somalia",groups:["014","202","002"],callingCodes:["252"]},geometry:{type:"MultiPolygon",coordinates:[[[[48.95249,11.56816],[43.42425,11.70983],[42.95776,10.98533],[42.69452,10.62672],[42.87643,10.18441],[43.0937,9.90579],[43.23518,9.84605],[43.32613,9.59205],[44.19222,8.93028],[46.99339,7.9989],[47.92477,8.00111],[47.97917,8.00124],[44.98104,4.91821],[44.02436,4.9451],[43.40263,4.79289],[43.04177,4.57923],[42.97746,4.44032],[42.84526,4.28357],[42.55853,4.20518],[42.07619,4.17667],[41.89488,3.97375],[41.31368,3.14314],[40.98767,2.82959],[41.00099,-0.83068],[41.56,-1.59812],[41.56362,-1.66375],[41.75542,-1.85308],[49.16337,2.78611],[52.253,11.68582],[51.12877,12.56479],[48.95249,11.56816]]]]}},{type:"Feature",properties:{iso1A2:"SR",iso1A3:"SUR",iso1N3:"740",wikidata:"Q730",nameEn:"Suriname",groups:["005","419","019"],driveSide:"left",callingCodes:["597"]},geometry:{type:"MultiPolygon",coordinates:[[[[-54.26916,5.26909],[-54.01877,5.52789],[-54.01074,5.68785],[-53.7094,6.2264],[-56.84822,6.73257],[-57.31629,5.33714],[-57.22536,5.15605],[-57.37442,5.0208],[-57.8699,4.89394],[-58.0307,3.95513],[-57.35891,3.32121],[-56.70519,2.02964],[-56.55439,2.02003],[-56.47045,1.95135],[-55.99278,1.83137],[-55.89863,1.89861],[-55.92159,2.05236],[-56.13054,2.27723],[-55.96292,2.53188],[-55.71493,2.40342],[-55.01919,2.564],[-54.6084,2.32856],[-54.42864,2.42442],[-54.28534,2.67798],[-53.9849,3.58697],[-53.98914,3.627],[-54.05128,3.63557],[-54.19367,3.84387],[-54.38444,4.13222],[-54.4717,4.91964],[-54.26916,5.26909]]]]}},{type:"Feature",properties:{iso1A2:"SS",iso1A3:"SSD",iso1N3:"728",wikidata:"Q958",nameEn:"South Sudan",groups:["014","202","002"],callingCodes:["211"]},geometry:{type:"MultiPolygon",coordinates:[[[[34.10229,9.50238],[33.87958,9.49937],[33.9082,9.762],[33.96323,9.80972],[33.99185,9.99623],[33.96984,10.15446],[33.90159,10.17179],[33.80913,10.32994],[33.66604,10.44254],[33.52294,10.64382],[33.24645,10.77913],[33.26977,10.83632],[33.13988,11.43248],[33.25876,12.22111],[32.73921,12.22757],[32.73921,11.95203],[32.10079,11.95203],[32.39578,11.70208],[32.39358,11.18207],[32.46967,11.04662],[31.99177,10.65065],[31.77539,10.28939],[31.28504,9.75287],[30.84605,9.7498],[30.82893,9.71451],[30.53005,9.95992],[30.00389,10.28633],[29.94629,10.29245],[29.54,10.07949],[29.53844,9.75133],[29.06988,9.74826],[28.99983,9.67155],[27.90704,9.61323],[27.14427,9.62858],[26.70685,9.48735],[26.35815,9.57946],[26.21338,9.91545],[25.93241,10.17941],[25.93163,10.38159],[25.78141,10.42599],[25.0918,10.33718],[25.05688,10.06776],[24.97739,9.9081],[24.84653,9.80643],[24.49389,9.79962],[24.12744,9.73784],[24.09319,9.66572],[23.69155,9.67566],[23.62179,9.53823],[23.64981,9.44303],[23.64358,9.28637],[23.56263,9.19418],[23.4848,9.16959],[23.44744,8.99128],[23.59065,8.99743],[23.51905,8.71749],[24.25691,8.69288],[24.13238,8.36959],[24.35965,8.26177],[24.85156,8.16933],[24.98855,7.96588],[25.25319,7.8487],[25.29214,7.66675],[25.20649,7.61115],[25.20337,7.50312],[25.35281,7.42595],[25.37461,7.33024],[25.90076,7.09549],[26.38022,6.63493],[26.32729,6.36272],[26.58259,6.1987],[26.51721,6.09655],[27.22705,5.71254],[27.22705,5.62889],[27.28621,5.56382],[27.23017,5.37167],[27.26886,5.25876],[27.44012,5.07349],[27.56656,4.89375],[27.65462,4.89375],[27.76469,4.79284],[27.79551,4.59976],[28.20719,4.35614],[28.6651,4.42638],[28.8126,4.48784],[29.03054,4.48784],[29.22207,4.34297],[29.43341,4.50101],[29.49726,4.7007],[29.82087,4.56246],[29.79666,4.37809],[30.06964,4.13221],[30.1621,4.10586],[30.22374,3.93896],[30.27658,3.95653],[30.47691,3.83353],[30.55396,3.84451],[30.57378,3.74567],[30.56277,3.62703],[30.78512,3.67097],[30.80713,3.60506],[30.85997,3.5743],[30.85153,3.48867],[30.97601,3.693],[31.16666,3.79853],[31.29476,3.8015],[31.50478,3.67814],[31.50776,3.63652],[31.72075,3.74354],[31.81459,3.82083],[31.86821,3.78664],[31.96205,3.6499],[31.95907,3.57408],[32.05187,3.589],[32.08491,3.56287],[32.08866,3.53543],[32.19888,3.50867],[32.20782,3.6053],[32.41337,3.748],[32.72021,3.77327],[32.89746,3.81339],[33.02852,3.89296],[33.18356,3.77812],[33.51264,3.75068],[33.9873,4.23316],[34.47601,4.72162],[35.34151,5.02364],[35.30992,4.90402],[35.47843,4.91872],[35.42366,4.76969],[35.51424,4.61643],[35.9419,4.61933],[35.82118,4.77382],[35.81968,5.10757],[35.8576,5.33413],[35.50792,5.42431],[35.29938,5.34042],[35.31188,5.50106],[35.13058,5.62118],[35.12611,5.68937],[35.00546,5.89387],[34.96227,6.26415],[35.01738,6.46991],[34.87736,6.60161],[34.77459,6.5957],[34.65096,6.72589],[34.53776,6.74808],[34.53925,6.82794],[34.47669,6.91076],[34.35753,6.91963],[34.19369,7.04382],[34.19369,7.12807],[34.01495,7.25664],[34.03878,7.27437],[34.02984,7.36449],[33.87642,7.5491],[33.71407,7.65983],[33.44745,7.7543],[33.32531,7.71297],[33.24637,7.77939],[33.04944,7.78989],[33.0006,7.90333],[33.08401,8.05822],[33.18083,8.13047],[33.1853,8.29264],[33.19721,8.40317],[33.3119,8.45474],[33.54575,8.47094],[33.66938,8.44442],[33.71407,8.3678],[33.87195,8.41938],[33.89579,8.4842],[34.01346,8.50041],[34.14453,8.60204],[34.14304,9.04654],[34.10229,9.50238]]]]}},{type:"Feature",properties:{iso1A2:"ST",iso1A3:"STP",iso1N3:"678",wikidata:"Q1039",nameEn:"São Tomé and Principe",groups:["017","202","002"],callingCodes:["239"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.9107,-0.09539],[6.69416,-0.53945],[8.0168,1.79377],[7.23334,2.23756],[5.9107,-0.09539]]]]}},{type:"Feature",properties:{iso1A2:"SV",iso1A3:"SLV",iso1N3:"222",wikidata:"Q792",nameEn:"El Salvador",groups:["013","003","419","019"],callingCodes:["503"]},geometry:{type:"MultiPolygon",coordinates:[[[[-89.34776,14.43013],[-89.39028,14.44561],[-89.57441,14.41637],[-89.58814,14.33165],[-89.50614,14.26084],[-89.52397,14.22628],[-89.61844,14.21937],[-89.70756,14.1537],[-89.75569,14.07073],[-89.73251,14.04133],[-89.76103,14.02923],[-89.81807,14.07073],[-89.88937,14.0396],[-90.10505,13.85104],[-90.11344,13.73679],[-90.55276,12.8866],[-88.11443,12.63306],[-87.7346,13.13228],[-87.55124,13.12523],[-87.69751,13.25228],[-87.73714,13.32715],[-87.80177,13.35689],[-87.84675,13.41078],[-87.83467,13.44655],[-87.77354,13.45767],[-87.73841,13.44169],[-87.72115,13.46083],[-87.71657,13.50577],[-87.78148,13.52906],[-87.73106,13.75443],[-87.68821,13.80829],[-87.7966,13.91353],[-88.00331,13.86948],[-88.07641,13.98447],[-88.23018,13.99915],[-88.25791,13.91108],[-88.48982,13.86458],[-88.49738,13.97224],[-88.70661,14.04317],[-88.73182,14.10919],[-88.815,14.11652],[-88.85785,14.17763],[-88.94608,14.20207],[-89.04187,14.33644],[-89.34776,14.43013]]]]}},{type:"Feature",properties:{iso1A2:"SX",iso1A3:"SXM",iso1N3:"534",wikidata:"Q26273",nameEn:"Sint Maarten",country:"NL",groups:["029","003","419","019"],callingCodes:["1 721"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.29212,17.90532],[-63.07669,17.79659],[-62.93924,18.02904],[-63.02323,18.05757],[-63.04039,18.05619],[-63.0579,18.06614],[-63.07759,18.04943],[-63.09686,18.04608],[-63.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615],[-63.29212,17.90532]]]]}},{type:"Feature",properties:{iso1A2:"SY",iso1A3:"SYR",iso1N3:"760",wikidata:"Q858",nameEn:"Syria",groups:["145","142"],callingCodes:["963"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.23683,37.2863],[42.21548,37.28026],[42.20454,37.28715],[42.22381,37.30238],[42.22257,37.31395],[42.2112,37.32491],[42.19301,37.31323],[42.18225,37.28569],[42.00894,37.17209],[41.515,37.08084],[41.21937,37.07665],[40.90856,37.13147],[40.69136,37.0996],[39.81589,36.75538],[39.21538,36.66834],[39.03217,36.70911],[38.74042,36.70629],[38.55908,36.84429],[38.38859,36.90064],[38.21064,36.91842],[37.81974,36.76055],[37.68048,36.75065],[37.49103,36.66904],[37.47253,36.63243],[37.21988,36.6736],[37.16177,36.66069],[37.10894,36.6704],[37.08279,36.63495],[37.02088,36.66422],[37.01647,36.69512],[37.04619,36.71101],[37.04399,36.73483],[36.99886,36.74012],[36.99557,36.75997],[36.66727,36.82901],[36.61581,36.74629],[36.62681,36.71189],[36.57398,36.65186],[36.58829,36.58295],[36.54206,36.49539],[36.6081,36.33772],[36.65653,36.33861],[36.68672,36.23677],[36.6125,36.22592],[36.50463,36.2419],[36.4617,36.20461],[36.39206,36.22088],[36.37474,36.01163],[36.33956,35.98687],[36.30099,36.00985],[36.28338,36.00273],[36.29769,35.96086],[36.27678,35.94839],[36.25366,35.96264],[36.19973,35.95195],[36.17441,35.92076],[36.1623,35.80925],[36.14029,35.81015],[36.13919,35.83692],[36.11827,35.85923],[35.99829,35.88242],[36.01844,35.92403],[36.00514,35.94113],[35.98499,35.94107],[35.931,35.92109],[35.51152,36.10954],[35.48515,34.70851],[35.97386,34.63322],[35.98718,34.64977],[36.29165,34.62991],[36.32399,34.69334],[36.35135,34.68516],[36.35384,34.65447],[36.42941,34.62505],[36.46003,34.6378],[36.45299,34.59438],[36.41429,34.61175],[36.39846,34.55672],[36.3369,34.52629],[36.34745,34.5002],[36.4442,34.50165],[36.46179,34.46541],[36.55853,34.41609],[36.53039,34.3798],[36.56556,34.31881],[36.60778,34.31009],[36.58667,34.27667],[36.59195,34.2316],[36.62537,34.20251],[36.5128,34.09916],[36.50576,34.05982],[36.41078,34.05253],[36.28589,33.91981],[36.38263,33.86579],[36.3967,33.83365],[36.14517,33.85118],[36.06778,33.82927],[35.9341,33.6596],[36.05723,33.57904],[35.94465,33.52774],[35.94816,33.47886],[35.88668,33.43183],[35.82577,33.40479],[35.81324,33.36354],[35.77477,33.33609],[35.813,33.3172],[35.77513,33.27342],[35.81295,33.24841],[35.81647,33.2028],[35.83846,33.19397],[35.84285,33.16673],[35.81911,33.1336],[35.81911,33.11077],[35.84802,33.1031],[35.87188,32.98028],[35.89298,32.9456],[35.87012,32.91976],[35.84021,32.8725],[35.83758,32.82817],[35.78745,32.77938],[35.75983,32.74803],[35.88405,32.71321],[35.93307,32.71966],[35.96633,32.66237],[36.02239,32.65911],[36.08074,32.51463],[36.20379,32.52751],[36.20875,32.49529],[36.23948,32.50108],[36.40959,32.37908],[36.83946,32.31293],[38.79171,33.37328],[40.64314,34.31604],[40.97676,34.39788],[41.12388,34.65742],[41.2345,34.80049],[41.21654,35.1508],[41.26569,35.42708],[41.38184,35.62502],[41.37027,35.84095],[41.2564,36.06012],[41.28864,36.35368],[41.40058,36.52502],[41.81736,36.58782],[42.36697,37.0627],[42.35724,37.10998],[42.32313,37.17814],[42.34735,37.22548],[42.2824,37.2798],[42.26039,37.27017],[42.23683,37.2863]]]]}},{type:"Feature",properties:{iso1A2:"SZ",iso1A3:"SWZ",iso1N3:"748",wikidata:"Q1050",nameEn:"Eswatini",aliases:["Swaziland"],groups:["018","202","002"],driveSide:"left",callingCodes:["268"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.86881,-25.99973],[31.4175,-25.71886],[31.31237,-25.7431],[31.13073,-25.91558],[30.95819,-26.26303],[30.78927,-26.48271],[30.81101,-26.84722],[30.88826,-26.79622],[30.97757,-26.92706],[30.96088,-27.0245],[31.15027,-27.20151],[31.49834,-27.31549],[31.97592,-27.31675],[31.97463,-27.11057],[32.00893,-26.8096],[32.09664,-26.80721],[32.13315,-26.84345],[32.13409,-26.5317],[32.07352,-26.40185],[32.10435,-26.15656],[32.08599,-26.00978],[32.00916,-25.999],[31.974,-25.95387],[31.86881,-25.99973]]]]}},{type:"Feature",properties:{iso1A2:"TA",iso1A3:"TAA",wikidata:"Q220982",nameEn:"Tristan da Cunha",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290 8","44 20"]},geometry:{type:"MultiPolygon",coordinates:[[[[-13.48367,-36.6746],[-13.41694,-37.88844],[-11.48092,-37.8367],[-11.55782,-36.60319],[-13.48367,-36.6746]]]]}},{type:"Feature",properties:{iso1A2:"TC",iso1A3:"TCA",iso1N3:"796",wikidata:"Q18221",nameEn:"Turks and Caicos Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 649"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.41726,22.40371],[-72.72017,21.48055],[-71.46138,20.64433],[-70.63262,21.53631],[-72.41726,22.40371]]]]}},{type:"Feature",properties:{iso1A2:"TD",iso1A3:"TCD",iso1N3:"148",wikidata:"Q657",nameEn:"Chad",groups:["017","202","002"],callingCodes:["235"]},geometry:{type:"MultiPolygon",coordinates:[[[[23.99539,19.49944],[15.99566,23.49639],[14.99751,23.00539],[15.19692,21.99339],[15.20213,21.49365],[15.28332,21.44557],[15.62515,20.95395],[15.57248,20.92138],[15.55382,20.86507],[15.56004,20.79488],[15.59841,20.74039],[15.6721,20.70069],[15.99632,20.35364],[15.75098,19.93002],[15.6032,18.77402],[15.50373,16.89649],[14.37425,15.72591],[13.86301,15.04043],[13.78991,14.87519],[13.809,14.72915],[13.67878,14.64013],[13.68573,14.55276],[13.48259,14.46704],[13.47559,14.40881],[13.6302,13.71094],[14.08251,13.0797],[14.46881,13.08259],[14.56101,12.91036],[14.55058,12.78256],[14.83314,12.62963],[14.90827,12.3269],[14.89019,12.16593],[14.96952,12.0925],[15.00146,12.1223],[15.0349,12.10698],[15.05786,12.0608],[15.04808,11.8731],[15.11579,11.79313],[15.06595,11.71126],[15.13149,11.5537],[15.0585,11.40481],[15.10021,11.04101],[15.04957,11.02347],[15.09127,10.87431],[15.06737,10.80921],[15.15532,10.62846],[15.14936,10.53915],[15.23724,10.47764],[15.30874,10.31063],[15.50535,10.1098],[15.68761,9.99344],[15.41408,9.92876],[15.24618,9.99246],[15.14043,9.99246],[15.05999,9.94845],[14.95722,9.97926],[14.80082,9.93818],[14.4673,10.00264],[14.20411,10.00055],[14.1317,9.82413],[14.01793,9.73169],[13.97544,9.6365],[14.37094,9.2954],[14.35707,9.19611],[14.83566,8.80557],[15.09484,8.65982],[15.20426,8.50892],[15.50743,7.79302],[15.59272,7.7696],[15.56964,7.58936],[15.49743,7.52179],[15.73118,7.52006],[15.79942,7.44149],[16.40703,7.68809],[16.41583,7.77971],[16.58315,7.88657],[16.59415,7.76444],[16.658,7.75353],[16.6668,7.67281],[16.8143,7.53971],[17.67288,7.98905],[17.93926,7.95853],[18.02731,8.01085],[18.6085,8.05009],[18.64153,8.08714],[18.62612,8.14163],[18.67455,8.22226],[18.79783,8.25929],[19.11044,8.68172],[18.86388,8.87971],[19.06421,9.00367],[20.36748,9.11019],[20.82979,9.44696],[21.26348,9.97642],[21.34934,9.95907],[21.52766,10.2105],[21.63553,10.217],[21.71479,10.29932],[21.72139,10.64136],[22.45889,11.00246],[22.87758,10.91915],[22.97249,11.21955],[22.93124,11.41645],[22.7997,11.40424],[22.54907,11.64372],[22.64092,12.07485],[22.48369,12.02766],[22.50548,12.16769],[22.38873,12.45514],[22.46345,12.61925],[22.22684,12.74682],[22.15679,12.66634],[21.98711,12.63292],[21.89371,12.68001],[21.81432,12.81362],[21.94819,13.05637],[22.02914,13.13976],[22.1599,13.19281],[22.29689,13.3731],[22.08674,13.77863],[22.22995,13.96754],[22.5553,14.11704],[22.55997,14.23024],[22.44944,14.24986],[22.38562,14.58907],[22.70474,14.69149],[22.66115,14.86308],[22.99584,15.22989],[22.99584,15.40105],[22.92579,15.47007],[22.93201,15.55107],[23.10792,15.71297],[23.38812,15.69649],[23.62785,15.7804],[23.99997,15.69575],[23.99539,19.49944]]]]}},{type:"Feature",properties:{iso1A2:"TF",iso1A3:"ATF",iso1N3:"260",wikidata:"Q129003",nameEn:"French Southern and Antarctic Lands",country:"FR",groups:["014","202","002"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.53458,-16.36909],[54.96649,-16.28353],[54.61476,-15.02273],[53.53458,-16.36909]]],[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[80.15867,-36.04977],[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977]]]]}},{type:"Feature",properties:{iso1A2:"TG",iso1A3:"TGO",iso1N3:"768",wikidata:"Q945",nameEn:"Togo",groups:["011","202","002"],callingCodes:["228"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.50388,11.01011],[-0.13493,11.14075],[-0.14462,11.10811],[-0.05733,11.08628],[-0.0275,11.11202],[-0.00514,11.10763],[0.00342,11.08317],[0.02395,11.06229],[0.03355,10.9807],[-0.0063,10.96417],[-0.00908,10.91644],[-0.02685,10.8783],[-0.0228,10.81916],[-0.07183,10.76794],[-0.07327,10.71845],[-0.09141,10.7147],[-0.05945,10.63458],[0.12886,10.53149],[0.18846,10.4096],[0.29453,10.41546],[0.33028,10.30408],[0.39584,10.31112],[0.35293,10.09412],[0.41371,10.06361],[0.41252,10.02018],[0.36366,10.03309],[0.32075,9.72781],[0.34816,9.71607],[0.34816,9.66907],[0.32313,9.6491],[0.28261,9.69022],[0.26712,9.66437],[0.29334,9.59387],[0.36008,9.6256],[0.38153,9.58682],[0.23851,9.57389],[0.2409,9.52335],[0.30406,9.521],[0.31241,9.50337],[0.2254,9.47869],[0.25758,9.42696],[0.33148,9.44812],[0.36485,9.49749],[0.49118,9.48339],[0.56388,9.40697],[0.45424,9.04581],[0.52455,8.87746],[0.37319,8.75262],[0.47211,8.59945],[0.64731,8.48866],[0.73432,8.29529],[0.63897,8.25873],[0.5913,8.19622],[0.61156,8.18324],[0.6056,8.13959],[0.58891,8.12779],[0.62943,7.85751],[0.58295,7.62368],[0.51979,7.58706],[0.52455,7.45354],[0.57223,7.39326],[0.62943,7.41099],[0.65327,7.31643],[0.59606,7.01252],[0.52217,6.9723],[0.52098,6.94391],[0.56508,6.92971],[0.52853,6.82921],[0.57406,6.80348],[0.58176,6.76049],[0.6497,6.73682],[0.63659,6.63857],[0.74862,6.56517],[0.71048,6.53083],[0.89283,6.33779],[0.99652,6.33779],[1.03108,6.24064],[1.05969,6.22998],[1.09187,6.17074],[1.19966,6.17069],[1.19771,6.11522],[1.27574,5.93551],[1.67336,6.02702],[1.62913,6.24075],[1.79826,6.28221],[1.76906,6.43189],[1.58105,6.68619],[1.61812,6.74843],[1.55877,6.99737],[1.64249,6.99562],[1.61838,9.0527],[1.5649,9.16941],[1.41746,9.3226],[1.33675,9.54765],[1.36624,9.5951],[1.35507,9.99525],[0.77666,10.37665],[0.80358,10.71459],[0.8804,10.803],[0.91245,10.99597],[0.66104,10.99964],[0.4958,10.93269],[0.50521,10.98035],[0.48852,10.98561],[0.50388,11.01011]]]]}},{type:"Feature",properties:{iso1A2:"TH",iso1A3:"THA",iso1N3:"764",wikidata:"Q869",nameEn:"Thailand",groups:["035","142"],driveSide:"left",callingCodes:["66"]},geometry:{type:"MultiPolygon",coordinates:[[[[100.08404,20.36626],[99.95721,20.46301],[99.91616,20.44986],[99.90499,20.4487],[99.89692,20.44789],[99.89301,20.44311],[99.89168,20.44548],[99.88451,20.44596],[99.88211,20.44488],[99.86383,20.44371],[99.81096,20.33687],[99.68255,20.32077],[99.46008,20.39673],[99.46077,20.36198],[99.5569,20.20676],[99.52943,20.14811],[99.416,20.08614],[99.20328,20.12877],[99.0735,20.10298],[98.98679,19.7419],[98.83661,19.80931],[98.56065,19.67807],[98.51182,19.71303],[98.24884,19.67876],[98.13829,19.78541],[98.03314,19.80941],[98.04364,19.65755],[97.84715,19.55782],[97.88423,19.5041],[97.78769,19.39429],[97.84186,19.29526],[97.78606,19.26769],[97.84024,19.22217],[97.83479,19.09972],[97.73797,19.04261],[97.73654,18.9812],[97.66487,18.9371],[97.73836,18.88478],[97.76752,18.58097],[97.5258,18.4939],[97.36444,18.57138],[97.34522,18.54596],[97.50383,18.26844],[97.56219,18.33885],[97.64116,18.29778],[97.60841,18.23846],[97.73723,17.97912],[97.66794,17.88005],[97.76407,17.71595],[97.91829,17.54504],[98.11185,17.36829],[98.10439,17.33847],[98.34566,17.04822],[98.39441,17.06266],[98.52624,16.89979],[98.49603,16.8446],[98.53833,16.81934],[98.46994,16.73613],[98.50253,16.7139],[98.49713,16.69022],[98.51043,16.70107],[98.51579,16.69433],[98.51472,16.68521],[98.51833,16.676],[98.51113,16.64503],[98.5695,16.62826],[98.57912,16.55983],[98.63817,16.47424],[98.68074,16.27068],[98.84485,16.42354],[98.92656,16.36425],[98.8376,16.11706],[98.69585,16.13353],[98.57019,16.04578],[98.59853,15.87197],[98.541,15.65406],[98.58598,15.46821],[98.56027,15.33471],[98.4866,15.39154],[98.39351,15.34177],[98.41906,15.27103],[98.40522,15.25268],[98.30446,15.30667],[98.22,15.21327],[98.18821,15.13125],[98.24874,14.83013],[98.56762,14.37701],[98.97356,14.04868],[99.16695,13.72621],[99.20617,13.20575],[99.12225,13.19847],[99.10646,13.05804],[99.18748,12.9898],[99.18905,12.84799],[99.29254,12.68921],[99.409,12.60603],[99.47519,12.1353],[99.56445,12.14805],[99.53424,12.02317],[99.64891,11.82699],[99.64108,11.78948],[99.5672,11.62732],[99.47598,11.62434],[99.39485,11.3925],[99.31573,11.32081],[99.32756,11.28545],[99.06938,10.94857],[99.02337,10.97217],[98.99701,10.92962],[99.0069,10.85485],[98.86819,10.78336],[98.78511,10.68351],[98.77275,10.62548],[98.81944,10.52761],[98.7391,10.31488],[98.55174,9.92804],[98.52291,9.92389],[98.47298,9.95782],[98.33094,9.91973],[98.12555,9.44056],[97.63455,9.60854],[97.19814,8.18901],[99.31854,5.99868],[99.50117,6.44501],[99.91873,6.50233],[100.0756,6.4045],[100.12,6.42105],[100.19511,6.72559],[100.29651,6.68439],[100.30828,6.66462],[100.31618,6.66781],[100.31884,6.66423],[100.32671,6.66526],[100.32607,6.65933],[100.31929,6.65413],[100.35413,6.54932],[100.41152,6.52299],[100.41791,6.5189],[100.42351,6.51762],[100.43027,6.52389],[100.66986,6.45086],[100.74361,6.50811],[100.74822,6.46231],[100.81045,6.45086],[100.85884,6.24929],[101.10313,6.25617],[101.12618,6.19431],[101.06165,6.14161],[101.12388,6.11411],[101.087,5.9193],[101.02708,5.91013],[100.98815,5.79464],[101.14062,5.61613],[101.25755,5.71065],[101.25524,5.78633],[101.58019,5.93534],[101.69773,5.75881],[101.75074,5.79091],[101.80144,5.74505],[101.89188,5.8386],[101.91776,5.84269],[101.92819,5.85511],[101.94712,5.98421],[101.9714,6.00575],[101.97114,6.01992],[101.99209,6.04075],[102.01835,6.05407],[102.09182,6.14161],[102.07732,6.193],[102.08127,6.22679],[102.09086,6.23546],[102.46318,7.22462],[102.47649,9.66162],[102.52395,11.25257],[102.91449,11.65512],[102.90973,11.75613],[102.83957,11.8519],[102.78427,11.98746],[102.77026,12.06815],[102.70176,12.1686],[102.73134,12.37091],[102.78116,12.40284],[102.7796,12.43781],[102.57567,12.65358],[102.51963,12.66117],[102.4994,12.71736],[102.53053,12.77506],[102.49335,12.92711],[102.48694,12.97537],[102.52275,12.99813],[102.46011,13.08057],[102.43422,13.09061],[102.36146,13.26006],[102.36001,13.31142],[102.34611,13.35618],[102.35692,13.38274],[102.35563,13.47307],[102.361,13.50551],[102.33828,13.55613],[102.36859,13.57488],[102.44601,13.5637],[102.5358,13.56933],[102.57573,13.60461],[102.62483,13.60883],[102.58635,13.6286],[102.5481,13.6589],[102.56848,13.69366],[102.72727,13.77806],[102.77864,13.93374],[102.91251,14.01531],[102.93275,14.19044],[103.16469,14.33075],[103.39353,14.35639],[103.53518,14.42575],[103.71109,14.4348],[103.70175,14.38052],[103.93836,14.3398],[104.27616,14.39861],[104.55014,14.36091],[104.69335,14.42726],[104.97667,14.38806],[105.02804,14.23722],[105.08408,14.20402],[105.14012,14.23873],[105.17748,14.34432],[105.20894,14.34967],[105.43783,14.43865],[105.53864,14.55731],[105.5121,14.80802],[105.61162,15.00037],[105.46661,15.13132],[105.58043,15.32724],[105.50662,15.32054],[105.4692,15.33709],[105.47635,15.3796],[105.58191,15.41031],[105.60446,15.53301],[105.61756,15.68792],[105.46573,15.74742],[105.42285,15.76971],[105.37959,15.84074],[105.34115,15.92737],[105.38508,15.987],[105.42001,16.00657],[105.06204,16.09792],[105.00262,16.25627],[104.88057,16.37311],[104.73349,16.565],[104.76099,16.69302],[104.7397,16.81005],[104.76442,16.84752],[104.7373,16.91125],[104.73712,17.01404],[104.80716,17.19025],[104.80061,17.39367],[104.69867,17.53038],[104.45404,17.66788],[104.35432,17.82871],[104.2757,17.86139],[104.21776,17.99335],[104.10927,18.10826],[104.06533,18.21656],[103.97725,18.33631],[103.93916,18.33914],[103.85642,18.28666],[103.82449,18.33979],[103.699,18.34125],[103.60957,18.40528],[103.47773,18.42841],[103.41044,18.4486],[103.30977,18.4341],[103.24779,18.37807],[103.23818,18.34875],[103.29757,18.30475],[103.17093,18.2618],[103.14994,18.23172],[103.1493,18.17799],[103.07343,18.12351],[103.07823,18.03833],[103.0566,18.00144],[103.01998,17.97095],[102.9912,17.9949],[102.95812,18.0054],[102.86323,17.97531],[102.81988,17.94233],[102.79044,17.93612],[102.75954,17.89561],[102.68538,17.86653],[102.67543,17.84529],[102.69946,17.81686],[102.68194,17.80151],[102.59485,17.83537],[102.5896,17.84889],[102.61432,17.92273],[102.60971,17.95411],[102.59234,17.96127],[102.45523,17.97106],[102.11359,18.21532],[101.88485,18.02474],[101.78087,18.07559],[101.72294,17.92867],[101.44667,17.7392],[101.15108,17.47586],[100.96541,17.57926],[101.02185,17.87637],[101.1793,18.0544],[101.19118,18.2125],[101.15108,18.25624],[101.18227,18.34367],[101.06047,18.43247],[101.27585,18.68875],[101.22832,18.73377],[101.25803,18.89545],[101.35606,19.04716],[101.261,19.12717],[101.24911,19.33334],[101.20604,19.35296],[101.21347,19.46223],[101.26991,19.48324],[101.26545,19.59242],[101.08928,19.59748],[100.90302,19.61901],[100.77231,19.48324],[100.64606,19.55884],[100.58219,19.49164],[100.49604,19.53504],[100.398,19.75047],[100.5094,19.87904],[100.58808,20.15791],[100.55218,20.17741],[100.51052,20.14928],[100.47567,20.19133],[100.4537,20.19971],[100.44992,20.23644],[100.41473,20.25625],[100.37439,20.35156],[100.33383,20.4028],[100.25769,20.3992],[100.22076,20.31598],[100.16668,20.2986],[100.1712,20.24324],[100.11785,20.24787],[100.09337,20.26293],[100.09999,20.31614],[100.08404,20.36626]]]]}},{type:"Feature",properties:{iso1A2:"TJ",iso1A3:"TJK",iso1N3:"762",wikidata:"Q863",nameEn:"Tajikistan",groups:["143","142"],callingCodes:["992"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.45251,41.04438],[70.38028,41.02014],[70.36655,40.90296],[69.69434,40.62615],[69.59441,40.70181],[69.53021,40.77621],[69.38327,40.7918],[69.32834,40.70233],[69.3455,40.57988],[69.2643,40.57506],[69.21063,40.54469],[69.27066,40.49274],[69.28525,40.41894],[69.30774,40.36102],[69.33794,40.34819],[69.32833,40.29794],[69.30808,40.2821],[69.24817,40.30357],[69.25229,40.26362],[69.30104,40.24502],[69.30448,40.18774],[69.2074,40.21488],[69.15659,40.2162],[69.04544,40.22904],[68.85832,40.20885],[68.84357,40.18604],[68.79276,40.17555],[68.77902,40.20492],[68.5332,40.14826],[68.52771,40.11676],[68.62796,40.07789],[69.01523,40.15771],[69.01935,40.11466],[68.96579,40.06949],[68.84906,40.04952],[68.93695,39.91167],[68.88889,39.87163],[68.63071,39.85265],[68.61972,39.68905],[68.54166,39.53929],[68.12053,39.56317],[67.70992,39.66156],[67.62889,39.60234],[67.44899,39.57799],[67.46547,39.53564],[67.39681,39.52505],[67.46822,39.46146],[67.45998,39.315],[67.36522,39.31287],[67.33226,39.23739],[67.67833,39.14479],[67.68915,39.00775],[68.09704,39.02589],[68.19743,38.85985],[68.06948,38.82115],[68.12877,38.73677],[68.05598,38.71641],[68.0807,38.64136],[68.05873,38.56087],[68.11366,38.47169],[68.06274,38.39435],[68.13289,38.40822],[68.40343,38.19484],[68.27159,37.91477],[68.12635,37.93],[67.81566,37.43107],[67.8474,37.31594],[67.78329,37.1834],[67.7803,37.08978],[67.87917,37.0591],[68.02194,36.91923],[68.18542,37.02074],[68.27605,37.00977],[68.29253,37.10621],[68.41201,37.10402],[68.41888,37.13906],[68.61851,37.19815],[68.6798,37.27906],[68.81438,37.23862],[68.80889,37.32494],[68.91189,37.26704],[68.88168,37.33368],[68.96407,37.32603],[69.03274,37.25174],[69.25152,37.09426],[69.39529,37.16752],[69.45022,37.23315],[69.36645,37.40462],[69.44954,37.4869],[69.51888,37.5844],[69.80041,37.5746],[69.84435,37.60616],[69.93362,37.61378],[69.95971,37.5659],[70.15015,37.52519],[70.28243,37.66706],[70.27694,37.81258],[70.1863,37.84296],[70.17206,37.93276],[70.4898,38.12546],[70.54673,38.24541],[70.60407,38.28046],[70.61526,38.34774],[70.64966,38.34999],[70.69189,38.37031],[70.6761,38.39144],[70.67438,38.40597],[70.69807,38.41861],[70.72485,38.4131],[70.75455,38.4252],[70.77132,38.45548],[70.78581,38.45502],[70.78702,38.45031],[70.79766,38.44944],[70.80521,38.44447],[70.81697,38.44507],[70.82538,38.45394],[70.84376,38.44688],[70.88719,38.46826],[70.92728,38.43021],[70.98693,38.48862],[71.03545,38.44779],[71.0556,38.40176],[71.09542,38.42517],[71.10592,38.42077],[71.10957,38.40671],[71.1451,38.40106],[71.21291,38.32797],[71.33114,38.30339],[71.33869,38.27335],[71.37803,38.25641],[71.36444,38.15358],[71.29878,38.04429],[71.28922,38.01272],[71.27622,37.99946],[71.27278,37.96496],[71.24969,37.93031],[71.2809,37.91995],[71.296,37.93403],[71.32871,37.88564],[71.51565,37.95349],[71.58843,37.92425],[71.59255,37.79956],[71.55752,37.78677],[71.54324,37.77104],[71.53053,37.76534],[71.55234,37.73209],[71.54186,37.69691],[71.51972,37.61945],[71.5065,37.60912],[71.49693,37.53527],[71.50616,37.50733],[71.5256,37.47971],[71.49612,37.4279],[71.47685,37.40281],[71.4862,37.33405],[71.49821,37.31975],[71.50674,37.31502],[71.48536,37.26017],[71.4824,37.24921],[71.48339,37.23937],[71.47386,37.2269],[71.4555,37.21418],[71.4494,37.18137],[71.44127,37.11856],[71.43097,37.05855],[71.45578,37.03094],[71.46923,36.99925],[71.48481,36.93218],[71.51502,36.89128],[71.57195,36.74943],[71.67083,36.67346],[71.83229,36.68084],[72.31676,36.98115],[72.54095,37.00007],[72.66381,37.02014],[72.79693,37.22222],[73.06884,37.31729],[73.29633,37.46495],[73.77197,37.4417],[73.76647,37.33913],[73.61129,37.27469],[73.64974,37.23643],[73.82552,37.22659],[73.8564,37.26158],[74.20308,37.34208],[74.23339,37.41116],[74.41055,37.3948],[74.56161,37.37734],[74.68383,37.3948],[74.8294,37.3435],[74.88887,37.23275],[75.12328,37.31839],[75.09719,37.37297],[75.15899,37.41443],[75.06011,37.52779],[74.94338,37.55501],[74.8912,37.67576],[75.00935,37.77486],[74.92416,37.83428],[74.9063,38.03033],[74.82665,38.07359],[74.80331,38.19889],[74.69894,38.22155],[74.69619,38.42947],[74.51217,38.47034],[74.17022,38.65504],[73.97933,38.52945],[73.79806,38.61106],[73.80656,38.66449],[73.7033,38.84782],[73.7445,38.93867],[73.82964,38.91517],[73.81728,39.04007],[73.75823,39.023],[73.60638,39.24534],[73.54572,39.27567],[73.55396,39.3543],[73.5004,39.38402],[73.59241,39.40843],[73.59831,39.46425],[73.45096,39.46677],[73.31912,39.38615],[73.18454,39.35536],[72.85934,39.35116],[72.62027,39.39696],[72.33173,39.33093],[72.23834,39.17248],[72.17242,39.2661],[72.09689,39.26823],[72.04059,39.36704],[71.90601,39.27674],[71.79202,39.27355],[71.7522,39.32031],[71.80164,39.40631],[71.76816,39.45456],[71.62688,39.44056],[71.5517,39.45722],[71.55856,39.57588],[71.49814,39.61397],[71.08752,39.50704],[71.06418,39.41586],[70.7854,39.38933],[70.64087,39.58792],[70.44757,39.60128],[70.2869,39.53141],[70.11111,39.58223],[69.87491,39.53882],[69.68677,39.59281],[69.3594,39.52516],[69.26938,39.8127],[69.35649,40.01994],[69.43134,39.98431],[69.43557,39.92877],[69.53615,39.93991],[69.5057,40.03277],[69.53855,40.0887],[69.53794,40.11833],[69.55555,40.12296],[69.57615,40.10524],[69.64704,40.12165],[69.67001,40.10639],[70.01283,40.23288],[70.58297,40.00891],[70.57384,39.99394],[70.47557,39.93216],[70.55033,39.96619],[70.58912,39.95211],[70.65946,39.9878],[70.65827,40.0981],[70.7928,40.12797],[70.80495,40.16813],[70.9818,40.22392],[70.8607,40.217],[70.62342,40.17396],[70.56394,40.26421],[70.57149,40.3442],[70.37511,40.38605],[70.32626,40.45174],[70.49871,40.52503],[70.80009,40.72825],[70.45251,41.04438]]],[[[70.68112,40.90612],[70.6158,40.97661],[70.56077,41.00642],[70.54223,40.98787],[70.57501,40.98941],[70.6721,40.90555],[70.68112,40.90612]]],[[[70.74189,39.86319],[70.53651,39.89155],[70.52631,39.86989],[70.54998,39.85137],[70.59667,39.83542],[70.63105,39.77923],[70.74189,39.86319]]]]}},{type:"Feature",properties:{iso1A2:"TK",iso1A3:"TKL",iso1N3:"772",wikidata:"Q36823",nameEn:"Tokelau",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["690"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.75195,-10.12005],[-167.75329,-7.52784],[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005]]]]}},{type:"Feature",properties:{iso1A2:"TL",iso1A3:"TLS",iso1N3:"626",wikidata:"Q574",nameEn:"East Timor",aliases:["Timor-Leste","TP"],groups:["035","142"],driveSide:"left",callingCodes:["670"]},geometry:{type:"MultiPolygon",coordinates:[[[[124.46701,-9.13002],[124.94011,-8.85617],[124.97742,-9.08128],[125.11764,-8.96359],[125.18632,-9.03142],[125.18907,-9.16434],[125.09434,-9.19669],[125.04044,-9.17093],[124.97892,-9.19281],[125.09025,-9.46406],[125.68138,-9.85176],[127.55165,-9.05052],[127.42116,-8.22471],[125.87691,-8.31789],[125.65946,-8.06136],[125.31127,-8.22976],[124.92337,-8.75859],[124.33472,-9.11416],[124.04628,-9.22671],[124.04286,-9.34243],[124.10539,-9.41206],[124.14517,-9.42324],[124.21247,-9.36904],[124.28115,-9.42189],[124.28115,-9.50453],[124.3535,-9.48493],[124.35258,-9.43002],[124.38554,-9.3582],[124.45971,-9.30263],[124.46701,-9.13002]]]]}},{type:"Feature",properties:{iso1A2:"TM",iso1A3:"TKM",iso1N3:"795",wikidata:"Q874",nameEn:"Turkmenistan",groups:["143","142"],callingCodes:["993"]},geometry:{type:"MultiPolygon",coordinates:[[[[60.5078,41.21694],[60.06581,41.4363],[60.18117,41.60082],[60.06032,41.76287],[60.08504,41.80997],[60.33223,41.75058],[59.95046,41.97966],[60.0356,42.01028],[60.04659,42.08982],[59.96419,42.1428],[60.00539,42.212],[59.94633,42.27655],[59.4341,42.29738],[59.2955,42.37064],[59.17317,42.52248],[58.93422,42.5407],[58.6266,42.79314],[58.57991,42.64988],[58.27504,42.69632],[58.14321,42.62159],[58.29427,42.56497],[58.51674,42.30348],[58.40688,42.29535],[58.3492,42.43335],[57.99214,42.50021],[57.90975,42.4374],[57.92897,42.24047],[57.84932,42.18555],[57.6296,42.16519],[57.30275,42.14076],[57.03633,41.92043],[56.96218,41.80383],[57.03359,41.41777],[57.13796,41.36625],[57.03423,41.25435],[56.00314,41.32584],[55.45471,41.25609],[54.95182,41.92424],[54.20635,42.38477],[52.97575,42.1308],[52.47884,41.78034],[52.26048,41.69249],[51.7708,40.29239],[53.89734,37.3464],[54.24565,37.32047],[54.36211,37.34912],[54.58664,37.45809],[54.67247,37.43532],[54.77822,37.51597],[54.81804,37.61285],[54.77684,37.62264],[54.851,37.75739],[55.13412,37.94705],[55.44152,38.08564],[55.76561,38.12238],[55.97847,38.08024],[56.33278,38.08132],[56.32454,38.18502],[56.43303,38.26054],[56.62255,38.24005],[56.73928,38.27887],[57.03453,38.18717],[57.21169,38.28965],[57.37236,38.09321],[57.35042,37.98546],[57.79534,37.89299],[58.21399,37.77281],[58.22999,37.6856],[58.39959,37.63134],[58.47786,37.6433],[58.5479,37.70526],[58.6921,37.64548],[58.9338,37.67374],[59.22905,37.51161],[59.33507,37.53146],[59.39797,37.47892],[59.39385,37.34257],[59.55178,37.13594],[59.74678,37.12499],[60.00768,37.04102],[60.34767,36.63214],[61.14516,36.64644],[61.18187,36.55348],[61.1393,36.38782],[61.22719,36.12759],[61.12007,35.95992],[61.22444,35.92879],[61.26152,35.80749],[61.22719,35.67038],[61.27371,35.61482],[61.58742,35.43803],[61.77693,35.41341],[61.97743,35.4604],[62.05709,35.43803],[62.15871,35.33278],[62.29191,35.25964],[62.29878,35.13312],[62.48006,35.28796],[62.62288,35.22067],[62.74098,35.25432],[62.90853,35.37086],[63.0898,35.43131],[63.12276,35.53196],[63.10079,35.63024],[63.23262,35.67487],[63.10318,35.81782],[63.12276,35.86208],[63.29579,35.85985],[63.53475,35.90881],[63.56496,35.95106],[63.98519,36.03773],[64.05385,36.10433],[64.43288,36.24401],[64.57295,36.34362],[64.62514,36.44311],[64.61141,36.6351],[64.97945,37.21913],[65.51778,37.23881],[65.64263,37.34388],[65.64137,37.45061],[65.72274,37.55438],[66.30993,37.32409],[66.55743,37.35409],[66.52303,37.39827],[66.65761,37.45497],[66.52852,37.58568],[66.53676,37.80084],[66.67684,37.96776],[66.56697,38.0435],[66.41042,38.02403],[66.24013,38.16238],[65.83913,38.25733],[65.55873,38.29052],[64.32576,38.98691],[64.19086,38.95561],[63.70778,39.22349],[63.6913,39.27666],[62.43337,39.98528],[62.34273,40.43206],[62.11751,40.58242],[61.87856,41.12257],[61.4446,41.29407],[61.39732,41.19873],[61.33199,41.14946],[61.22212,41.14946],[61.03261,41.25691],[60.5078,41.21694]]]]}},{type:"Feature",properties:{iso1A2:"TN",iso1A3:"TUN",iso1N3:"788",wikidata:"Q948",nameEn:"Tunisia",groups:["015","002"],callingCodes:["216"]},geometry:{type:"MultiPolygon",coordinates:[[[[11.2718,37.6713],[7.89009,38.19924],[8.59123,37.14286],[8.64044,36.9401],[8.62972,36.86499],[8.67706,36.8364],[8.57613,36.78062],[8.46537,36.7706],[8.47609,36.66607],[8.16167,36.48817],[8.18936,36.44939],[8.40731,36.42208],[8.2626,35.91733],[8.26472,35.73669],[8.35371,35.66373],[8.36086,35.47774],[8.30329,35.29884],[8.47318,35.23376],[8.3555,35.10007],[8.30727,34.95378],[8.25189,34.92009],[8.29655,34.72798],[8.20482,34.57575],[7.86264,34.3987],[7.81242,34.21841],[7.74207,34.16492],[7.66174,34.20167],[7.52851,34.06493],[7.54088,33.7726],[7.73687,33.42114],[7.83028,33.18851],[8.11433,33.10175],[8.1179,33.05086],[8.31895,32.83483],[8.35999,32.50101],[9.07483,32.07865],[9.55544,30.23971],[9.76848,30.34366],[9.88152,30.34074],[10.29516,30.90337],[10.12239,31.42098],[10.31364,31.72648],[10.48497,31.72956],[10.62788,31.96629],[10.7315,31.97235],[11.04234,32.2145],[11.53898,32.4138],[11.57828,32.48013],[11.46037,32.6307],[11.51549,33.09826],[11.55852,33.1409],[11.56255,33.16754],[11.66543,33.34642],[11.2718,37.6713]]]]}},{type:"Feature",properties:{iso1A2:"TO",iso1A3:"TON",iso1N3:"776",wikidata:"Q678",nameEn:"Tonga",groups:["061","009"],driveSide:"left",callingCodes:["676"]},geometry:{type:"MultiPolygon",coordinates:[[[[-176.74538,-22.89767],[-180,-22.90585],[-180,-24.21376],[-173.10761,-24.19665],[-173.11048,-23.23027],[-173.13438,-14.94228],[-174.17905,-14.94502],[-176.76826,-14.95183],[-176.74538,-22.89767]]]]}},{type:"Feature",properties:{iso1A2:"TR",iso1A3:"TUR",iso1N3:"792",wikidata:"Q43",nameEn:"Turkey",groups:["145","142"],callingCodes:["90"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.54366,41.52185],[40.89217,41.72528],[34.8305,42.4581],[28.32297,41.98371],[28.02971,41.98066],[27.91479,41.97902],[27.83492,41.99709],[27.81235,41.94803],[27.69949,41.97515],[27.55191,41.90928],[27.52379,41.93756],[27.45478,41.96591],[27.27411,42.10409],[27.22376,42.10152],[27.19251,42.06028],[27.08486,42.08735],[27.03277,42.0809],[26.95638,42.00741],[26.79143,41.97386],[26.62996,41.97644],[26.56051,41.92995],[26.57961,41.90024],[26.53968,41.82653],[26.36952,41.82265],[26.33589,41.76802],[26.32952,41.73637],[26.35957,41.71149],[26.47958,41.67037],[26.5209,41.62592],[26.59196,41.60491],[26.59742,41.48058],[26.61767,41.42281],[26.62997,41.34613],[26.5837,41.32131],[26.5209,41.33993],[26.39861,41.25053],[26.32259,41.24929],[26.31928,41.07386],[26.3606,41.02027],[26.33297,40.98388],[26.35894,40.94292],[26.32259,40.94042],[26.28623,40.93005],[26.29441,40.89119],[26.26169,40.9168],[26.20856,40.86048],[26.21351,40.83298],[26.15685,40.80709],[26.12854,40.77339],[26.12495,40.74283],[26.08638,40.73214],[26.0754,40.72772],[26.03489,40.73051],[25.94795,40.72797],[26.04292,40.3958],[25.61285,40.17161],[25.94257,39.39358],[26.43357,39.43096],[26.70773,39.0312],[26.61814,38.81372],[26.21136,38.65436],[26.32173,38.48731],[26.24183,38.44695],[26.21136,38.17558],[27.05537,37.9131],[27.16428,37.72343],[26.99377,37.69034],[26.95583,37.64989],[27.14757,37.32],[27.20312,36.94571],[27.45627,36.9008],[27.24613,36.71622],[27.46117,36.53789],[27.89482,36.69898],[27.95037,36.46155],[28.23708,36.56812],[29.30783,36.01033],[29.48192,36.18377],[29.61002,36.1731],[29.61805,36.14179],[29.69611,36.10365],[29.73302,35.92555],[32.82353,35.70297],[35.51152,36.10954],[35.931,35.92109],[35.98499,35.94107],[36.00514,35.94113],[36.01844,35.92403],[35.99829,35.88242],[36.11827,35.85923],[36.13919,35.83692],[36.14029,35.81015],[36.1623,35.80925],[36.17441,35.92076],[36.19973,35.95195],[36.25366,35.96264],[36.27678,35.94839],[36.29769,35.96086],[36.28338,36.00273],[36.30099,36.00985],[36.33956,35.98687],[36.37474,36.01163],[36.39206,36.22088],[36.4617,36.20461],[36.50463,36.2419],[36.6125,36.22592],[36.68672,36.23677],[36.65653,36.33861],[36.6081,36.33772],[36.54206,36.49539],[36.58829,36.58295],[36.57398,36.65186],[36.62681,36.71189],[36.61581,36.74629],[36.66727,36.82901],[36.99557,36.75997],[36.99886,36.74012],[37.04399,36.73483],[37.04619,36.71101],[37.01647,36.69512],[37.02088,36.66422],[37.08279,36.63495],[37.10894,36.6704],[37.16177,36.66069],[37.21988,36.6736],[37.47253,36.63243],[37.49103,36.66904],[37.68048,36.75065],[37.81974,36.76055],[38.21064,36.91842],[38.38859,36.90064],[38.55908,36.84429],[38.74042,36.70629],[39.03217,36.70911],[39.21538,36.66834],[39.81589,36.75538],[40.69136,37.0996],[40.90856,37.13147],[41.21937,37.07665],[41.515,37.08084],[42.00894,37.17209],[42.18225,37.28569],[42.19301,37.31323],[42.2112,37.32491],[42.22257,37.31395],[42.22381,37.30238],[42.20454,37.28715],[42.21548,37.28026],[42.23683,37.2863],[42.26039,37.27017],[42.2824,37.2798],[42.34735,37.22548],[42.32313,37.17814],[42.35724,37.10998],[42.56725,37.14878],[42.78887,37.38615],[42.93705,37.32015],[43.11403,37.37436],[43.30083,37.30629],[43.33508,37.33105],[43.50787,37.24436],[43.56702,37.25675],[43.63085,37.21957],[43.7009,37.23692],[43.8052,37.22825],[43.82699,37.19477],[43.84878,37.22205],[43.90949,37.22453],[44.02002,37.33229],[44.13521,37.32486],[44.2613,37.25055],[44.27998,37.16501],[44.22239,37.15756],[44.18503,37.09551],[44.25975,36.98119],[44.30645,36.97373],[44.35937,37.02843],[44.35315,37.04955],[44.38117,37.05825],[44.42631,37.05825],[44.63179,37.19229],[44.76698,37.16162],[44.78319,37.1431],[44.7868,37.16644],[44.75986,37.21549],[44.81021,37.2915],[44.58449,37.45018],[44.61401,37.60165],[44.56887,37.6429],[44.62096,37.71985],[44.55498,37.783],[44.45948,37.77065],[44.3883,37.85433],[44.22509,37.88859],[44.42476,38.25763],[44.50115,38.33939],[44.44386,38.38295],[44.38309,38.36117],[44.3119,38.37887],[44.3207,38.49799],[44.32058,38.62752],[44.28065,38.6465],[44.26155,38.71427],[44.30322,38.81581],[44.18863,38.93881],[44.20946,39.13975],[44.1043,39.19842],[44.03667,39.39223],[44.22452,39.4169],[44.29818,39.378],[44.37921,39.4131],[44.42832,39.4131],[44.41849,39.56659],[44.48111,39.61579],[44.47298,39.68788],[44.6137,39.78393],[44.65422,39.72163],[44.71806,39.71124],[44.81043,39.62677],[44.80977,39.65768],[44.75779,39.7148],[44.61845,39.8281],[44.46635,39.97733],[44.26973,40.04866],[44.1778,40.02845],[44.1057,40.03555],[43.92307,40.01787],[43.65688,40.11199],[43.65221,40.14889],[43.71136,40.16673],[43.59928,40.34019],[43.60862,40.43267],[43.54791,40.47413],[43.63664,40.54159],[43.7425,40.66805],[43.74872,40.7365],[43.67712,40.84846],[43.67712,40.93084],[43.58683,40.98961],[43.47319,41.02251],[43.44984,41.0988],[43.4717,41.12611],[43.44973,41.17666],[43.36118,41.2028],[43.23096,41.17536],[43.1945,41.25242],[43.13373,41.25503],[43.21707,41.30331],[43.02956,41.37891],[42.8785,41.50516],[42.84899,41.47265],[42.78995,41.50126],[42.84471,41.58912],[42.72794,41.59714],[42.59202,41.58183],[42.51772,41.43606],[42.26387,41.49346],[41.95134,41.52466],[41.81939,41.43621],[41.7124,41.47417],[41.7148,41.4932],[41.54366,41.52185]]]]}},{type:"Feature",properties:{iso1A2:"TT",iso1A3:"TTO",iso1N3:"780",wikidata:"Q754",nameEn:"Trinidad and Tobago",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 868"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.62505,11.18974],[-62.08693,10.04435],[-60.89962,9.81445],[-60.07172,11.77667],[-61.62505,11.18974]]]]}},{type:"Feature",properties:{iso1A2:"TV",iso1A3:"TUV",iso1N3:"798",wikidata:"Q672",nameEn:"Tuvalu",groups:["061","009"],driveSide:"left",callingCodes:["688"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-5],[174,-11.5],[179.99999,-11.5],[179.99999,-5],[174,-5]]]]}},{type:"Feature",properties:{iso1A2:"TW",iso1A3:"TWN",iso1N3:"158",wikidata:"Q865",nameEn:"Taiwan",groups:["030","142"],callingCodes:["886"]},geometry:{type:"MultiPolygon",coordinates:[[[[123.0791,22.07818],[122.26612,25.98197],[120.49232,25.22863],[118.56434,24.49266],[118.42453,24.54644],[118.35291,24.51645],[118.28244,24.51231],[118.11703,24.39734],[120.69238,21.52331],[123.0791,22.07818]]]]}},{type:"Feature",properties:{iso1A2:"TZ",iso1A3:"TZA",iso1N3:"834",wikidata:"Q924",nameEn:"Tanzania",groups:["014","202","002"],driveSide:"left",callingCodes:["255"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.80408,-0.99911],[30.76635,-0.9852],[30.70631,-1.01175],[30.64166,-1.06601],[30.47194,-1.0555],[30.45116,-1.10641],[30.50889,-1.16412],[30.57123,-1.33264],[30.71974,-1.43244],[30.84079,-1.64652],[30.80802,-1.91477],[30.89303,-2.08223],[30.83915,-2.35795],[30.54501,-2.41404],[30.41789,-2.66266],[30.52747,-2.65841],[30.40662,-2.86151],[30.4987,-2.9573],[30.57926,-2.89791],[30.6675,-2.98987],[30.83823,-2.97837],[30.84165,-3.25152],[30.45915,-3.56532],[30.22042,-4.01738],[30.03323,-4.26631],[29.88172,-4.35743],[29.82885,-4.36153],[29.77289,-4.41733],[29.75109,-4.45836],[29.63827,-4.44681],[29.43673,-4.44845],[29.52552,-6.2731],[30.2567,-7.14121],[30.79243,-8.27382],[31.00796,-8.58615],[31.37533,-8.60769],[31.57147,-8.70619],[31.57147,-8.81388],[31.71158,-8.91386],[31.81587,-8.88618],[31.94663,-8.93846],[31.94196,-9.02303],[31.98866,-9.07069],[32.08206,-9.04609],[32.16146,-9.05993],[32.25486,-9.13371],[32.43543,-9.11988],[32.49147,-9.14754],[32.53661,-9.24281],[32.75611,-9.28583],[32.76233,-9.31963],[32.95389,-9.40138],[32.99397,-9.36712],[33.14925,-9.49322],[33.31581,-9.48554],[33.48052,-9.62442],[33.76677,-9.58516],[33.93298,-9.71647],[33.9638,-9.62206],[33.95829,-9.54066],[34.03865,-9.49398],[34.54499,-10.0678],[34.51911,-10.12279],[34.57581,-10.56271],[34.65946,-10.6828],[34.67047,-10.93796],[34.61161,-11.01611],[34.63305,-11.11731],[34.79375,-11.32245],[34.91153,-11.39799],[34.96296,-11.57354],[35.63599,-11.55927],[35.82767,-11.41081],[36.19094,-11.57593],[36.19094,-11.70008],[36.62068,-11.72884],[36.80309,-11.56836],[37.3936,-11.68949],[37.76614,-11.53352],[37.8388,-11.3123],[37.93618,-11.26228],[38.21598,-11.27289],[38.47258,-11.4199],[38.88996,-11.16978],[39.24395,-11.17433],[39.58249,-10.96043],[40.00295,-10.80255],[40.44265,-10.4618],[40.74206,-10.25691],[40.14328,-4.64201],[39.62121,-4.68136],[39.44306,-4.93877],[39.21631,-4.67835],[37.81321,-3.69179],[37.75036,-3.54243],[37.63099,-3.50723],[37.5903,-3.42735],[37.71745,-3.304],[37.67199,-3.06222],[34.0824,-1.02264],[34.03084,-1.05101],[34.02286,-1.00779],[33.93107,-0.99298],[30.80408,-0.99911]]]]}},{type:"Feature",properties:{iso1A2:"UA",iso1A3:"UKR",iso1N3:"804",wikidata:"Q212",nameEn:"Ukraine",groups:["151","150"],callingCodes:["380"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.57318,46.10317],[33.61467,46.13561],[33.63854,46.14147],[33.61517,46.22615],[33.646,46.23028],[33.74047,46.18555],[33.79715,46.20482],[33.85234,46.19863],[33.91549,46.15938],[34.05272,46.10838],[34.07311,46.11769],[34.12929,46.10494],[34.181,46.06804],[34.25111,46.0532],[34.33912,46.06114],[34.41221,46.00245],[34.44155,45.95995],[34.48729,45.94267],[34.52011,45.95097],[34.55889,45.99347],[34.60861,45.99347],[34.66679,45.97136],[34.75479,45.90705],[34.80153,45.90047],[34.79905,45.81009],[34.96015,45.75634],[35.23066,45.79231],[37.62608,46.82615],[38.12112,46.86078],[38.3384,46.98085],[38.22955,47.12069],[38.23049,47.2324],[38.32112,47.2585],[38.33074,47.30508],[38.22225,47.30788],[38.28954,47.39255],[38.28679,47.53552],[38.35062,47.61631],[38.76379,47.69346],[38.79628,47.81109],[38.87979,47.87719],[39.73935,47.82876],[39.82213,47.96396],[39.77544,48.04206],[39.88256,48.04482],[39.83724,48.06501],[39.94847,48.22811],[40.00752,48.22445],[39.99241,48.31768],[39.97325,48.31399],[39.9693,48.29904],[39.95248,48.29972],[39.91465,48.26743],[39.90041,48.3049],[39.84273,48.30947],[39.84136,48.33321],[39.94847,48.35055],[39.88794,48.44226],[39.86196,48.46633],[39.84548,48.57821],[39.79764,48.58668],[39.67226,48.59368],[39.71765,48.68673],[39.73104,48.7325],[39.79466,48.83739],[39.97182,48.79398],[40.08168,48.87443],[40.03636,48.91957],[39.98967,48.86901],[39.78368,48.91596],[39.74874,48.98675],[39.72649,48.9754],[39.71353,48.98959],[39.6683,48.99454],[39.6836,49.05121],[39.93437,49.05709],[40.01988,49.1761],[40.22176,49.25683],[40.18331,49.34996],[40.14912,49.37681],[40.1141,49.38798],[40.03087,49.45452],[40.03636,49.52321],[40.16683,49.56865],[40.13249,49.61672],[39.84548,49.56064],[39.65047,49.61761],[39.59142,49.73758],[39.44496,49.76067],[39.27968,49.75976],[39.1808,49.88911],[38.9391,49.79524],[38.90477,49.86787],[38.73311,49.90238],[38.68677,50.00904],[38.65688,49.97176],[38.35408,50.00664],[38.32524,50.08866],[38.18517,50.08161],[38.21675,49.98104],[38.02999,49.90592],[38.02999,49.94482],[37.90776,50.04194],[37.79515,50.08425],[37.75807,50.07896],[37.61113,50.21976],[37.62879,50.24481],[37.62486,50.29966],[37.47243,50.36277],[37.48204,50.46079],[37.08468,50.34935],[36.91762,50.34963],[36.69377,50.26982],[36.64571,50.218],[36.56655,50.2413],[36.58371,50.28563],[36.47817,50.31457],[36.30101,50.29088],[36.20763,50.3943],[36.06893,50.45205],[35.8926,50.43829],[35.80388,50.41356],[35.73659,50.35489],[35.61711,50.35707],[35.58003,50.45117],[35.47463,50.49247],[35.39464,50.64751],[35.48116,50.66405],[35.47704,50.77274],[35.41367,50.80227],[35.39307,50.92145],[35.32598,50.94524],[35.40837,51.04119],[35.31774,51.08434],[35.20375,51.04723],[35.12685,51.16191],[35.14058,51.23162],[34.97304,51.2342],[34.82472,51.17483],[34.6874,51.18],[34.6613,51.25053],[34.38802,51.2746],[34.31661,51.23936],[34.23009,51.26429],[34.33446,51.363],[34.22048,51.4187],[34.30562,51.5205],[34.17599,51.63253],[34.07765,51.67065],[34.42922,51.72852],[34.41136,51.82793],[34.09413,52.00835],[34.11199,52.14087],[34.05239,52.20132],[33.78789,52.37204],[33.55718,52.30324],[33.48027,52.31499],[33.51323,52.35779],[33.18913,52.3754],[32.89937,52.2461],[32.85405,52.27888],[32.69475,52.25535],[32.54781,52.32423],[32.3528,52.32842],[32.38988,52.24946],[32.33083,52.23685],[32.34044,52.1434],[32.2777,52.10266],[32.23331,52.08085],[32.08813,52.03319],[31.92159,52.05144],[31.96141,52.08015],[31.85018,52.11305],[31.81722,52.09955],[31.7822,52.11406],[31.38326,52.12991],[31.25142,52.04131],[31.13332,52.1004],[30.95589,52.07775],[30.90897,52.00699],[30.76443,51.89739],[30.68804,51.82806],[30.51946,51.59649],[30.64992,51.35014],[30.56203,51.25655],[30.36153,51.33984],[30.34642,51.42555],[30.17888,51.51025],[29.77376,51.4461],[29.7408,51.53417],[29.54372,51.48372],[29.49773,51.39814],[29.42357,51.4187],[29.32881,51.37843],[29.25191,51.49828],[29.25603,51.57089],[29.20659,51.56918],[29.16402,51.64679],[29.1187,51.65872],[28.99098,51.56833],[28.95528,51.59222],[28.81795,51.55552],[28.76027,51.48802],[28.78224,51.45294],[28.75615,51.41442],[28.73143,51.46236],[28.69161,51.44695],[28.64429,51.5664],[28.47051,51.59734],[28.37592,51.54505],[28.23452,51.66988],[28.10658,51.57857],[27.95827,51.56065],[27.91844,51.61952],[27.85253,51.62293],[27.76052,51.47604],[27.67125,51.50854],[27.71932,51.60672],[27.55727,51.63486],[27.51058,51.5854],[27.47212,51.61184],[27.24828,51.60161],[27.26613,51.65957],[27.20948,51.66713],[27.20602,51.77291],[26.99422,51.76933],[26.9489,51.73788],[26.80043,51.75777],[26.69759,51.82284],[26.46962,51.80501],[26.39367,51.87315],[26.19084,51.86781],[26.00408,51.92967],[25.83217,51.92587],[25.80574,51.94556],[25.73673,51.91973],[25.46163,51.92205],[25.20228,51.97143],[24.98784,51.91273],[24.37123,51.88222],[24.29021,51.80841],[24.3163,51.75063],[24.13075,51.66979],[23.99907,51.58369],[23.8741,51.59734],[23.91118,51.63316],[23.7766,51.66809],[23.60906,51.62122],[23.6736,51.50255],[23.62751,51.50512],[23.69905,51.40871],[23.63858,51.32182],[23.80678,51.18405],[23.90376,51.07697],[23.92217,51.00836],[24.04576,50.90196],[24.14524,50.86128],[24.0952,50.83262],[23.99254,50.83847],[23.95925,50.79271],[24.0595,50.71625],[24.0996,50.60752],[24.07048,50.5071],[24.03668,50.44507],[23.99563,50.41289],[23.79445,50.40481],[23.71382,50.38248],[23.67635,50.33385],[23.28221,50.0957],[22.99329,49.84249],[22.83179,49.69875],[22.80261,49.69098],[22.78304,49.65543],[22.64534,49.53094],[22.69444,49.49378],[22.748,49.32759],[22.72009,49.20288],[22.86336,49.10513],[22.89122,49.00725],[22.56155,49.08865],[22.54338,49.01424],[22.48296,48.99172],[22.42934,48.92857],[22.34151,48.68893],[22.21379,48.6218],[22.16023,48.56548],[22.14689,48.4005],[22.2083,48.42534],[22.38133,48.23726],[22.49806,48.25189],[22.59007,48.15121],[22.58733,48.10813],[22.66835,48.09162],[22.73427,48.12005],[22.81804,48.11363],[22.87847,48.04665],[22.84276,47.98602],[22.89849,47.95851],[22.94301,47.96672],[22.92241,48.02002],[23.0158,47.99338],[23.08858,48.00716],[23.1133,48.08061],[23.15999,48.12188],[23.27397,48.08245],[23.33577,48.0237],[23.4979,47.96858],[23.52803,48.01818],[23.5653,48.00499],[23.63894,48.00293],[23.66262,47.98786],[23.75188,47.99705],[23.80904,47.98142],[23.8602,47.9329],[23.89352,47.94512],[23.94192,47.94868],[23.96337,47.96672],[23.98553,47.96076],[24.00801,47.968],[24.02999,47.95087],[24.06466,47.95317],[24.11281,47.91487],[24.22566,47.90231],[24.34926,47.9244],[24.43578,47.97131],[24.61994,47.95062],[24.70632,47.84428],[24.81893,47.82031],[24.88896,47.7234],[25.11144,47.75203],[25.23778,47.89403],[25.63878,47.94924],[25.77723,47.93919],[26.05901,47.9897],[26.17711,47.99246],[26.33504,48.18418],[26.55202,48.22445],[26.62823,48.25804],[26.6839,48.35828],[26.79239,48.29071],[26.82809,48.31629],[26.71274,48.40388],[26.85556,48.41095],[26.93384,48.36558],[27.03821,48.37653],[27.0231,48.42485],[27.08078,48.43214],[27.13434,48.37288],[27.27855,48.37534],[27.32159,48.4434],[27.37604,48.44398],[27.37741,48.41026],[27.44333,48.41209],[27.46942,48.454],[27.5889,48.49224],[27.59027,48.46311],[27.6658,48.44034],[27.74422,48.45926],[27.79225,48.44244],[27.81902,48.41874],[27.87533,48.4037],[27.88391,48.36699],[27.95883,48.32368],[28.04527,48.32661],[28.09873,48.3124],[28.07504,48.23494],[28.17666,48.25963],[28.19314,48.20749],[28.2856,48.23202],[28.32508,48.23384],[28.35519,48.24957],[28.36996,48.20543],[28.34912,48.1787],[28.30586,48.1597],[28.30609,48.14018],[28.34009,48.13147],[28.38712,48.17567],[28.43701,48.15832],[28.42454,48.12047],[28.48428,48.0737],[28.53921,48.17453],[28.69896,48.13106],[28.85232,48.12506],[28.8414,48.03392],[28.9306,47.96255],[29.1723,47.99013],[29.19839,47.89261],[29.27804,47.88893],[29.20663,47.80367],[29.27255,47.79953],[29.22242,47.73607],[29.22414,47.60012],[29.11743,47.55001],[29.18603,47.43387],[29.3261,47.44664],[29.39889,47.30179],[29.47854,47.30366],[29.48678,47.36043],[29.5733,47.36508],[29.59665,47.25521],[29.54996,47.24962],[29.57696,47.13581],[29.49732,47.12878],[29.53044,47.07851],[29.61038,47.09932],[29.62137,47.05069],[29.57056,46.94766],[29.72986,46.92234],[29.75458,46.8604],[29.87405,46.88199],[29.98814,46.82358],[29.94522,46.80055],[29.9743,46.75325],[29.94409,46.56002],[29.88916,46.54302],[30.02511,46.45132],[30.16794,46.40967],[30.09103,46.38694],[29.94114,46.40114],[29.88329,46.35851],[29.74496,46.45605],[29.66359,46.4215],[29.6763,46.36041],[29.5939,46.35472],[29.49914,46.45889],[29.35357,46.49505],[29.24886,46.37912],[29.23547,46.55435],[29.02409,46.49582],[29.01241,46.46177],[28.9306,46.45699],[29.004,46.31495],[28.98478,46.31803],[28.94953,46.25852],[29.06656,46.19716],[28.94643,46.09176],[29.00613,46.04962],[28.98004,46.00385],[28.74383,45.96664],[28.78503,45.83475],[28.69852,45.81753],[28.70401,45.78019],[28.52823,45.73803],[28.47879,45.66994],[28.51587,45.6613],[28.54196,45.58062],[28.49252,45.56716],[28.51449,45.49982],[28.43072,45.48538],[28.41836,45.51715],[28.30201,45.54744],[28.21139,45.46895],[28.28504,45.43907],[28.34554,45.32102],[28.5735,45.24759],[28.71358,45.22631],[28.78911,45.24179],[28.81383,45.3384],[28.94292,45.28045],[28.96077,45.33164],[29.24779,45.43388],[29.42632,45.44545],[29.59798,45.38857],[29.68175,45.26885],[29.65428,45.25629],[29.69272,45.19227],[30.04414,45.08461],[31.62627,45.50633],[33.54017,46.0123],[33.59087,46.06013],[33.57318,46.10317]]]]}},{type:"Feature",properties:{iso1A2:"UG",iso1A3:"UGA",iso1N3:"800",wikidata:"Q1036",nameEn:"Uganda",groups:["014","202","002"],driveSide:"left",callingCodes:["256"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.93107,-0.99298],[33.9264,-0.54188],[33.98449,-0.13079],[33.90936,0.10581],[34.10067,0.36372],[34.08727,0.44713],[34.11408,0.48884],[34.13493,0.58118],[34.20196,0.62289],[34.27345,0.63182],[34.31516,0.75693],[34.40041,0.80266],[34.43349,0.85254],[34.52369,1.10692],[34.57427,1.09868],[34.58029,1.14712],[34.67562,1.21265],[34.80223,1.22754],[34.82606,1.26626],[34.82606,1.30944],[34.7918,1.36752],[34.87819,1.5596],[34.92734,1.56109],[34.9899,1.6668],[34.98692,1.97348],[34.90947,2.42447],[34.95267,2.47209],[34.77244,2.70272],[34.78137,2.76223],[34.73967,2.85447],[34.65774,2.8753],[34.60114,2.93034],[34.56242,3.11478],[34.45815,3.18319],[34.40006,3.37949],[34.41794,3.44342],[34.39112,3.48802],[34.44922,3.51627],[34.45815,3.67385],[34.15429,3.80464],[34.06046,4.15235],[33.9873,4.23316],[33.51264,3.75068],[33.18356,3.77812],[33.02852,3.89296],[32.89746,3.81339],[32.72021,3.77327],[32.41337,3.748],[32.20782,3.6053],[32.19888,3.50867],[32.08866,3.53543],[32.08491,3.56287],[32.05187,3.589],[31.95907,3.57408],[31.96205,3.6499],[31.86821,3.78664],[31.81459,3.82083],[31.72075,3.74354],[31.50776,3.63652],[31.50478,3.67814],[31.29476,3.8015],[31.16666,3.79853],[30.97601,3.693],[30.85153,3.48867],[30.94081,3.50847],[30.93486,3.40737],[30.84251,3.26908],[30.77101,3.04897],[30.8574,2.9508],[30.8857,2.83923],[30.75612,2.5863],[30.74271,2.43601],[30.83059,2.42559],[30.91102,2.33332],[30.96911,2.41071],[31.06593,2.35862],[31.07934,2.30207],[31.12104,2.27676],[31.1985,2.29462],[31.20148,2.2217],[31.28042,2.17853],[31.30127,2.11006],[30.48503,1.21675],[30.24671,1.14974],[30.22139,0.99635],[30.1484,0.89805],[29.98307,0.84295],[29.95477,0.64486],[29.97413,0.52124],[29.87284,0.39166],[29.81922,0.16824],[29.77454,0.16675],[29.7224,0.07291],[29.72687,-0.08051],[29.65091,-0.46777],[29.67474,-0.47969],[29.67176,-0.55714],[29.62708,-0.71055],[29.63006,-0.8997],[29.58388,-0.89821],[29.59061,-1.39016],[29.82657,-1.31187],[29.912,-1.48269],[30.16369,-1.34303],[30.35212,-1.06896],[30.47194,-1.0555],[30.64166,-1.06601],[30.70631,-1.01175],[30.76635,-0.9852],[30.80408,-0.99911],[33.93107,-0.99298]]]]}},{type:"Feature",properties:{iso1A2:"UM",iso1A3:"UMI",iso1N3:"581",wikidata:"Q16645",nameEn:"United States Minor Outlying Islands",country:"US",groups:["057","009"]},geometry:{type:"MultiPolygon",coordinates:[[[[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631]]],[[[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722],[-161.04969,-1.36251]]],[[[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462]]],[[[-170.65691,16.57199],[-168.87689,16.01159],[-169.2329,17.4933],[-170.65691,16.57199]]],[[[-176.29741,29.09786],[-177.77531,29.29793],[-177.5224,27.7635],[-176.29741,29.09786]]],[[[-74.7289,18.71009],[-75.71816,18.46438],[-74.76465,18.06252],[-74.7289,18.71009]]],[[[167.34779,18.97692],[166.67967,20.14834],[165.82549,18.97692],[167.34779,18.97692]]]]}},{type:"Feature",properties:{iso1A2:"US",iso1A3:"USA",iso1N3:"840",wikidata:"Q30",nameEn:"United States of America",groups:["021","003","019"],roadSpeedUnit:"mph",callingCodes:["1"]},geometry:{type:"MultiPolygon",coordinates:[[[[-177.8563,29.18961],[-179.49839,27.86265],[-151.6784,9.55515],[-154.05867,45.51124],[-177.5224,27.7635],[-177.8563,29.18961]]],[[[169.34848,52.47228],[180,51.0171],[179.84401,55.10087],[169.34848,52.47228]]],[[[-168.95635,65.98512],[-169.03888,65.48473],[-172.76104,63.77445],[-179.55295,57.62081],[-179.55295,50.81807],[-133.92876,54.62289],[-130.61931,54.70835],[-130.64499,54.76912],[-130.44184,54.85377],[-130.27203,54.97174],[-130.18765,55.07744],[-130.08035,55.21556],[-129.97513,55.28029],[-130.15373,55.74895],[-130.00857,55.91344],[-130.00093,56.00325],[-130.10173,56.12178],[-130.33965,56.10849],[-130.77769,56.36185],[-131.8271,56.62247],[-133.38523,58.42773],[-133.84645,58.73543],[-134.27175,58.8634],[-134.48059,59.13231],[-134.55699,59.1297],[-134.7047,59.2458],[-135.00267,59.28745],[-135.03069,59.56208],[-135.48007,59.79937],[-136.31566,59.59083],[-136.22381,59.55526],[-136.33727,59.44466],[-136.47323,59.46617],[-136.52365,59.16752],[-136.82619,59.16198],[-137.4925,58.89415],[-137.60623,59.24465],[-138.62145,59.76431],[-138.71149,59.90728],[-139.05365,59.99655],[-139.20603,60.08896],[-139.05831,60.35205],[-139.68991,60.33693],[-139.98024,60.18027],[-140.45648,60.30919],[-140.5227,60.22077],[-141.00116,60.30648],[-140.97446,84.39275],[-168.25765,71.99091],[-168.95635,65.98512]]],[[[-97.13927,25.96583],[-96.92418,25.97377],[-82.02215,24.23074],[-79.89631,24.6597],[-79.14818,27.83105],[-61.98255,37.34815],[-67.16117,44.20069],[-66.93432,44.82597],[-66.96824,44.83078],[-66.98249,44.87071],[-66.96824,44.90965],[-67.0216,44.95333],[-67.11316,45.11176],[-67.15965,45.16179],[-67.19603,45.16771],[-67.20349,45.1722],[-67.22751,45.16344],[-67.27039,45.1934],[-67.29748,45.18173],[-67.29754,45.14865],[-67.34927,45.122],[-67.48201,45.27351],[-67.42394,45.37969],[-67.50578,45.48971],[-67.42144,45.50584],[-67.43815,45.59162],[-67.6049,45.60725],[-67.80705,45.69528],[-67.80653,45.80022],[-67.75654,45.82324],[-67.80961,45.87531],[-67.75196,45.91814],[-67.78111,45.9392],[-67.78578,47.06473],[-67.87993,47.10377],[-67.94843,47.1925],[-68.23244,47.35712],[-68.37458,47.35851],[-68.38332,47.28723],[-68.57914,47.28431],[-68.60575,47.24659],[-68.70125,47.24399],[-68.89222,47.1807],[-69.05039,47.2456],[-69.05073,47.30076],[-69.05148,47.42012],[-69.22119,47.46461],[-69.99966,46.69543],[-70.05812,46.41768],[-70.18547,46.35357],[-70.29078,46.18832],[-70.23855,46.1453],[-70.31025,45.96424],[-70.24694,45.95138],[-70.25976,45.89675],[-70.41523,45.79497],[-70.38934,45.73215],[-70.54019,45.67291],[-70.68516,45.56964],[-70.72651,45.49771],[-70.62518,45.42286],[-70.65383,45.37592],[-70.78372,45.43269],[-70.82638,45.39828],[-70.80236,45.37444],[-70.84816,45.22698],[-70.89864,45.2398],[-70.91169,45.29849],[-70.95193,45.33895],[-71.0107,45.34819],[-71.01866,45.31573],[-71.08364,45.30623],[-71.14568,45.24128],[-71.19723,45.25438],[-71.22338,45.25184],[-71.29371,45.29996],[-71.37133,45.24624],[-71.44252,45.2361],[-71.40364,45.21382],[-71.42778,45.12624],[-71.48735,45.07784],[-71.50067,45.01357],[-73.35025,45.00942],[-74.32699,44.99029],[-74.66689,45.00646],[-74.8447,45.00606],[-74.99101,44.98051],[-75.01363,44.95608],[-75.2193,44.87821],[-75.41441,44.76614],[-75.76813,44.51537],[-75.8217,44.43176],[-75.95947,44.34463],[-76.00018,44.34896],[-76.16285,44.28262],[-76.1664,44.23051],[-76.244,44.19643],[-76.31222,44.19894],[-76.35324,44.13493],[-76.43859,44.09393],[-76.79706,43.63099],[-79.25796,43.54052],[-79.06921,43.26183],[-79.05512,43.25375],[-79.05544,43.21224],[-79.05002,43.20133],[-79.05384,43.17418],[-79.04652,43.16396],[-79.0427,43.13934],[-79.06881,43.12029],[-79.05671,43.10937],[-79.07486,43.07845],[-79.01055,43.06659],[-78.99941,43.05612],[-79.02424,43.01983],[-79.02074,42.98444],[-78.98126,42.97],[-78.96312,42.95509],[-78.93224,42.95229],[-78.90905,42.93022],[-78.90712,42.89733],[-78.93684,42.82887],[-82.67862,41.67615],[-83.11184,41.95671],[-83.14962,42.04089],[-83.12724,42.2376],[-83.09837,42.28877],[-83.07837,42.30978],[-83.02253,42.33045],[-82.82964,42.37355],[-82.64242,42.55594],[-82.58873,42.54984],[-82.57583,42.5718],[-82.51858,42.611],[-82.51063,42.66025],[-82.46613,42.76615],[-82.4826,42.8068],[-82.45331,42.93139],[-82.4253,42.95423],[-82.4146,42.97626],[-82.42469,42.992],[-82.48419,45.30225],[-83.59589,45.82131],[-83.43746,45.99749],[-83.57017,46.105],[-83.83329,46.12169],[-83.90453,46.05922],[-83.95399,46.05634],[-84.1096,46.23987],[-84.09756,46.25512],[-84.11615,46.2681],[-84.11254,46.32329],[-84.13451,46.39218],[-84.11196,46.50248],[-84.12885,46.53068],[-84.17723,46.52753],[-84.1945,46.54061],[-84.2264,46.53337],[-84.26351,46.49508],[-84.29893,46.49127],[-84.34174,46.50683],[-84.42101,46.49853],[-84.4481,46.48972],[-84.47607,46.45225],[-84.55635,46.45974],[-84.85871,46.88881],[-88.37033,48.30586],[-89.48837,48.01412],[-89.57972,48.00023],[-89.77248,48.02607],[-89.89974,47.98109],[-90.07418,48.11043],[-90.56312,48.09488],[-90.56444,48.12184],[-90.75045,48.09143],[-90.87588,48.2484],[-91.08016,48.18096],[-91.25025,48.08522],[-91.43248,48.04912],[-91.45829,48.07454],[-91.58025,48.04339],[-91.55649,48.10611],[-91.70451,48.11805],[-91.71231,48.19875],[-91.86125,48.21278],[-91.98929,48.25409],[-92.05339,48.35958],[-92.14732,48.36578],[-92.202,48.35252],[-92.26662,48.35651],[-92.30939,48.31251],[-92.27167,48.25046],[-92.37185,48.22259],[-92.48147,48.36609],[-92.45588,48.40624],[-92.50712,48.44921],[-92.65606,48.43471],[-92.71323,48.46081],[-92.69927,48.49573],[-92.62747,48.50278],[-92.6342,48.54133],[-92.7287,48.54005],[-92.94973,48.60866],[-93.25391,48.64266],[-93.33946,48.62787],[-93.3712,48.60599],[-93.39758,48.60364],[-93.40693,48.60948],[-93.44472,48.59147],[-93.47022,48.54357],[-93.66382,48.51845],[-93.79267,48.51631],[-93.80939,48.52439],[-93.80676,48.58232],[-93.83288,48.62745],[-93.85769,48.63284],[-94.23215,48.65202],[-94.25104,48.65729],[-94.25172,48.68404],[-94.27153,48.70232],[-94.4174,48.71049],[-94.44258,48.69223],[-94.53826,48.70216],[-94.54885,48.71543],[-94.58903,48.71803],[-94.69335,48.77883],[-94.69669,48.80918],[-94.70486,48.82365],[-94.70087,48.8339],[-94.687,48.84077],[-94.75017,49.09931],[-94.77355,49.11998],[-94.82487,49.29483],[-94.8159,49.32299],[-94.85381,49.32492],[-94.95681,49.37035],[-94.99532,49.36579],[-95.01419,49.35647],[-95.05825,49.35311],[-95.12903,49.37056],[-95.15357,49.384],[-95.15355,48.9996],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999],[-117.03266,49.00056],[-123.32163,49.00419],[-123.0093,48.83186],[-123.0093,48.76586],[-123.26565,48.6959],[-123.15614,48.35395],[-123.50039,48.21223],[-125.03842,48.53282],[-133.98258,38.06389],[-118.48109,32.5991],[-117.1243,32.53427],[-115.88053,32.63624],[-114.71871,32.71894],[-114.76736,32.64094],[-114.80584,32.62028],[-114.81141,32.55543],[-114.79524,32.55731],[-114.82011,32.49609],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-108.20979,31.33316],[-108.20899,31.78534],[-106.529,31.784],[-106.52266,31.77509],[-106.51251,31.76922],[-106.50962,31.76155],[-106.50111,31.75714],[-106.48815,31.74769],[-106.47298,31.75054],[-106.46726,31.75998],[-106.45244,31.76523],[-106.43419,31.75478],[-106.41773,31.75196],[-106.38003,31.73151],[-106.3718,31.71165],[-106.34864,31.69663],[-106.33419,31.66303],[-106.30305,31.62154],[-106.28084,31.56173],[-106.24612,31.54193],[-106.23711,31.51262],[-106.20346,31.46305],[-106.09025,31.40569],[-106.00363,31.39181],[-104.77674,30.4236],[-104.5171,29.64671],[-104.3969,29.57105],[-104.39363,29.55396],[-104.37752,29.54255],[-103.15787,28.93865],[-102.60596,29.8192],[-101.47277,29.7744],[-101.05686,29.44738],[-101.01128,29.36947],[-100.96725,29.3477],[-100.94579,29.34523],[-100.94056,29.33371],[-100.87982,29.296],[-100.79696,29.24688],[-100.67294,29.09744],[-100.63689,28.90812],[-100.59809,28.88197],[-100.52313,28.75598],[-100.5075,28.74066],[-100.51222,28.70679],[-100.50029,28.66117],[-99.55409,27.61314],[-99.51478,27.55836],[-99.52955,27.49747],[-99.50208,27.50021],[-99.48045,27.49016],[-99.482,27.47128],[-99.49744,27.43746],[-99.53573,27.30926],[-99.08477,26.39849],[-99.03053,26.41249],[-99.00546,26.3925],[-98.35126,26.15129],[-98.30491,26.10475],[-98.27075,26.09457],[-98.24603,26.07191],[-97.97017,26.05232],[-97.95155,26.0625],[-97.66511,26.01708],[-97.52025,25.88518],[-97.49828,25.89877],[-97.45669,25.86874],[-97.42511,25.83969],[-97.37332,25.83854],[-97.35946,25.92189],[-97.13927,25.96583]]]]}},{type:"Feature",properties:{iso1A2:"UY",iso1A3:"URY",iso1N3:"858",wikidata:"Q77",nameEn:"Uruguay",groups:["005","419","019"],callingCodes:["598"]},geometry:{type:"MultiPolygon",coordinates:[[[[-57.65132,-30.19229],[-57.61478,-30.25165],[-57.64859,-30.35095],[-57.89115,-30.49572],[-57.8024,-30.77193],[-57.89476,-30.95994],[-57.86729,-31.06352],[-57.9908,-31.34924],[-57.98127,-31.3872],[-58.07569,-31.44916],[-58.0023,-31.53084],[-58.00076,-31.65016],[-58.20252,-31.86966],[-58.10036,-32.25338],[-58.22362,-32.52416],[-58.1224,-32.98842],[-58.40475,-33.11777],[-58.44442,-33.84033],[-58.34425,-34.15035],[-57.83001,-34.69099],[-54.78916,-36.21945],[-52.83257,-34.01481],[-53.37138,-33.74313],[-53.39593,-33.75169],[-53.44031,-33.69344],[-53.52794,-33.68908],[-53.53459,-33.16843],[-53.1111,-32.71147],[-53.37671,-32.57005],[-53.39572,-32.58596],[-53.76024,-32.0751],[-54.17384,-31.86168],[-55.50821,-30.91349],[-55.50841,-30.9027],[-55.51862,-30.89828],[-55.52712,-30.89997],[-55.53276,-30.90218],[-55.53431,-30.89714],[-55.54572,-30.89051],[-55.55218,-30.88193],[-55.55373,-30.8732],[-55.5634,-30.8686],[-55.58866,-30.84117],[-55.87388,-31.05053],[-56.4619,-30.38457],[-56.4795,-30.3899],[-56.49267,-30.39471],[-56.90236,-30.02578],[-57.22502,-30.26121],[-57.65132,-30.19229]]]]}},{type:"Feature",properties:{iso1A2:"UZ",iso1A3:"UZB",iso1N3:"860",wikidata:"Q265",nameEn:"Uzbekistan",groups:["143","142"],callingCodes:["998"]},geometry:{type:"MultiPolygon",coordinates:[[[[65.85194,42.85481],[65.53277,43.31856],[65.18666,43.48835],[64.96464,43.74748],[64.53885,43.56941],[63.34656,43.64003],[62.01711,43.51008],[61.01475,44.41383],[58.59711,45.58671],[55.97842,44.99622],[55.97832,44.99622],[55.97822,44.99617],[55.97811,44.99617],[55.97801,44.99612],[55.97801,44.99607],[55.97791,44.99607],[55.9778,44.99607],[55.9777,44.99601],[55.9777,44.99596],[55.9776,44.99591],[55.97749,44.99591],[55.97739,44.99591],[55.97739,44.99586],[55.97729,44.99586],[55.97718,44.99581],[55.97708,44.99576],[55.97698,44.9957],[55.97698,44.99565],[55.97687,44.9956],[55.97677,44.9956],[55.97677,44.99555],[55.97677,44.9955],[55.97667,44.99545],[55.97656,44.99539],[55.97646,44.99534],[55.97646,44.99529],[55.97636,44.99524],[55.97636,44.99519],[55.97625,44.99514],[55.97615,44.99508],[55.97615,44.99503],[55.97615,44.99498],[55.97615,44.99493],[55.97615,44.99483],[55.97615,44.99477],[55.97605,44.99477],[55.97605,44.99467],[55.97605,44.99462],[55.97605,44.99457],[55.97605,44.99452],[55.97594,44.99446],[55.97584,44.99441],[55.97584,44.99436],[55.97584,44.99431],[55.97584,44.99426],[55.97584,44.99421],[55.97584,44.99415],[55.97584,44.99405],[55.97584,44.994],[55.97584,44.9939],[55.97584,44.99384],[55.97584,44.99374],[55.97584,44.99369],[55.97584,44.99359],[55.97584,44.99353],[55.97584,44.99348],[55.97584,44.99343],[55.97584,44.99338],[55.97584,44.99328],[55.97584,44.99322],[56.00314,41.32584],[57.03423,41.25435],[57.13796,41.36625],[57.03359,41.41777],[56.96218,41.80383],[57.03633,41.92043],[57.30275,42.14076],[57.6296,42.16519],[57.84932,42.18555],[57.92897,42.24047],[57.90975,42.4374],[57.99214,42.50021],[58.3492,42.43335],[58.40688,42.29535],[58.51674,42.30348],[58.29427,42.56497],[58.14321,42.62159],[58.27504,42.69632],[58.57991,42.64988],[58.6266,42.79314],[58.93422,42.5407],[59.17317,42.52248],[59.2955,42.37064],[59.4341,42.29738],[59.94633,42.27655],[60.00539,42.212],[59.96419,42.1428],[60.04659,42.08982],[60.0356,42.01028],[59.95046,41.97966],[60.33223,41.75058],[60.08504,41.80997],[60.06032,41.76287],[60.18117,41.60082],[60.06581,41.4363],[60.5078,41.21694],[61.03261,41.25691],[61.22212,41.14946],[61.33199,41.14946],[61.39732,41.19873],[61.4446,41.29407],[61.87856,41.12257],[62.11751,40.58242],[62.34273,40.43206],[62.43337,39.98528],[63.6913,39.27666],[63.70778,39.22349],[64.19086,38.95561],[64.32576,38.98691],[65.55873,38.29052],[65.83913,38.25733],[66.24013,38.16238],[66.41042,38.02403],[66.56697,38.0435],[66.67684,37.96776],[66.53676,37.80084],[66.52852,37.58568],[66.65761,37.45497],[66.52303,37.39827],[66.55743,37.35409],[66.64699,37.32958],[66.95598,37.40162],[67.08232,37.35469],[67.13039,37.27168],[67.2224,37.24545],[67.2581,37.17216],[67.51868,37.26102],[67.78329,37.1834],[67.8474,37.31594],[67.81566,37.43107],[68.12635,37.93],[68.27159,37.91477],[68.40343,38.19484],[68.13289,38.40822],[68.06274,38.39435],[68.11366,38.47169],[68.05873,38.56087],[68.0807,38.64136],[68.05598,38.71641],[68.12877,38.73677],[68.06948,38.82115],[68.19743,38.85985],[68.09704,39.02589],[67.68915,39.00775],[67.67833,39.14479],[67.33226,39.23739],[67.36522,39.31287],[67.45998,39.315],[67.46822,39.46146],[67.39681,39.52505],[67.46547,39.53564],[67.44899,39.57799],[67.62889,39.60234],[67.70992,39.66156],[68.12053,39.56317],[68.54166,39.53929],[68.61972,39.68905],[68.63071,39.85265],[68.88889,39.87163],[68.93695,39.91167],[68.84906,40.04952],[68.96579,40.06949],[69.01935,40.11466],[69.01523,40.15771],[68.62796,40.07789],[68.52771,40.11676],[68.5332,40.14826],[68.77902,40.20492],[68.79276,40.17555],[68.84357,40.18604],[68.85832,40.20885],[69.04544,40.22904],[69.15659,40.2162],[69.2074,40.21488],[69.30448,40.18774],[69.30104,40.24502],[69.25229,40.26362],[69.24817,40.30357],[69.30808,40.2821],[69.32833,40.29794],[69.33794,40.34819],[69.30774,40.36102],[69.28525,40.41894],[69.27066,40.49274],[69.21063,40.54469],[69.2643,40.57506],[69.3455,40.57988],[69.32834,40.70233],[69.38327,40.7918],[69.53021,40.77621],[69.59441,40.70181],[69.69434,40.62615],[70.36655,40.90296],[70.38028,41.02014],[70.45251,41.04438],[70.80009,40.72825],[70.49871,40.52503],[70.32626,40.45174],[70.37511,40.38605],[70.57149,40.3442],[70.56394,40.26421],[70.62342,40.17396],[70.8607,40.217],[70.9818,40.22392],[70.95789,40.28761],[71.05901,40.28765],[71.13042,40.34106],[71.36663,40.31593],[71.4246,40.28619],[71.51215,40.26943],[71.51549,40.22986],[71.61725,40.20615],[71.61931,40.26775],[71.68386,40.26984],[71.70569,40.20391],[71.69621,40.18492],[71.71719,40.17886],[71.73054,40.14818],[71.82646,40.21872],[71.85002,40.25647],[72.05464,40.27586],[71.96401,40.31907],[72.18648,40.49893],[72.24368,40.46091],[72.40346,40.4007],[72.44191,40.48222],[72.41513,40.50856],[72.38384,40.51535],[72.41714,40.55736],[72.34406,40.60144],[72.40517,40.61917],[72.47795,40.5532],[72.66713,40.5219],[72.66713,40.59076],[72.69579,40.59778],[72.73995,40.58409],[72.74768,40.58051],[72.74862,40.57131],[72.75982,40.57273],[72.74894,40.59592],[72.74866,40.60873],[72.80137,40.67856],[72.84754,40.67229],[72.85372,40.7116],[72.8722,40.71111],[72.93296,40.73089],[72.99133,40.76457],[73.0612,40.76678],[73.13412,40.79122],[73.13267,40.83512],[73.01869,40.84681],[72.94454,40.8094],[72.84291,40.85512],[72.68157,40.84942],[72.59136,40.86947],[72.55109,40.96046],[72.48742,40.97136],[72.45206,41.03018],[72.38511,41.02785],[72.36138,41.04384],[72.34757,41.06104],[72.34026,41.04539],[72.324,41.03381],[72.18339,40.99571],[72.17594,41.02377],[72.21061,41.05607],[72.1792,41.10621],[72.14864,41.13363],[72.17594,41.15522],[72.16433,41.16483],[72.10745,41.15483],[72.07249,41.11739],[71.85964,41.19081],[71.91457,41.2982],[71.83914,41.3546],[71.76625,41.4466],[71.71132,41.43012],[71.73054,41.54713],[71.65914,41.49599],[71.6787,41.42111],[71.57227,41.29175],[71.46688,41.31883],[71.43814,41.19644],[71.46148,41.13958],[71.40198,41.09436],[71.34877,41.16807],[71.27187,41.11015],[71.25813,41.18796],[71.11806,41.15359],[71.02193,41.19494],[70.9615,41.16393],[70.86263,41.23833],[70.77885,41.24813],[70.78572,41.36419],[70.67586,41.47953],[70.48909,41.40335],[70.17682,41.5455],[70.69777,41.92554],[71.28719,42.18033],[71.13263,42.28356],[70.94483,42.26238],[69.49545,41.545],[69.45751,41.56863],[69.39485,41.51518],[69.45081,41.46246],[69.37468,41.46555],[69.35554,41.47211],[69.29778,41.43673],[69.25059,41.46693],[69.23332,41.45847],[69.22671,41.46298],[69.20439,41.45391],[69.18528,41.45175],[69.17701,41.43769],[69.15137,41.43078],[69.05006,41.36183],[69.01308,41.22804],[68.7217,41.05025],[68.73945,40.96989],[68.65662,40.93861],[68.62221,41.03019],[68.49983,40.99669],[68.58444,40.91447],[68.63,40.59358],[68.49983,40.56437],[67.96736,40.83798],[68.1271,41.0324],[68.08273,41.08148],[67.98511,41.02794],[67.9644,41.14611],[66.69129,41.1311],[66.53302,41.87388],[66.00546,41.94455],[66.09482,42.93426],[65.85194,42.85481]],[[70.68112,40.90612],[70.6721,40.90555],[70.57501,40.98941],[70.54223,40.98787],[70.56077,41.00642],[70.6158,40.97661],[70.68112,40.90612]]],[[[71.21139,40.03369],[71.12218,40.03052],[71.06305,40.1771],[71.00236,40.18154],[71.01035,40.05481],[71.11037,40.01984],[71.11668,39.99291],[71.09063,39.99],[71.10501,39.95568],[71.04979,39.89808],[71.10531,39.91354],[71.16101,39.88423],[71.23067,39.93581],[71.1427,39.95026],[71.21139,40.03369]]],[[[71.86463,39.98598],[71.78838,40.01404],[71.71511,39.96348],[71.7504,39.93701],[71.84316,39.95582],[71.86463,39.98598]]]]}},{type:"Feature",properties:{iso1A2:"VA",iso1A3:"VAT",iso1N3:"336",wikidata:"Q237",nameEn:"Vatican City",aliases:["Holy See"],groups:["039","150"],callingCodes:["379","39 06"]},geometry:{type:"MultiPolygon",coordinates:[[[[12.45181,41.90056],[12.45446,41.90028],[12.45435,41.90143],[12.45626,41.90172],[12.45691,41.90125],[12.4577,41.90115],[12.45834,41.90174],[12.45826,41.90281],[12.45755,41.9033],[12.45762,41.9058],[12.45561,41.90629],[12.45543,41.90738],[12.45091,41.90625],[12.44984,41.90545],[12.44815,41.90326],[12.44582,41.90194],[12.44834,41.90095],[12.45181,41.90056]]]]}},{type:"Feature",properties:{iso1A2:"VC",iso1A3:"VCT",iso1N3:"670",wikidata:"Q757",nameEn:"St. Vincent and the Grenadines",aliases:["WV"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 784"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.73897,12.61191],[-61.38256,12.52991],[-61.13395,12.51526],[-60.70539,13.41452],[-61.43129,13.68336],[-61.73897,12.61191]]]]}},{type:"Feature",properties:{iso1A2:"VE",iso1A3:"VEN",iso1N3:"862",wikidata:"Q717",nameEn:"Venezuela",aliases:["YV"],groups:["005","419","019"],callingCodes:["58"]},geometry:{type:"MultiPolygon",coordinates:[[[[-71.22331,13.01387],[-70.92579,11.96275],[-71.3275,11.85],[-71.9675,11.65536],[-72.24983,11.14138],[-72.4767,11.1117],[-72.88002,10.44309],[-72.98085,9.85253],[-73.36905,9.16636],[-73.02119,9.27584],[-72.94052,9.10663],[-72.77415,9.10165],[-72.65474,8.61428],[-72.4042,8.36513],[-72.36987,8.19976],[-72.35163,8.01163],[-72.39137,8.03534],[-72.47213,7.96106],[-72.48801,7.94329],[-72.48183,7.92909],[-72.47042,7.92306],[-72.45806,7.91141],[-72.46183,7.90682],[-72.44454,7.86031],[-72.46763,7.79518],[-72.47827,7.65604],[-72.45321,7.57232],[-72.47415,7.48928],[-72.43132,7.40034],[-72.19437,7.37034],[-72.04895,7.03837],[-71.82441,7.04314],[-71.44118,7.02116],[-71.42212,7.03854],[-71.37234,7.01588],[-71.03941,6.98163],[-70.7596,7.09799],[-70.10716,6.96516],[-69.41843,6.1072],[-67.60654,6.2891],[-67.4625,6.20625],[-67.43513,5.98835],[-67.58558,5.84537],[-67.63914,5.64963],[-67.59141,5.5369],[-67.83341,5.31104],[-67.85358,4.53249],[-67.62671,3.74303],[-67.50067,3.75812],[-67.30945,3.38393],[-67.85862,2.86727],[-67.85862,2.79173],[-67.65696,2.81691],[-67.21967,2.35778],[-66.85795,1.22998],[-66.28507,0.74585],[-65.6727,1.01353],[-65.50158,0.92086],[-65.57288,0.62856],[-65.11657,1.12046],[-64.38932,1.5125],[-64.34654,1.35569],[-64.08274,1.64792],[-64.06135,1.94722],[-63.39827,2.16098],[-63.39114,2.4317],[-64.0257,2.48156],[-64.02908,2.79797],[-64.48379,3.7879],[-64.84028,4.24665],[-64.72977,4.28931],[-64.57648,4.12576],[-64.14512,4.12932],[-63.99183,3.90172],[-63.86082,3.94796],[-63.70218,3.91417],[-63.67099,4.01731],[-63.50611,3.83592],[-63.42233,3.89995],[-63.4464,3.9693],[-63.21111,3.96219],[-62.98296,3.59935],[-62.7655,3.73099],[-62.74411,4.03331],[-62.57656,4.04754],[-62.44822,4.18621],[-62.13094,4.08309],[-61.54629,4.2822],[-61.48569,4.43149],[-61.29675,4.44216],[-61.31457,4.54167],[-61.15703,4.49839],[-60.98303,4.54167],[-60.86539,4.70512],[-60.5802,4.94312],[-60.73204,5.20931],[-61.4041,5.95304],[-61.15058,6.19558],[-61.20762,6.58174],[-61.13632,6.70922],[-60.54873,6.8631],[-60.39419,6.94847],[-60.28074,7.1162],[-60.44116,7.20817],[-60.54098,7.14804],[-60.63367,7.25061],[-60.59802,7.33194],[-60.71923,7.55817],[-60.64793,7.56877],[-60.51959,7.83373],[-60.38056,7.8302],[-60.02407,8.04557],[-59.97059,8.20791],[-59.83156,8.23261],[-59.80661,8.28906],[-59.85562,8.35213],[-59.98508,8.53046],[-59.54058,8.6862],[-60.89962,9.81445],[-62.08693,10.04435],[-61.62505,11.18974],[-63.73917,11.92623],[-63.19938,16.44103],[-67.89186,12.4116],[-68.01417,11.77722],[-68.33524,11.78151],[-68.99639,11.79035],[-71.22331,13.01387]]]]}},{type:"Feature",properties:{iso1A2:"VG",iso1A3:"VGB",iso1N3:"092",wikidata:"Q25305",nameEn:"British Virgin Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 284"]},geometry:{type:"MultiPolygon",coordinates:[[[[-64.03057,18.08241],[-63.75633,19.39745],[-65.02435,18.73231],[-64.86027,18.39056],[-64.64067,18.36478],[-64.646,18.10286],[-64.03057,18.08241]]]]}},{type:"Feature",properties:{iso1A2:"VI",iso1A3:"VIR",iso1N3:"850",wikidata:"Q11703",nameEn:"United States Virgin Islands",country:"US",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 340"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.02435,18.73231],[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86027,18.39056],[-65.02435,18.73231]]]]}},{type:"Feature",properties:{iso1A2:"VN",iso1A3:"VNM",iso1N3:"704",wikidata:"Q881",nameEn:"Vietnam",groups:["035","142"],callingCodes:["84"]},geometry:{type:"MultiPolygon",coordinates:[[[[108.10003,21.47338],[108.0569,21.53604],[108.02926,21.54997],[107.97932,21.54503],[107.97383,21.53961],[107.97074,21.54072],[107.96774,21.53601],[107.95232,21.5388],[107.92652,21.58906],[107.90006,21.5905],[107.86114,21.65128],[107.80355,21.66141],[107.66967,21.60787],[107.56537,21.61945],[107.54047,21.5934],[107.49065,21.59774],[107.49532,21.62958],[107.47197,21.6672],[107.41593,21.64839],[107.38636,21.59774],[107.35989,21.60063],[107.35834,21.6672],[107.29296,21.74674],[107.24625,21.7077],[107.20734,21.71493],[107.10771,21.79879],[107.02615,21.81981],[107.00964,21.85948],[107.06101,21.88982],[107.05634,21.92303],[106.99252,21.95191],[106.97228,21.92592],[106.92714,21.93459],[106.9178,21.97357],[106.81038,21.97934],[106.74345,22.00965],[106.72551,21.97923],[106.69276,21.96013],[106.68274,21.99811],[106.70142,22.02409],[106.6983,22.15102],[106.67495,22.1885],[106.69986,22.22309],[106.6516,22.33977],[106.55976,22.34841],[106.57221,22.37],[106.55665,22.46498],[106.58395,22.474],[106.61269,22.60301],[106.65316,22.5757],[106.71698,22.58432],[106.72321,22.63606],[106.76293,22.73491],[106.82404,22.7881],[106.83685,22.8098],[106.81271,22.8226],[106.78422,22.81532],[106.71128,22.85982],[106.71387,22.88296],[106.6734,22.89587],[106.6516,22.86862],[106.60179,22.92884],[106.55976,22.92311],[106.51306,22.94891],[106.49749,22.91164],[106.34961,22.86718],[106.27022,22.87722],[106.19705,22.98475],[106.00179,22.99049],[105.99568,22.94178],[105.90119,22.94168],[105.8726,22.92756],[105.72382,23.06641],[105.57594,23.075],[105.56037,23.16806],[105.49966,23.20669],[105.42805,23.30824],[105.40782,23.28107],[105.32376,23.39684],[105.22569,23.27249],[105.17276,23.28679],[105.11672,23.25247],[105.07002,23.26248],[104.98712,23.19176],[104.96532,23.20463],[104.9486,23.17235],[104.91435,23.18666],[104.87992,23.17141],[104.87382,23.12854],[104.79478,23.12934],[104.8334,23.01484],[104.86765,22.95178],[104.84942,22.93631],[104.77114,22.90017],[104.72755,22.81984],[104.65283,22.83419],[104.60457,22.81841],[104.58122,22.85571],[104.47225,22.75813],[104.35593,22.69353],[104.25683,22.76534],[104.27084,22.8457],[104.11384,22.80363],[104.03734,22.72945],[104.01088,22.51823],[103.99247,22.51958],[103.97384,22.50634],[103.96783,22.51173],[103.96352,22.50584],[103.95191,22.5134],[103.94513,22.52553],[103.93286,22.52703],[103.87904,22.56683],[103.64506,22.79979],[103.56255,22.69499],[103.57812,22.65764],[103.52675,22.59155],[103.43646,22.70648],[103.43179,22.75816],[103.32282,22.8127],[103.28079,22.68063],[103.18895,22.64471],[103.15782,22.59873],[103.17961,22.55705],[103.07843,22.50097],[103.0722,22.44775],[102.9321,22.48659],[102.8636,22.60735],[102.60675,22.73376],[102.57095,22.7036],[102.51802,22.77969],[102.46665,22.77108],[102.42618,22.69212],[102.38415,22.67919],[102.41061,22.64184],[102.25339,22.4607],[102.26428,22.41321],[102.16621,22.43336],[102.14099,22.40092],[102.18712,22.30403],[102.51734,22.02676],[102.49092,21.99002],[102.62301,21.91447],[102.67145,21.65894],[102.74189,21.66713],[102.82115,21.73667],[102.81894,21.83888],[102.85637,21.84501],[102.86077,21.71213],[102.97965,21.74076],[102.98846,21.58936],[102.86297,21.4255],[102.94223,21.46034],[102.88939,21.3107],[102.80794,21.25736],[102.89825,21.24707],[102.97745,21.05821],[103.03469,21.05821],[103.12055,20.89994],[103.21497,20.89832],[103.38032,20.79501],[103.45737,20.82382],[103.68633,20.66324],[103.73478,20.6669],[103.82282,20.8732],[103.98024,20.91531],[104.11121,20.96779],[104.27412,20.91433],[104.63957,20.6653],[104.38199,20.47155],[104.40621,20.3849],[104.47886,20.37459],[104.66158,20.47774],[104.72102,20.40554],[104.62195,20.36633],[104.61315,20.24452],[104.86852,20.14121],[104.91695,20.15567],[104.9874,20.09573],[104.8465,19.91783],[104.8355,19.80395],[104.68359,19.72729],[104.64837,19.62365],[104.53169,19.61743],[104.41281,19.70035],[104.23229,19.70242],[104.06498,19.66926],[104.05617,19.61743],[104.10832,19.51575],[104.06058,19.43484],[103.87125,19.31854],[104.5361,18.97747],[104.64617,18.85668],[105.12829,18.70453],[105.19654,18.64196],[105.1327,18.58355],[105.10408,18.43533],[105.15942,18.38691],[105.38366,18.15315],[105.46292,18.22008],[105.64784,17.96687],[105.60381,17.89356],[105.76612,17.67147],[105.85744,17.63221],[106.09019,17.36399],[106.18991,17.28227],[106.24444,17.24714],[106.29287,17.3018],[106.31929,17.20509],[106.43597,17.01362],[106.50862,16.9673],[106.55045,17.0031],[106.54824,16.92729],[106.51963,16.92097],[106.52183,16.87884],[106.55265,16.86831],[106.55485,16.68704],[106.59013,16.62259],[106.58267,16.6012],[106.61477,16.60713],[106.66052,16.56892],[106.65832,16.47816],[106.74418,16.41904],[106.84104,16.55415],[106.88727,16.52671],[106.88067,16.43594],[106.96638,16.34938],[106.97385,16.30204],[107.02597,16.31132],[107.09091,16.3092],[107.15035,16.26271],[107.14595,16.17816],[107.25822,16.13587],[107.33968,16.05549],[107.44975,16.08511],[107.46296,16.01106],[107.39471,15.88829],[107.34188,15.89464],[107.21419,15.83747],[107.21859,15.74638],[107.27143,15.71459],[107.27583,15.62769],[107.34408,15.62345],[107.3815,15.49832],[107.50699,15.48771],[107.53341,15.40496],[107.62367,15.42193],[107.60605,15.37524],[107.62587,15.2266],[107.58844,15.20111],[107.61926,15.13949],[107.61486,15.0566],[107.46516,15.00982],[107.48277,14.93751],[107.59285,14.87795],[107.51579,14.79282],[107.54361,14.69092],[107.55371,14.628],[107.52102,14.59034],[107.52569,14.54665],[107.48521,14.40346],[107.44941,14.41552],[107.39493,14.32655],[107.40427,14.24509],[107.33577,14.11832],[107.37158,14.07906],[107.35757,14.02319],[107.38247,13.99147],[107.44318,13.99751],[107.46498,13.91593],[107.45252,13.78897],[107.53503,13.73908],[107.61909,13.52577],[107.62843,13.3668],[107.49144,13.01215],[107.49611,12.88926],[107.55993,12.7982],[107.5755,12.52177],[107.55059,12.36824],[107.4463,12.29373],[107.42917,12.24657],[107.34511,12.33327],[107.15831,12.27547],[106.99953,12.08983],[106.92325,12.06548],[106.79405,12.0807],[106.70687,11.96956],[106.4111,11.97413],[106.4687,11.86751],[106.44068,11.86294],[106.44535,11.8279],[106.41577,11.76999],[106.45158,11.68616],[106.44691,11.66787],[106.37219,11.69836],[106.30525,11.67549],[106.26478,11.72122],[106.18539,11.75171],[106.13158,11.73283],[106.06708,11.77761],[106.02038,11.77457],[106.00792,11.7197],[105.95188,11.63738],[105.88962,11.67854],[105.8507,11.66635],[105.80867,11.60536],[105.81645,11.56876],[105.87328,11.55953],[105.88962,11.43605],[105.86782,11.28343],[106.10444,11.07879],[106.1527,11.10476],[106.1757,11.07301],[106.20095,10.97795],[106.14301,10.98176],[106.18539,10.79451],[106.06708,10.8098],[105.94535,10.9168],[105.93403,10.83853],[105.84603,10.85873],[105.86376,10.89839],[105.77751,11.03671],[105.50045,10.94586],[105.42884,10.96878],[105.34011,10.86179],[105.11449,10.96332],[105.08326,10.95656],[105.02722,10.89236],[105.09571,10.72722],[104.95094,10.64003],[104.87933,10.52833],[104.59018,10.53073],[104.49869,10.4057],[104.47963,10.43046],[104.43778,10.42386],[103.99198,10.48391],[102.47649,9.66162],[104.81582,8.03101],[109.55486,8.10026],[111.60491,13.57105],[108.00365,17.98159],[108.10003,21.47338]]]]}},{type:"Feature",properties:{iso1A2:"VU",iso1A3:"VUT",iso1N3:"548",wikidata:"Q686",nameEn:"Vanuatu",groups:["054","009"],callingCodes:["678"]},geometry:{type:"MultiPolygon",coordinates:[[[[162.93363,-17.28904],[173.26254,-22.69968],[168.21179,-12.88558],[166.02864,-12.9396],[162.93363,-17.28904]]]]}},{type:"Feature",properties:{iso1A2:"WF",iso1A3:"WLF",iso1N3:"876",wikidata:"Q35555",nameEn:"Wallis and Futuna",country:"FR",groups:["061","009"],callingCodes:["681"]},geometry:{type:"MultiPolygon",coordinates:[[[[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"WS",iso1A3:"WSM",iso1N3:"882",wikidata:"Q683",nameEn:"Samoa",groups:["061","009"],driveSide:"left",callingCodes:["685"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.17905,-14.94502],[-173.13438,-14.94228],[-171.14262,-14.93704],[-171.14953,-12.4725],[-174.18596,-12.48057],[-174.17905,-14.94502]]]]}},{type:"Feature",properties:{iso1A2:"XK",iso1A3:"XKX",wikidata:"Q1246",nameEn:"Kosovo",aliases:["KV"],groups:["039","150"],isoStatus:"usrAssn",callingCodes:["383"]},geometry:{type:"MultiPolygon",coordinates:[[[[21.39045,42.74888],[21.44047,42.87276],[21.36941,42.87397],[21.32974,42.90424],[21.2719,42.8994],[21.23534,42.95523],[21.23877,43.00848],[21.2041,43.02277],[21.16734,42.99694],[21.14465,43.11089],[21.08952,43.13471],[21.05378,43.10707],[21.00749,43.13984],[20.96287,43.12416],[20.83727,43.17842],[20.88685,43.21697],[20.82145,43.26769],[20.73811,43.25068],[20.68688,43.21335],[20.59929,43.20492],[20.69515,43.09641],[20.64557,43.00826],[20.59929,43.01067],[20.48692,42.93208],[20.53484,42.8885],[20.43734,42.83157],[20.40594,42.84853],[20.35692,42.8335],[20.27869,42.81945],[20.2539,42.76245],[20.04898,42.77701],[20.02088,42.74789],[20.02915,42.71147],[20.0969,42.65559],[20.07761,42.55582],[20.17127,42.50469],[20.21797,42.41237],[20.24399,42.32168],[20.34479,42.32656],[20.3819,42.3029],[20.48857,42.25444],[20.56955,42.12097],[20.55633,42.08173],[20.59434,42.03879],[20.63069,41.94913],[20.57946,41.91593],[20.59524,41.8818],[20.68523,41.85318],[20.76786,41.91839],[20.75464,42.05229],[21.11491,42.20794],[21.16614,42.19815],[21.22728,42.08909],[21.31983,42.10993],[21.29913,42.13954],[21.30496,42.1418],[21.38428,42.24465],[21.43882,42.23609],[21.43882,42.2789],[21.50823,42.27156],[21.52145,42.24465],[21.58992,42.25915],[21.56772,42.30946],[21.5264,42.33634],[21.53467,42.36809],[21.57021,42.3647],[21.59029,42.38042],[21.62887,42.37664],[21.64209,42.41081],[21.62556,42.45106],[21.7035,42.51899],[21.70522,42.54176],[21.7327,42.55041],[21.75672,42.62695],[21.79413,42.65923],[21.75025,42.70125],[21.6626,42.67813],[21.58755,42.70418],[21.59154,42.72643],[21.47498,42.74695],[21.39045,42.74888]]]]}},{type:"Feature",properties:{iso1A2:"YE",iso1A3:"YEM",iso1N3:"887",wikidata:"Q805",nameEn:"Yemen",groups:["145","142"],callingCodes:["967"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.32998,16.16312],[53.09917,16.67084],[52.81185,17.28568],[52.74267,17.29519],[52.78009,17.35124],[52.00311,19.00083],[49.04884,18.59899],[48.19996,18.20584],[47.58351,17.50366],[47.48245,17.10808],[47.00571,16.94765],[46.76494,17.29151],[46.31018,17.20464],[44.50126,17.47475],[43.70631,17.35762],[43.43005,17.56148],[43.29185,17.53224],[43.22533,17.38343],[43.32653,17.31179],[43.20156,17.25901],[43.17787,17.14717],[43.23967,17.03428],[43.18233,17.02673],[43.1813,16.98438],[43.19328,16.94703],[43.1398,16.90696],[43.18338,16.84852],[43.22012,16.83932],[43.22956,16.80613],[43.24801,16.80613],[43.26303,16.79479],[43.25857,16.75304],[43.21325,16.74416],[43.22066,16.65179],[43.15274,16.67248],[43.11601,16.53166],[42.97215,16.51093],[42.94351,16.49467],[42.94625,16.39721],[42.76801,16.40371],[42.15205,16.40211],[41.37609,16.19728],[41.29956,15.565],[42.63806,13.58268],[43.29075,12.79154],[43.32909,12.59711],[43.90659,12.3823],[50.51849,13.0483],[51.12877,12.56479],[52.253,11.68582],[55.69862,12.12478],[53.32998,16.16312]]]]}},{type:"Feature",properties:{iso1A2:"YT",iso1A3:"MYT",iso1N3:"175",wikidata:"Q17063",nameEn:"Mayotte",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.83794,-13.66915],[45.54824,-13.22353],[45.50237,-11.90315],[43.83794,-13.66915]]]]}},{type:"Feature",properties:{iso1A2:"ZA",iso1A3:"ZAF",iso1N3:"710",wikidata:"Q258",nameEn:"South Africa",groups:["018","202","002"],driveSide:"left",callingCodes:["27"]},geometry:{type:"MultiPolygon",coordinates:[[[[31.30611,-22.422],[31.16344,-22.32599],[31.08932,-22.34884],[30.86696,-22.28907],[30.6294,-22.32599],[30.48686,-22.31368],[30.38614,-22.34533],[30.28351,-22.35587],[30.2265,-22.2961],[30.13147,-22.30841],[29.92242,-22.19408],[29.76848,-22.14128],[29.64609,-22.12917],[29.37703,-22.19581],[29.21955,-22.17771],[29.18974,-22.18599],[29.15268,-22.21399],[29.10881,-22.21202],[29.0151,-22.22907],[28.91889,-22.44299],[28.63287,-22.55887],[28.34874,-22.5694],[28.04562,-22.8394],[28.04752,-22.90243],[27.93729,-22.96194],[27.93539,-23.04941],[27.74154,-23.2137],[27.6066,-23.21894],[27.52393,-23.37952],[27.33768,-23.40917],[26.99749,-23.65486],[26.84165,-24.24885],[26.51667,-24.47219],[26.46346,-24.60358],[26.39409,-24.63468],[25.8515,-24.75727],[25.84295,-24.78661],[25.88571,-24.87802],[25.72702,-25.25503],[25.69661,-25.29284],[25.6643,-25.4491],[25.58543,-25.6343],[25.33076,-25.76616],[25.12266,-25.75931],[25.01718,-25.72507],[24.8946,-25.80723],[24.67319,-25.81749],[24.44703,-25.73021],[24.36531,-25.773],[24.18287,-25.62916],[23.9244,-25.64286],[23.47588,-25.29971],[23.03497,-25.29971],[22.86012,-25.50572],[22.70808,-25.99186],[22.56365,-26.19668],[22.41921,-26.23078],[22.21206,-26.3773],[22.06192,-26.61882],[21.90703,-26.66808],[21.83291,-26.65959],[21.77114,-26.69015],[21.7854,-26.79199],[21.69322,-26.86152],[21.37869,-26.82083],[21.13353,-26.86661],[20.87031,-26.80047],[20.68596,-26.9039],[20.63275,-26.78181],[20.61754,-26.4692],[20.86081,-26.14892],[20.64795,-25.47827],[20.29826,-24.94869],[20.03678,-24.81004],[20.02809,-24.78725],[19.99817,-24.76768],[19.99882,-28.42622],[18.99885,-28.89165],[17.4579,-28.68718],[17.15405,-28.08573],[16.90446,-28.057],[16.59922,-28.53246],[16.46592,-28.57126],[16.45332,-28.63117],[12.51595,-32.27486],[38.88176,-48.03306],[34.51034,-26.91792],[32.35222,-26.86027],[32.29584,-26.852],[32.22302,-26.84136],[32.19409,-26.84032],[32.13315,-26.84345],[32.09664,-26.80721],[32.00893,-26.8096],[31.97463,-27.11057],[31.97592,-27.31675],[31.49834,-27.31549],[31.15027,-27.20151],[30.96088,-27.0245],[30.97757,-26.92706],[30.88826,-26.79622],[30.81101,-26.84722],[30.78927,-26.48271],[30.95819,-26.26303],[31.13073,-25.91558],[31.31237,-25.7431],[31.4175,-25.71886],[31.86881,-25.99973],[31.974,-25.95387],[31.92649,-25.84216],[32.00631,-25.65044],[31.97875,-25.46356],[32.01676,-25.38117],[32.03196,-25.10785],[31.9835,-24.29983],[31.90368,-24.18892],[31.87707,-23.95293],[31.77445,-23.90082],[31.70223,-23.72695],[31.67942,-23.60858],[31.56539,-23.47268],[31.55779,-23.176],[31.30611,-22.422]],[[29.33204,-29.45598],[29.28545,-29.58456],[29.12553,-29.76266],[29.16548,-29.91706],[28.9338,-30.05072],[28.80222,-30.10579],[28.68627,-30.12885],[28.399,-30.1592],[28.2319,-30.28476],[28.12073,-30.68072],[27.74814,-30.60635],[27.69467,-30.55862],[27.67819,-30.53437],[27.6521,-30.51707],[27.62137,-30.50509],[27.56781,-30.44562],[27.56901,-30.42504],[27.45452,-30.32239],[27.38108,-30.33456],[27.36649,-30.27246],[27.37293,-30.19401],[27.40778,-30.14577],[27.32555,-30.14785],[27.29603,-30.05473],[27.22719,-30.00718],[27.09489,-29.72796],[27.01016,-29.65439],[27.33464,-29.48161],[27.4358,-29.33465],[27.47254,-29.31968],[27.45125,-29.29708],[27.48679,-29.29349],[27.54258,-29.25575],[27.5158,-29.2261],[27.55974,-29.18954],[27.75458,-28.89839],[27.8907,-28.91612],[27.88933,-28.88156],[27.9392,-28.84864],[27.98675,-28.8787],[28.02503,-28.85991],[28.1317,-28.7293],[28.2348,-28.69471],[28.30518,-28.69531],[28.40612,-28.6215],[28.65091,-28.57025],[28.68043,-28.58744],[29.40524,-29.21246],[29.44883,-29.3772],[29.33204,-29.45598]]]]}},{type:"Feature",properties:{iso1A2:"ZM",iso1A3:"ZMB",iso1N3:"894",wikidata:"Q953",nameEn:"Zambia",groups:["014","202","002"],driveSide:"left",callingCodes:["260"]},geometry:{type:"MultiPolygon",coordinates:[[[[32.95389,-9.40138],[32.76233,-9.31963],[32.75611,-9.28583],[32.53661,-9.24281],[32.49147,-9.14754],[32.43543,-9.11988],[32.25486,-9.13371],[32.16146,-9.05993],[32.08206,-9.04609],[31.98866,-9.07069],[31.94196,-9.02303],[31.94663,-8.93846],[31.81587,-8.88618],[31.71158,-8.91386],[31.57147,-8.81388],[31.57147,-8.70619],[31.37533,-8.60769],[31.00796,-8.58615],[30.79243,-8.27382],[28.88917,-8.4831],[28.9711,-8.66935],[28.38526,-9.23393],[28.36562,-9.30091],[28.52636,-9.35379],[28.51627,-9.44726],[28.56208,-9.49122],[28.68532,-9.78],[28.62795,-9.92942],[28.65032,-10.65133],[28.37241,-11.57848],[28.48357,-11.87532],[29.18592,-12.37921],[29.4992,-12.43843],[29.48404,-12.23604],[29.8139,-12.14898],[29.81551,-13.44683],[29.65078,-13.41844],[29.60531,-13.21685],[29.01918,-13.41353],[28.33199,-12.41375],[27.59932,-12.22123],[27.21025,-11.76157],[27.22541,-11.60323],[27.04351,-11.61312],[26.88687,-12.01868],[26.01777,-11.91488],[25.33058,-11.65767],[25.34069,-11.19707],[24.42612,-11.44975],[24.34528,-11.06816],[24.00027,-10.89356],[24.02603,-11.15368],[23.98804,-12.13149],[24.06672,-12.29058],[23.90937,-12.844],[24.03339,-12.99091],[21.97988,-13.00148],[22.00323,-16.18028],[22.17217,-16.50269],[23.20038,-17.47563],[23.47474,-17.62877],[24.23619,-17.47489],[24.32811,-17.49082],[24.38712,-17.46818],[24.5621,-17.52963],[24.70864,-17.49501],[25.00198,-17.58221],[25.26433,-17.79571],[25.51646,-17.86232],[25.6827,-17.81987],[25.85738,-17.91403],[25.85892,-17.97726],[26.08925,-17.98168],[26.0908,-17.93021],[26.21601,-17.88608],[26.55918,-17.99638],[26.68403,-18.07411],[26.74314,-18.0199],[26.89926,-17.98756],[27.14196,-17.81398],[27.30736,-17.60487],[27.61377,-17.34378],[27.62795,-17.24365],[27.83141,-16.96274],[28.73725,-16.5528],[28.76199,-16.51575],[28.81454,-16.48611],[28.8501,-16.04537],[28.9243,-15.93987],[29.01298,-15.93805],[29.21955,-15.76589],[29.4437,-15.68702],[29.8317,-15.6126],[30.35574,-15.6513],[30.41902,-15.62269],[30.22098,-14.99447],[33.24249,-14.00019],[33.16749,-13.93992],[33.07568,-13.98447],[33.02977,-14.05022],[32.99042,-13.95689],[32.88985,-13.82956],[32.79015,-13.80755],[32.76962,-13.77224],[32.84528,-13.71576],[32.7828,-13.64805],[32.68654,-13.64268],[32.66468,-13.60019],[32.68436,-13.55769],[32.73683,-13.57682],[32.84176,-13.52794],[32.86113,-13.47292],[33.0078,-13.19492],[32.98289,-13.12671],[33.02181,-12.88707],[32.96733,-12.88251],[32.94397,-12.76868],[33.05917,-12.59554],[33.18837,-12.61377],[33.28177,-12.54692],[33.37517,-12.54085],[33.54485,-12.35996],[33.47636,-12.32498],[33.3705,-12.34931],[33.25998,-12.14242],[33.33937,-11.91252],[33.32692,-11.59248],[33.24252,-11.59302],[33.23663,-11.40637],[33.29267,-11.43536],[33.29267,-11.3789],[33.39697,-11.15296],[33.25998,-10.88862],[33.28022,-10.84428],[33.47636,-10.78465],[33.70675,-10.56896],[33.54797,-10.36077],[33.53863,-10.20148],[33.31297,-10.05133],[33.37902,-9.9104],[33.36581,-9.81063],[33.31517,-9.82364],[33.2095,-9.61099],[33.12144,-9.58929],[33.10163,-9.66525],[33.05485,-9.61316],[33.00256,-9.63053],[33.00476,-9.5133],[32.95389,-9.40138]]]]}},{type:"Feature",properties:{iso1A2:"ZW",iso1A3:"ZWE",iso1N3:"716",wikidata:"Q954",nameEn:"Zimbabwe",groups:["014","202","002"],driveSide:"left",callingCodes:["263"]},geometry:{type:"MultiPolygon",coordinates:[[[[30.41902,-15.62269],[30.35574,-15.6513],[29.8317,-15.6126],[29.4437,-15.68702],[29.21955,-15.76589],[29.01298,-15.93805],[28.9243,-15.93987],[28.8501,-16.04537],[28.81454,-16.48611],[28.76199,-16.51575],[28.73725,-16.5528],[27.83141,-16.96274],[27.62795,-17.24365],[27.61377,-17.34378],[27.30736,-17.60487],[27.14196,-17.81398],[26.89926,-17.98756],[26.74314,-18.0199],[26.68403,-18.07411],[26.55918,-17.99638],[26.21601,-17.88608],[26.0908,-17.93021],[26.08925,-17.98168],[25.85892,-17.97726],[25.85738,-17.91403],[25.6827,-17.81987],[25.51646,-17.86232],[25.26433,-17.79571],[25.23909,-17.90832],[25.31799,-18.07091],[25.39972,-18.12691],[25.53465,-18.39041],[25.68859,-18.56165],[25.79217,-18.6355],[25.82353,-18.82808],[25.94326,-18.90362],[25.99837,-19.02943],[25.96226,-19.08152],[26.17227,-19.53709],[26.72246,-19.92707],[27.21278,-20.08244],[27.29831,-20.28935],[27.28865,-20.49873],[27.69361,-20.48531],[27.72972,-20.51735],[27.69171,-21.08409],[27.91407,-21.31621],[28.01669,-21.57624],[28.29416,-21.59037],[28.49942,-21.66634],[28.58114,-21.63455],[29.07763,-21.81877],[29.04023,-21.85864],[29.02191,-21.90647],[29.02191,-21.95665],[29.04108,-22.00563],[29.08495,-22.04867],[29.14501,-22.07275],[29.1974,-22.07472],[29.24648,-22.05967],[29.3533,-22.18363],[29.37703,-22.19581],[29.64609,-22.12917],[29.76848,-22.14128],[29.92242,-22.19408],[30.13147,-22.30841],[30.2265,-22.2961],[30.28351,-22.35587],[30.38614,-22.34533],[30.48686,-22.31368],[30.6294,-22.32599],[30.86696,-22.28907],[31.08932,-22.34884],[31.16344,-22.32599],[31.30611,-22.422],[31.38336,-22.36919],[32.41234,-21.31246],[32.48236,-21.32873],[32.37115,-21.133],[32.51644,-20.91929],[32.48122,-20.63319],[32.55167,-20.56312],[32.66174,-20.56106],[32.85987,-20.27841],[32.85987,-20.16686],[32.93032,-20.03868],[33.01178,-20.02007],[33.06461,-19.77787],[32.95013,-19.67219],[32.84666,-19.68462],[32.84446,-19.48343],[32.78282,-19.47513],[32.77966,-19.36098],[32.85107,-19.29238],[32.87088,-19.09279],[32.84006,-19.0262],[32.72118,-19.02204],[32.69917,-18.94293],[32.73439,-18.92628],[32.70137,-18.84712],[32.82465,-18.77419],[32.9017,-18.7992],[32.95013,-18.69079],[32.88629,-18.58023],[32.88629,-18.51344],[33.02278,-18.4696],[33.03159,-18.35054],[32.94133,-17.99705],[33.0492,-17.60298],[32.98536,-17.55891],[32.96554,-17.48964],[33.0426,-17.3468],[33.00517,-17.30477],[32.96554,-17.11971],[32.84113,-16.92259],[32.91051,-16.89446],[32.97655,-16.70689],[32.78943,-16.70267],[32.69917,-16.66893],[32.71017,-16.59932],[32.42838,-16.4727],[32.28529,-16.43892],[32.02772,-16.43892],[31.91324,-16.41569],[31.90223,-16.34388],[31.67988,-16.19595],[31.42451,-16.15154],[31.30563,-16.01193],[31.13171,-15.98019],[30.97761,-16.05848],[30.91597,-15.99924],[30.42568,-15.9962],[30.41902,-15.62269]]]]}}]; - var rawBorders = { - type: type, - features: features - }; - - let borders = rawBorders; - let whichPolygonGetter = {}; - let featuresByCode = {}; - let idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g; - let levels = [ - 'subterritory', - 'territory', - 'country', - 'intermediateRegion', - 'subregion', - 'region', - 'union', - 'world' - ]; - loadDerivedDataAndCaches(borders); - function loadDerivedDataAndCaches(borders) { - let identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn']; - let geometryFeatures = []; - for (let i in borders.features) { - let feature = borders.features[i]; - feature.properties.id = feature.properties.iso1A2 || feature.properties.m49; - loadM49(feature); - loadIsoStatus(feature); - loadLevel(feature); - loadGroups(feature); - loadRoadSpeedUnit(feature); - loadDriveSide(feature); - loadFlag(feature); - cacheFeatureByIDs(feature); - if (feature.geometry) geometryFeatures.push(feature); - } - for (let i in borders.features) { - let feature = borders.features[i]; - feature.properties.groups.sort(function(groupID1, groupID2) { - return ( - levels.indexOf(featuresByCode[groupID1].properties.level) - - levels.indexOf(featuresByCode[groupID2].properties.level) - ); - }); - loadMembersForGroupsOf(feature); - } - let geometryOnlyCollection = { - type: 'RegionFeatureCollection', - features: geometryFeatures - }; - whichPolygonGetter = whichPolygon_1(geometryOnlyCollection); - function loadGroups(feature) { - let props = feature.properties; - if (!props.groups) { - props.groups = []; - } - if (props.country) { - props.groups.push(props.country); - } - if (props.m49 !== '001') { - props.groups.push('001'); - } - } - function loadM49(feature) { - let props = feature.properties; - if (!props.m49 && props.iso1N3) { - props.m49 = props.iso1N3; - } - } - function loadIsoStatus(feature) { - let props = feature.properties; - if (!props.isoStatus && props.iso1A2) { - props.isoStatus = 'official'; - } - } - function loadLevel(feature) { - let props = feature.properties; - if (props.level) return; - if (!props.country) { - props.level = 'country'; - } else if (props.isoStatus === 'official') { - props.level = 'territory'; - } else { - props.level = 'subterritory'; - } - } - function loadRoadSpeedUnit(feature) { - let props = feature.properties; - if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') { - props.roadSpeedUnit = 'km/h'; - } - } - function loadDriveSide(feature) { - let props = feature.properties; - if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') { - props.driveSide = 'right'; - } - } - function loadFlag(feature) { - if (!feature.properties.iso1A2) return; - let flag = feature.properties.iso1A2.replace(/./g, function(char) { - return String.fromCodePoint(char.charCodeAt(0) + 127397); - }); - feature.properties.emojiFlag = flag; - } - function loadMembersForGroupsOf(feature) { - let featureID = feature.properties.id; - let standardizedGroupIDs = []; - for (let j in feature.properties.groups) { - let groupID = feature.properties.groups[j]; - let groupFeature = featuresByCode[groupID]; - standardizedGroupIDs.push(groupFeature.properties.id); - if (groupFeature.properties.members) { - groupFeature.properties.members.push(featureID); - } else { - groupFeature.properties.members = [featureID]; - } - } - feature.properties.groups = standardizedGroupIDs; - } - function cacheFeatureByIDs(feature) { - for (let k in identifierProps) { - let prop = identifierProps[k]; - let id = prop && feature.properties[prop]; - if (id) { - id = id.replace(idFilterRegex, '').toUpperCase(); - featuresByCode[id] = feature; - } - } - if (feature.properties.aliases) { - for (let j in feature.properties.aliases) { - let alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase(); - featuresByCode[alias] = feature; - } - } - } - } - function locArray(loc) { - if (Array.isArray(loc)) { - return loc; - } else if (loc.coordinates) { - return loc.coordinates; - } - return loc.geometry.coordinates; - } - function smallestFeature(loc) { - let query = locArray(loc); - let featureProperties = whichPolygonGetter(query); - if (!featureProperties) return null; - return featuresByCode[featureProperties.id]; - } - function countryFeature(loc) { - let feature = smallestFeature(loc); - if (!feature) return null; - let countryCode = feature.properties.country || feature.properties.iso1A2; - return featuresByCode[countryCode]; - } - function featureForLoc(loc, opts) { - if (opts && opts.level && opts.level !== 'country') { - let features = featuresContaining(loc); - let targetLevel = opts.level; - let targetLevelIndex = levels.indexOf(targetLevel); - if (targetLevelIndex === -1) return null; - for (let i in features) { - let feature = features[i]; - if ( - feature.properties.level === targetLevel || - levels.indexOf(feature.properties.level) > targetLevelIndex - ) { - return feature; - } - } - return null; - } - return countryFeature(loc); - } - function featureForID(id) { - let stringID; - if (typeof id === 'number') { - stringID = id.toString(); - if (stringID.length === 1) { - stringID = '00' + stringID; - } else if (stringID.length === 2) { - stringID = '0' + stringID; - } - } else { - stringID = id.replace(idFilterRegex, '').toUpperCase(); - } - return featuresByCode[stringID] || null; - } - function smallestOrMatchingFeature(query) { - if (typeof query === 'object') { - return smallestFeature(query); - } - return featureForID(query); - } - function feature(query, opts) { - if (typeof query === 'object') { - return featureForLoc(query, opts); - } - return featureForID(query); - } - function iso1A2Code(query, opts) { - let match = feature(query, opts); - if (!match) return null; - return match.properties.iso1A2 || null; - } - function featuresContaining(query, strict) { - let feature = smallestOrMatchingFeature(query); - if (!feature) return []; - let features = []; - if (!strict || typeof query === 'object') { - features.push(feature); - } - let properties = feature.properties; - for (let i in properties.groups) { - let groupID = properties.groups[i]; - features.push(featuresByCode[groupID]); - } - return features; - } - function roadSpeedUnit(query) { - let feature = smallestOrMatchingFeature(query); - return (feature && feature.properties.roadSpeedUnit) || null; - } - - let _dataDeprecated; - let _nsi; - - function validationOutdatedTags() { - const type = 'outdated_tags'; - const nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office']; - - // A concern here in switching to async data means that `_dataDeprecated` - // and `_nsi` will not be available at first, so the data on early tiles - // may not have tags validated fully. - - // initialize deprecated tags array - _mainFileFetcher.get('deprecated') - .then(d => _dataDeprecated = d) - .catch(() => { /* ignore */ }); - - _mainFileFetcher.get('nsi_brands') - .then(d => { - _nsi = { - brands: d.brands, - matcher: matcher$1(), - wikidata: {}, - wikipedia: {} - }; - - // initialize name-suggestion-index matcher - _nsi.matcher.buildMatchIndex(d.brands); - - // index all known wikipedia and wikidata tags - Object.keys(d.brands).forEach(kvnd => { - const brand = d.brands[kvnd]; - const wd = brand.tags['brand:wikidata']; - const wp = brand.tags['brand:wikipedia']; - if (wd) { _nsi.wikidata[wd] = kvnd; } - if (wp) { _nsi.wikipedia[wp] = kvnd; } - }); - - return _nsi; - }) - .catch(() => { /* ignore */ }); - - - function oldTagIssues(entity, graph) { - const oldTags = Object.assign({}, entity.tags); // shallow copy - let preset = _mainPresetIndex.match(entity, graph); - let subtype = 'deprecated_tags'; - if (!preset) return []; - - // upgrade preset.. - if (preset.replacement) { - const newPreset = _mainPresetIndex.item(preset.replacement); - graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph); - entity = graph.entity(entity.id); - preset = newPreset; - } - - // upgrade tags.. - if (_dataDeprecated) { - const deprecatedTags = entity.deprecatedTags(_dataDeprecated); - if (deprecatedTags.length) { - deprecatedTags.forEach(tag => { - graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph); - }); - entity = graph.entity(entity.id); - } - } - - // add missing addTags.. - let newTags = Object.assign({}, entity.tags); // shallow copy - if (preset.tags !== preset.addTags) { - Object.keys(preset.addTags).forEach(k => { - if (!newTags[k]) { - if (preset.addTags[k] === '*') { - newTags[k] = 'yes'; - } else { - newTags[k] = preset.addTags[k]; - } - } - }); - } - - if (_nsi) { - // Do `wikidata` or `wikipedia` identify this entity as a brand? #6416 - // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia` - let isBrand; - if (newTags.wikidata) { // try matching `wikidata` - isBrand = _nsi.wikidata[newTags.wikidata]; - } - if (!isBrand && newTags.wikipedia) { // fallback to `wikipedia` - isBrand = _nsi.wikipedia[newTags.wikipedia]; - } - if (isBrand && !newTags.office) { // but avoid doing this for corporate offices - if (newTags.wikidata) { - newTags['brand:wikidata'] = newTags.wikidata; - delete newTags.wikidata; - } - if (newTags.wikipedia) { - newTags['brand:wikipedia'] = newTags.wikipedia; - delete newTags.wikipedia; - } - // I considered setting `name` and other tags here, but they aren't unique per wikidata - // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс") - // So users will really need to use a preset or assign `name` themselves. - } - - // try key/value|name match against name-suggestion-index - if (newTags.name) { - for (let i = 0; i < nsiKeys.length; i++) { - const k = nsiKeys[i]; - if (!newTags[k]) continue; - - const center = entity.extent(graph).center(); - const countryCode = iso1A2Code(center); - const match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase()); - if (!match) continue; - - // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia)) - if (match.d) continue; - - const brand = _nsi.brands[match.kvnd]; - if (brand && brand.tags['brand:wikidata'] && - brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) { - subtype = 'noncanonical_brand'; - - const keepTags = ['takeaway'].reduce((acc, k) => { - if (newTags[k]) { - acc[k] = newTags[k]; - } - return acc; - }, {}); - - nsiKeys.forEach(k => delete newTags[k]); - Object.assign(newTags, brand.tags, keepTags); - break; - } - } - } - } - - // determine diff - const tagDiff = utilTagDiff(oldTags, newTags); - if (!tagDiff.length) return []; - - const isOnlyAddingTags = tagDiff.every(d => d.type === '+'); - - let prefix = ''; - if (subtype === 'noncanonical_brand') { - prefix = 'noncanonical_brand.'; - } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) { - subtype = 'incomplete_tags'; - prefix = 'incomplete.'; - } - - // don't allow autofixing brand tags - let autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null; - - return [new validationIssue({ - type: type, - subtype: subtype, - severity: 'warning', - message: showMessage, - reference: showReference, - entityIds: [entity.id], - hash: JSON.stringify(tagDiff), - dynamicFixes: () => { - return [ - new validationIssueFix({ - autoArgs: autoArgs, - title: _t('issues.fix.upgrade_tags.title'), - onClick: (context) => { - context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation')); - } - }) - ]; - } - })]; - - - function doUpgrade(graph) { - const currEntity = graph.hasEntity(entity.id); - if (!currEntity) return graph; - - let newTags = Object.assign({}, currEntity.tags); // shallow copy - tagDiff.forEach(diff => { - if (diff.type === '-') { - delete newTags[diff.key]; - } else if (diff.type === '+') { - newTags[diff.key] = diff.newVal; - } - }); - - return actionChangeTags(currEntity.id, newTags)(graph); - } - - - function showMessage(context) { - const currEntity = context.hasEntity(entity.id); - if (!currEntity) return ''; - - let messageID = `issues.outdated_tags.${prefix}message`; - if (subtype === 'noncanonical_brand' && isOnlyAddingTags) { - messageID += '_incomplete'; - } - return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) }); - } - - - function showReference(selection) { - let enter = selection.selectAll('.issue-reference') - .data([0]) - .enter(); - - enter - .append('div') - .attr('class', 'issue-reference') - .text(_t(`issues.outdated_tags.${prefix}reference`)); - - enter - .append('strong') - .text(_t('issues.suggested')); - - enter - .append('table') - .attr('class', 'tagDiff-table') - .selectAll('.tagDiff-row') - .data(tagDiff) - .enter() - .append('tr') - .attr('class', 'tagDiff-row') - .append('td') - .attr('class', d => { - let klass = d.type === '+' ? 'add' : 'remove'; - return `tagDiff-cell tagDiff-cell-${klass}`; - }) - .text(d => d.display); - } - } - - - function oldMultipolygonIssues(entity, graph) { - let multipolygon, outerWay; - if (entity.type === 'relation') { - outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph); - multipolygon = entity; - } else if (entity.type === 'way') { - multipolygon = osmIsOldMultipolygonOuterMember(entity, graph); - outerWay = entity; - } else { - return []; - } - - if (!multipolygon || !outerWay) return []; - - return [new validationIssue({ - type: type, - subtype: 'old_multipolygon', - severity: 'warning', - message: showMessage, - reference: showReference, - entityIds: [outerWay.id, multipolygon.id], - dynamicFixes: () => { - return [ - new validationIssueFix({ - autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')], - title: _t('issues.fix.move_tags.title'), - onClick: (context) => { - context.perform(doUpgrade, _t('issues.fix.move_tags.annotation')); - } - }) - ]; - } - })]; - - - function doUpgrade(graph) { - let currMultipolygon = graph.hasEntity(multipolygon.id); - let currOuterWay = graph.hasEntity(outerWay.id); - if (!currMultipolygon || !currOuterWay) return graph; - - currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags); - graph = graph.replace(currMultipolygon); - return actionChangeTags(currOuterWay.id, {})(graph); - } - - - function showMessage(context) { - let currMultipolygon = context.hasEntity(multipolygon.id); - if (!currMultipolygon) return ''; - - return _t('issues.old_multipolygon.message', - { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) } - ); - } - - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.old_multipolygon.reference')); - } - } - - - let validation = function checkOutdatedTags(entity, graph) { - let issues = oldMultipolygonIssues(entity, graph); - if (!issues.length) issues = oldTagIssues(entity, graph); - return issues; - }; - - - validation.type = type; - - return validation; - } - - function validationPrivateData() { - var type = 'private_data'; - - // assume that some buildings are private - var privateBuildingValues = { - detached: true, - farm: true, - house: true, - houseboat: true, - residential: true, - semidetached_house: true, - static_caravan: true - }; - - // but they might be public if they have one of these other tags - var publicKeys = { - amenity: true, - craft: true, - historic: true, - leisure: true, - office: true, - shop: true, - tourism: true - }; - - // these tags may contain personally identifying info - var personalTags = { - 'contact:email': true, - 'contact:fax': true, - 'contact:phone': true, - email: true, - fax: true, - phone: true - }; - - - var validation = function checkPrivateData(entity) { - var tags = entity.tags; - if (!tags.building || !privateBuildingValues[tags.building]) return []; - - var keepTags = {}; - for (var k in tags) { - if (publicKeys[k]) return []; // probably a public feature - if (!personalTags[k]) { - keepTags[k] = tags[k]; - } - } - - var tagDiff = utilTagDiff(tags, keepTags); - if (!tagDiff.length) return []; - - var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags'; - - return [new validationIssue({ - type: type, - severity: 'warning', - message: showMessage, - reference: showReference, - entityIds: [entity.id], - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.' + fixID + '.title'), - onClick: function(context) { - context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation')); - } - }) - ]; - } - })]; - - - function doUpgrade(graph) { - var currEntity = graph.hasEntity(entity.id); - if (!currEntity) return graph; - - var newTags = Object.assign({}, currEntity.tags); // shallow copy - tagDiff.forEach(function(diff) { - if (diff.type === '-') { - delete newTags[diff.key]; - } else if (diff.type === '+') { - newTags[diff.key] = diff.newVal; - } - }); - - return actionChangeTags(currEntity.id, newTags)(graph); - } - - - function showMessage(context) { - var currEntity = context.hasEntity(this.entityIds[0]); - if (!currEntity) return ''; - - return _t('issues.private_data.contact.message', - { feature: utilDisplayLabel(currEntity, context.graph()) } - ); - } - - - function showReference(selection) { - var enter = selection.selectAll('.issue-reference') - .data([0]) - .enter(); - - enter - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.private_data.reference')); - - enter - .append('strong') - .text(_t('issues.suggested')); - - enter - .append('table') - .attr('class', 'tagDiff-table') - .selectAll('.tagDiff-row') - .data(tagDiff) - .enter() - .append('tr') - .attr('class', 'tagDiff-row') - .append('td') - .attr('class', function(d) { - var klass = d.type === '+' ? 'add' : 'remove'; - return 'tagDiff-cell tagDiff-cell-' + klass; - }) - .text(function(d) { return d.display; }); - } - }; - - - validation.type = type; - - return validation; - } - - let _discardNameRegexes = []; - - function validationSuspiciousName() { - const type = 'suspicious_name'; - const keysToTestForGenericValues = [ - 'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', - 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway' - ]; - - // A concern here in switching to async data means that `_nsiFilters` will not - // be available at first, so the data on early tiles may not have tags validated fully. - - _mainFileFetcher.get('nsi_filters') - .then(filters => { - // known list of generic names (e.g. "bar") - _discardNameRegexes = filters.discardNames - .map(discardName => new RegExp(discardName, 'i')); - }) - .catch(() => { /* ignore */ }); - - - function isDiscardedSuggestionName(lowercaseName) { - return _discardNameRegexes.some(regex => regex.test(lowercaseName)); - } - - // test if the name is just the key or tag value (e.g. "park") - function nameMatchesRawTag(lowercaseName, tags) { - for (let i = 0; i < keysToTestForGenericValues.length; i++) { - let key = keysToTestForGenericValues[i]; - let val = tags[key]; - if (val) { - val = val.toLowerCase(); - if (key === lowercaseName || - val === lowercaseName || - key.replace(/\_/g, ' ') === lowercaseName || - val.replace(/\_/g, ' ') === lowercaseName) { - return true; - } - } - } - return false; - } - - function isGenericName(name, tags) { - name = name.toLowerCase(); - return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name); - } - - function makeGenericNameIssue(entityId, nameKey, genericName, langCode) { - return new validationIssue({ - type: type, - subtype: 'generic_name', - severity: 'warning', - message: function(context) { - let entity = context.hasEntity(this.entityIds[0]); - if (!entity) return ''; - let preset = _mainPresetIndex.match(entity, context.graph()); - let langName = langCode && _mainLocalizer.languageName(langCode); - return _t('issues.generic_name.message' + (langName ? '_language' : ''), - { feature: preset.name(), name: genericName, language: langName } - ); - }, - reference: showReference, - entityIds: [entityId], - hash: nameKey + '=' + genericName, - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.remove_the_name.title'), - onClick: function(context) { - let entityId = this.issue.entityIds[0]; - let entity = context.entity(entityId); - let tags = Object.assign({}, entity.tags); // shallow copy - delete tags[nameKey]; - context.perform( - actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation') - ); - } - }) - ]; - } - }); - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.generic_name.reference')); - } - } - - function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) { - return new validationIssue({ - type: type, - subtype: 'not_name', - severity: 'warning', - message: function(context) { - const entity = context.hasEntity(this.entityIds[0]); - if (!entity) return ''; - const preset = _mainPresetIndex.match(entity, context.graph()); - const langName = langCode && _mainLocalizer.languageName(langCode); - return _t('issues.incorrect_name.message' + (langName ? '_language' : ''), - { feature: preset.name(), name: incorrectName, language: langName } - ); - }, - reference: showReference, - entityIds: [entityId], - hash: nameKey + '=' + incorrectName, - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-operation-delete', - title: _t('issues.fix.remove_the_name.title'), - onClick: function(context) { - const entityId = this.issue.entityIds[0]; - const entity = context.entity(entityId); - let tags = Object.assign({}, entity.tags); // shallow copy - delete tags[nameKey]; - context.perform( - actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation') - ); - } - }) - ]; - } - }); - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.generic_name.reference')); - } - } - - - let validation = function checkGenericName(entity) { - // a generic name is okay if it's a known brand or entity - if (entity.hasWikidata()) return []; - - let issues = []; - const notNames = (entity.tags['not:name'] || '').split(';'); - - for (let key in entity.tags) { - const m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/); - if (!m) continue; - - const langCode = m.length >= 2 ? m[1] : null; - const value = entity.tags[key]; - if (notNames.length) { - for (let i in notNames) { - const notName = notNames[i]; - if (notName && value === notName) { - issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode)); - continue; - } - } - } - if (isGenericName(value, entity.tags)) { - issues.push(makeGenericNameIssue(entity.id, key, value, langCode)); - } - } - - return issues; - }; - - - validation.type = type; - - return validation; - } - - function validationUnsquareWay(context) { - var type = 'unsquare_way'; - var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js - - // use looser epsilon for detection to reduce warnings of buildings that are essentially square already - var epsilon = 0.05; - var nodeThreshold = 10; - - function isBuilding(entity, graph) { - if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false; - return entity.tags.building && entity.tags.building !== 'no'; - } - - - var validation = function checkUnsquareWay(entity, graph) { - - if (!isBuilding(entity, graph)) return []; - - // don't flag ways marked as physically unsquare - if (entity.tags.nonsquare === 'yes') return []; - - var isClosed = entity.isClosed(); - if (!isClosed) return []; // this building has bigger problems - - // don't flag ways with lots of nodes since they are likely detail-mapped - var nodes = graph.childNodes(entity).slice(); // shallow copy - if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice - - // ignore if not all nodes are fully downloaded - var osm = services.osm; - if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) return []; - - // don't flag connected ways to avoid unresolvable unsquare loops - var hasConnectedSquarableWays = nodes.some(function(node) { - return graph.parentWays(node).some(function(way) { - if (way.id === entity.id) return false; - if (isBuilding(way, graph)) return true; - return graph.parentRelations(way).some(function(parentRelation) { - return parentRelation.isMultipolygon() && - parentRelation.tags.building && - parentRelation.tags.building !== 'no'; - }); - }); - }); - if (hasConnectedSquarableWays) return []; - - - // user-configurable square threshold - var storedDegreeThreshold = corePreferences('validate-square-degrees'); - var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold); - - var points = nodes.map(function(node) { return context.projection(node.loc); }); - if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return []; - - var autoArgs; - // don't allow autosquaring features linked to wikidata - if (!entity.tags.wikidata) { - // use same degree threshold as for detection - var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold); - autoAction.transitionable = false; // when autofixing, do it instantly - autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')]; - } - - return [new validationIssue({ - type: type, - subtype: 'building', - severity: 'warning', - message: function(context) { - var entity = context.hasEntity(this.entityIds[0]); - return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : ''; - }, - reference: showReference, - entityIds: [entity.id], - hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold, - dynamicFixes: function() { - return [ - new validationIssueFix({ - icon: 'iD-operation-orthogonalize', - title: _t('issues.fix.square_feature.title'), - autoArgs: autoArgs, - onClick: function(context, completionHandler) { - var entityId = this.issue.entityIds[0]; - // use same degree threshold as for detection - context.perform( - actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), - _t('operations.orthogonalize.annotation.feature.single') - ); - // run after the squaring transition (currently 150ms) - window.setTimeout(function() { completionHandler(); }, 175); - } - }), - /* - new validationIssueFix({ - title: t('issues.fix.tag_as_unsquare.title'), - onClick: function(context) { - var entityId = this.issue.entityIds[0]; - var entity = context.entity(entityId); - var tags = Object.assign({}, entity.tags); // shallow copy - tags.nonsquare = 'yes'; - context.perform( - actionChangeTags(entityId, tags), - t('issues.fix.tag_as_unsquare.annotation') - ); - } - }) - */ - ]; - } - })]; - - function showReference(selection) { - selection.selectAll('.issue-reference') - .data([0]) - .enter() - .append('div') - .attr('class', 'issue-reference') - .text(_t('issues.unsquare_way.buildings.reference')); - } - }; - - validation.type = type; - - return validation; - } - - var Validations = /*#__PURE__*/Object.freeze({ - __proto__: null, - validationAlmostJunction: validationAlmostJunction, - validationCloseNodes: validationCloseNodes, - validationCrossingWays: validationCrossingWays, - validationDisconnectedWay: validationDisconnectedWay, - validationFormatting: validationFormatting, - validationHelpRequest: validationHelpRequest, - validationImpossibleOneway: validationImpossibleOneway, - validationIncompatibleSource: validationIncompatibleSource, - validationMaprules: validationMaprules, - validationMismatchedGeometry: validationMismatchedGeometry, - validationMissingRole: validationMissingRole, - validationMissingTag: validationMissingTag, - validationOutdatedTags: validationOutdatedTags, - validationPrivateData: validationPrivateData, - validationSuspiciousName: validationSuspiciousName, - validationUnsquareWay: validationUnsquareWay - }); - - function coreValidator(context) { - var dispatch$1 = dispatch('validated', 'focusedIssue'); - var validator = utilRebind({}, dispatch$1, 'on'); - - var _rules = {}; - var _disabledRules = {}; - - var _ignoredIssueIDs = {}; // issue.id -> true - var _baseCache = validationCache(); // issues before any user edits - var _headCache = validationCache(); // issues after all user edits - var _validatedGraph = null; - var _deferred = new Set(); - - // - // initialize the validator rulesets - // - validator.init = function() { - Object.values(Validations).forEach(function(validation) { - if (typeof validation !== 'function') return; - - var fn = validation(context); - var key = fn.type; - _rules[key] = fn; - }); - - var disabledRules = corePreferences('validate-disabledRules'); - if (disabledRules) { - disabledRules.split(',') - .forEach(function(key) { _disabledRules[key] = true; }); - } - }; - - - // - // clear caches, called whenever iD resets after a save - // - validator.reset = function() { - Array.from(_deferred).forEach(function(handle) { - window.cancelIdleCallback(handle); - _deferred.delete(handle); - }); - - // clear caches - _ignoredIssueIDs = {}; - _baseCache = validationCache(); - _headCache = validationCache(); - _validatedGraph = null; - }; - - validator.resetIgnoredIssues = function() { - _ignoredIssueIDs = {}; - // reload UI - dispatch$1.call('validated'); - }; - - - // must update issues when the user changes the unsquare thereshold - validator.reloadUnsquareIssues = function() { - - reloadUnsquareIssues(_headCache, context.graph()); - reloadUnsquareIssues(_baseCache, context.history().base()); - - dispatch$1.call('validated'); - }; - - function reloadUnsquareIssues(cache, graph) { - - var checkUnsquareWay = _rules.unsquare_way; - if (typeof checkUnsquareWay !== 'function') return; - - // uncache existing - cache.uncacheIssuesOfType('unsquare_way'); - - var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph) // everywhere - .filter(function(entity) { - return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no'; - }); - - // rerun for all buildings - buildings.forEach(function(entity) { - var detected = checkUnsquareWay(entity, graph); - if (detected.length !== 1) return; - var issue = detected[0]; - if (!cache.issuesByEntityID[entity.id]) { - cache.issuesByEntityID[entity.id] = new Set(); - } - cache.issuesByEntityID[entity.id].add(issue.id); - cache.issuesByIssueID[issue.id] = issue; - }); - } - - // options = { - // what: 'all', // 'all' or 'edited' - // where: 'all', // 'all' or 'visible' - // includeIgnored: false // true, false, or 'only' - // includeDisabledRules: false // true, false, or 'only' - // }; - validator.getIssues = function(options) { - var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options); - var issues = Object.values(_headCache.issuesByIssueID); - var view = context.map().extent(); - - return issues.filter(function(issue) { - if (!issue) return false; - if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false; - if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false; - - if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false; - if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; - - // Sanity check: This issue may be for an entity that not longer exists. - // If we detect this, uncache and return false so it is not included.. - var entityIds = issue.entityIds || []; - for (var i = 0; i < entityIds.length; i++) { - var entityId = entityIds[i]; - if (!context.hasEntity(entityId)) { - delete _headCache.issuesByEntityID[entityId]; - delete _headCache.issuesByIssueID[issue.id]; - return false; - } - } - - if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false; - - if (opts.where === 'visible') { - var extent = issue.extent(context.graph()); - if (!view.intersects(extent)) return false; - } - - return true; - }); - }; - - validator.getResolvedIssues = function() { - var baseIssues = Object.values(_baseCache.issuesByIssueID); - return baseIssues.filter(function(issue) { - return !_headCache.issuesByIssueID[issue.id]; - }); - }; - - validator.focusIssue = function(issue) { - var extent = issue.extent(context.graph()); - - if (extent) { - var setZoom = Math.max(context.map().zoom(), 19); - context.map().unobscuredCenterZoomEase(extent.center(), setZoom); - - // select the first entity - if (issue.entityIds && issue.entityIds.length) { - window.setTimeout(function() { - var ids = issue.entityIds; - context.enter(modeSelect(context, [ids[0]])); - dispatch$1.call('focusedIssue', this, issue); - }, 250); // after ease - } - } - }; - - - validator.getIssuesBySeverity = function(options) { - var groups = utilArrayGroupBy(validator.getIssues(options), 'severity'); - groups.error = groups.error || []; - groups.warning = groups.warning || []; - return groups; - }; - - // show some issue types in a particular order - var orderedIssueTypes = [ - // flag missing data first - 'missing_tag', 'missing_role', - // then flag identity issues - 'outdated_tags', 'mismatched_geometry', - // flag geometry issues where fixing them might solve connectivity issues - 'crossing_ways', 'almost_junction', - // then flag connectivity issues - 'disconnected_way', 'impossible_oneway' - ]; - - // returns the issues that the given entity IDs have in common, matching the given options - validator.getSharedEntityIssues = function(entityIDs, options) { - var cache = _headCache; - - // gather the issues that are common to all the entities - var issueIDs = entityIDs.reduce(function(acc, entityID) { - var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set(); - if (!acc) { - return new Set(entityIssueIDs); - } - return new Set([...acc].filter(function(elem) { - return entityIssueIDs.has(elem); - })); - }, null) || []; - - var opts = options || {}; - - return Array.from(issueIDs) - .map(function(id) { return cache.issuesByIssueID[id]; }) - .filter(function(issue) { - if (!issue) return false; - if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false; - if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false; - - if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false; - if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; - - return true; - }).sort(function(issue1, issue2) { - if (issue1.type === issue2.type) { - // issues of the same type, sort deterministically - return issue1.id < issue2.id ? -1 : 1; - } - var index1 = orderedIssueTypes.indexOf(issue1.type); - var index2 = orderedIssueTypes.indexOf(issue2.type); - if (index1 !== -1 && index2 !== -1) { - // both issue types have explicit sort orders - return index1 - index2; - } else if (index1 === -1 && index2 === -1) { - // neither issue type has an explicit sort order, sort by type - return issue1.type < issue2.type ? -1 : 1; - } else { - // order explicit types before everything else - return index1 !== -1 ? -1 : 1; - } - }); - }; - - - validator.getEntityIssues = function(entityID, options) { - return validator.getSharedEntityIssues([entityID], options); - }; - - - validator.getRuleKeys = function() { - return Object.keys(_rules); - }; - - - validator.isRuleEnabled = function(key) { - return !_disabledRules[key]; - }; - - - validator.toggleRule = function(key) { - if (_disabledRules[key]) { - delete _disabledRules[key]; - } else { - _disabledRules[key] = true; - } - - corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(',')); - validator.validate(); - }; - - - validator.disableRules = function(keys) { - _disabledRules = {}; - keys.forEach(function(k) { - _disabledRules[k] = true; - }); - - corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(',')); - validator.validate(); - }; - - - validator.ignoreIssue = function(id) { - _ignoredIssueIDs[id] = true; - }; - - - // - // Run validation on a single entity for the given graph - // - function validateEntity(entity, graph) { - var entityIssues = []; - - // runs validation and appends resulting issues - function runValidation(key) { - - var fn = _rules[key]; - if (typeof fn !== 'function') { - console.error('no such validation rule = ' + key); // eslint-disable-line no-console - return; - } - - var detected = fn(entity, graph); - entityIssues = entityIssues.concat(detected); - } - - // run all rules - Object.keys(_rules).forEach(runValidation); - - return entityIssues; - } - - function entityIDsToValidate(entityIDs, graph) { - var processedIDs = new Set(); - return entityIDs.reduce(function(acc, entityID) { - // keep redundancy check separate from `acc` because an `entityID` - // could have been added to `acc` as a related entity through an earlier pass - if (processedIDs.has(entityID)) return acc; - processedIDs.add(entityID); - - var entity = graph.hasEntity(entityID); - if (!entity) return acc; - - acc.add(entityID); - - var checkParentRels = [entity]; - - if (entity.type === 'node') { - graph.parentWays(entity).forEach(function(parentWay) { - acc.add(parentWay.id); // include parent ways - checkParentRels.push(parentWay); - }); - } else if (entity.type === 'relation') { - entity.members.forEach(function(member) { - acc.add(member.id); // include members - }); - } else if (entity.type === 'way') { - entity.nodes.forEach(function(nodeID) { - acc.add(nodeID); // include child nodes - graph._parentWays[nodeID].forEach(function(wayID) { - acc.add(wayID); // include connected ways - }); - }); - } - - checkParentRels.forEach(function(entity) { // include parent relations - if (entity.type !== 'relation') { // but not super-relations - graph.parentRelations(entity).forEach(function(parentRelation) { - acc.add(parentRelation.id); - }); - } - }); - - return acc; - - }, new Set()); - } - - // - // Run validation for several entities, supplied `entityIDs`, - // against `graph` for the given `cache` - // - function validateEntities(entityIDs, graph, cache) { - - // clear caches for existing issues related to these entities - entityIDs.forEach(cache.uncacheEntityID); - - // detect new issues and update caches - entityIDs.forEach(function(entityID) { - var entity = graph.hasEntity(entityID); - // don't validate deleted entities - if (!entity) return; - - var issues = validateEntity(entity, graph); - cache.cacheIssues(issues); - }); - } - - - // - // Validates anything that has changed since the last time it was run. - // Also updates the "validatedGraph" to be the current graph - // and dispatches a `validated` event when finished. - // - validator.validate = function() { - - var currGraph = context.graph(); - _validatedGraph = _validatedGraph || context.history().base(); - if (currGraph === _validatedGraph) { - dispatch$1.call('validated'); - return; - } - var oldGraph = _validatedGraph; - var difference = coreDifference(oldGraph, currGraph); - _validatedGraph = currGraph; - - var createdAndModifiedEntityIDs = difference.extantIDs(true); // created/modified (true = w/relation members) - var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph); - - // check modified and deleted entities against the old graph in order to update their related entities - // (e.g. deleting the only highway connected to a road should create a disconnected highway issue) - var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified()) - .map(function(entity) { return entity.id; }); - var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph); - - // concat the sets - entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck); - - validateEntities(entityIDsToCheck, context.graph(), _headCache); - - dispatch$1.call('validated'); - }; - - - // WHEN TO RUN VALIDATION: - // When graph changes: - context.history() - .on('restore.validator', validator.validate) // restore saved history - .on('undone.validator', validator.validate) // undo - .on('redone.validator', validator.validate); // redo - // but not on 'change' (e.g. while drawing) - - // When user chages editing modes: - context - .on('exit.validator', validator.validate); - - // When merging fetched data: - context.history() - .on('merge.validator', function(entities) { - if (!entities) return; - var handle = window.requestIdleCallback(function() { - var entityIDs = entities.map(function(entity) { return entity.id; }); - var headGraph = context.graph(); - validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache); - - var baseGraph = context.history().base(); - validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache); - - dispatch$1.call('validated'); - }); - _deferred.add(handle); - }); - - - return validator; - } - - - function validationCache() { - - var cache = { - issuesByIssueID: {}, // issue.id -> issue - issuesByEntityID: {} // entity.id -> set(issue.id) - }; - - cache.cacheIssues = function(issues) { - issues.forEach(function(issue) { - var entityIds = issue.entityIds || []; - entityIds.forEach(function(entityId) { - if (!cache.issuesByEntityID[entityId]) { - cache.issuesByEntityID[entityId] = new Set(); - } - cache.issuesByEntityID[entityId].add(issue.id); - }); - cache.issuesByIssueID[issue.id] = issue; - }); - }; - - cache.uncacheIssue = function(issue) { - // When multiple entities are involved (e.g. crossing_ways), - // remove this issue from the other entity caches too.. - var entityIds = issue.entityIds || []; - entityIds.forEach(function(entityId) { - if (cache.issuesByEntityID[entityId]) { - cache.issuesByEntityID[entityId].delete(issue.id); - } - }); - delete cache.issuesByIssueID[issue.id]; - }; - - cache.uncacheIssues = function(issues) { - issues.forEach(cache.uncacheIssue); - }; - - cache.uncacheIssuesOfType = function(type) { - var issuesOfType = Object.values(cache.issuesByIssueID) - .filter(function(issue) { return issue.type === type; }); - cache.uncacheIssues(issuesOfType); - }; - - // - // Remove a single entity and all its related issues from the caches - // - cache.uncacheEntityID = function(entityID) { - var issueIDs = cache.issuesByEntityID[entityID]; - if (!issueIDs) return; - - issueIDs.forEach(function(issueID) { - var issue = cache.issuesByIssueID[issueID]; - if (issue) { - cache.uncacheIssue(issue); - } else { - delete cache.issuesByIssueID[issueID]; - } - }); - - delete cache.issuesByEntityID[entityID]; - }; - - return cache; - } - - function coreUploader(context) { - - var dispatch$1 = dispatch( - // Start and end events are dispatched exactly once each per legitimate outside call to `save` - 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate - 'saveEnded', // dispatched after the result event has been dispatched - - 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will - 'progressChanged', - - // Each save results in one of these outcomes: - 'resultNoChanges', // upload wasn't attempted since there were no edits - 'resultErrors', // upload failed due to errors - 'resultConflicts', // upload failed due to data conflicts - 'resultSuccess' // upload completed without errors - ); - - var _isSaving = false; - - var _conflicts = []; - var _errors = []; - var _origChanges; - - var _discardTags = {}; - _mainFileFetcher.get('discarded') - .then(function(d) { _discardTags = d; }) - .catch(function() { /* ignore */ }); - - var uploader = utilRebind({}, dispatch$1, 'on'); - - uploader.isSaving = function() { - return _isSaving; - }; - - uploader.save = function(changeset, tryAgain, checkConflicts) { - // Guard against accidentally entering save code twice - #4641 - if (_isSaving && !tryAgain) { - return; - } - - var osm = context.connection(); - if (!osm) return; - - // If user somehow got logged out mid-save, try to reauthenticate.. - // This can happen if they were logged in from before, but the tokens are no longer valid. - if (!osm.authenticated()) { - osm.authenticate(function(err) { - if (!err) { - uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off.. - } - }); - return; - } - - if (!_isSaving) { - _isSaving = true; - dispatch$1.call('saveStarted', this); - } - - var history = context.history(); - - _conflicts = []; - _errors = []; - - // Store original changes, in case user wants to download them as an .osc file - _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); - - // First time, `history.perform` a no-op action. - // Any conflict resolutions will be done as `history.replace` - // Remember to pop this later if needed - if (!tryAgain) { - history.perform(actionNoop()); - } - - // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true` - if (!checkConflicts) { - upload(changeset); - - // Do the full (slow) conflict check.. - } else { - performFullConflictCheck(changeset); - } - - }; - - - function performFullConflictCheck(changeset) { - - var osm = context.connection(); - if (!osm) return; - - var history = context.history(); - - var localGraph = context.graph(); - var remoteGraph = coreGraph(history.base(), true); - - var summary = history.difference().summary(); - var _toCheck = []; - for (var i = 0; i < summary.length; i++) { - var item = summary[i]; - if (item.changeType === 'modified') { - _toCheck.push(item.entity.id); - } - } - - var _toLoad = withChildNodes(_toCheck, localGraph); - var _loaded = {}; - var _toLoadCount = 0; - var _toLoadTotal = _toLoad.length; - - if (_toCheck.length) { - dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal); - _toLoad.forEach(function(id) { _loaded[id] = false; }); - osm.loadMultiple(_toLoad, loaded); - } else { - upload(changeset); - } - - return; - - function withChildNodes(ids, graph) { - var s = new Set(ids); - ids.forEach(function(id) { - var entity = graph.entity(id); - if (entity.type !== 'way') return; - - graph.childNodes(entity).forEach(function(child) { - if (child.version !== undefined) { - s.add(child.id); - } - }); - }); - - return Array.from(s); - } - - - // Reload modified entities into an alternate graph and check for conflicts.. - function loaded(err, result) { - if (_errors.length) return; - - if (err) { - _errors.push({ - msg: err.message || err.responseText, - details: [ _t('save.status_code', { code: err.status }) ] - }); - didResultInErrors(); - - } else { - var loadMore = []; - - result.data.forEach(function(entity) { - remoteGraph.replace(entity); - _loaded[entity.id] = true; - _toLoad = _toLoad.filter(function(val) { return val !== entity.id; }); - - if (!entity.visible) return; - - // Because loadMultiple doesn't download /full like loadEntity, - // need to also load children that aren't already being checked.. - var i, id; - if (entity.type === 'way') { - for (i = 0; i < entity.nodes.length; i++) { - id = entity.nodes[i]; - if (_loaded[id] === undefined) { - _loaded[id] = false; - loadMore.push(id); - } - } - } else if (entity.type === 'relation' && entity.isMultipolygon()) { - for (i = 0; i < entity.members.length; i++) { - id = entity.members[i].id; - if (_loaded[id] === undefined) { - _loaded[id] = false; - loadMore.push(id); - } - } - } - }); - - _toLoadCount += result.data.length; - _toLoadTotal += loadMore.length; - dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal); - - if (loadMore.length) { - _toLoad.push.apply(_toLoad, loadMore); - osm.loadMultiple(loadMore, loaded); - } - - if (!_toLoad.length) { - detectConflicts(); - upload(changeset); - } - } - } - - - function detectConflicts() { - function choice(id, text, action) { - return { - id: id, - text: text, - action: function() { - history.replace(action); - } - }; - } - function formatUser(d) { - return '
    ' + d + ''; - } - function entityName(entity) { - return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id); - } - - function sameVersions(local, remote) { - if (local.version !== remote.version) return false; - - if (local.type === 'way') { - var children = utilArrayUnion(local.nodes, remote.nodes); - for (var i = 0; i < children.length; i++) { - var a = localGraph.hasEntity(children[i]); - var b = remoteGraph.hasEntity(children[i]); - if (a && b && a.version !== b.version) return false; - } - } - - return true; - } - - _toCheck.forEach(function(id) { - var local = localGraph.entity(id); - var remote = remoteGraph.entity(id); - - if (sameVersions(local, remote)) return; - - var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser); - - history.replace(merge); - - var mergeConflicts = merge.conflicts(); - if (!mergeConflicts.length) return; // merged safely - - var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local'); - var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote'); - var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore')); - var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete')); - - _conflicts.push({ - id: id, - name: entityName(local), - details: mergeConflicts, - chosen: 1, - choices: [ - choice(id, keepMine, forceLocal), - choice(id, keepTheirs, forceRemote) - ] - }); - }); - } - } - - - function upload(changeset) { - var osm = context.connection(); - if (!osm) { - _errors.push({ msg: 'No OSM Service' }); - } - - if (_conflicts.length) { - didResultInConflicts(changeset); - - } else if (_errors.length) { - didResultInErrors(); - - } else { - var history = context.history(); - var changes = history.changes(actionDiscardTags(history.difference(), _discardTags)); - if (changes.modified.length || changes.created.length || changes.deleted.length) { - - dispatch$1.call('willAttemptUpload', this); - - osm.putChangeset(changeset, changes, uploadCallback); - - } else { - // changes were insignificant or reverted by user - didResultInNoChanges(); - } - } - } - - - function uploadCallback(err, changeset) { - if (err) { - if (err.status === 409) { // 409 Conflict - uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true - } else { - _errors.push({ - msg: err.message || err.responseText, - details: [ _t('save.status_code', { code: err.status }) ] - }); - didResultInErrors(); - } - - } else { - didResultInSuccess(changeset); - } - } - - function didResultInNoChanges() { - - dispatch$1.call('resultNoChanges', this); - - endSave(); - - context.flush(); // reset iD - } - - function didResultInErrors() { - - context.history().pop(); - - dispatch$1.call('resultErrors', this, _errors); - - endSave(); - } - - - function didResultInConflicts(changeset) { - - _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); }); - - dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges); - - endSave(); - } - - - function didResultInSuccess(changeset) { - - // delete the edit stack cached to local storage - context.history().clearSaved(); - - dispatch$1.call('resultSuccess', this, changeset); - - // Add delay to allow for postgres replication #1646 #2678 - window.setTimeout(function() { - - endSave(); - - context.flush(); // reset iD - }, 2500); - } - - - function endSave() { - _isSaving = false; - - dispatch$1.call('saveEnded', this); - } - - - uploader.cancelConflictResolution = function() { - context.history().pop(); - }; - - - uploader.processResolvedConflicts = function(changeset) { - var history = context.history(); - - for (var i = 0; i < _conflicts.length; i++) { - if (_conflicts[i].chosen === 1) { // user chose "use theirs" - var entity = context.hasEntity(_conflicts[i].id); - if (entity && entity.type === 'way') { - var children = utilArrayUniq(entity.nodes); - for (var j = 0; j < children.length; j++) { - history.replace(actionRevert(children[j])); - } - } - history.replace(actionRevert(_conflicts[i].id)); - } - } - - uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false - }; - - - uploader.reset = function() { - - }; - - - return uploader; - } - - var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2; - - // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen - window.matchMedia(` - (-webkit-min-device-pixel-ratio: 2), /* Safari */ - (min-resolution: 2dppx), /* standard */ - (min-resolution: 192dpi) /* fallback */ - `).addListener(function() { - - isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2; - }); - - - function localeDateString(s) { - if (!s) return null; - var options = { day: 'numeric', month: 'short', year: 'numeric' }; - var d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(_mainLocalizer.localeCode(), options); - } - - function vintageRange(vintage) { - var s; - if (vintage.start || vintage.end) { - s = (vintage.start || '?'); - if (vintage.start !== vintage.end) { - s += ' - ' + (vintage.end || '?'); - } - } - return s; - } - - - function rendererBackgroundSource(data) { - var source = Object.assign({}, data); // shallow copy - var _offset = [0, 0]; - var _name = source.name; - var _description = source.description; - var _best = !!source.best; - var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template; - - source.tileSize = data.tileSize || 256; - source.zoomExtent = data.zoomExtent || [0, 22]; - source.overzoom = data.overzoom !== false; - - source.offset = function(val) { - if (!arguments.length) return _offset; - _offset = val; - return source; - }; - - - source.nudge = function(val, zoomlevel) { - _offset[0] += val[0] / Math.pow(2, zoomlevel); - _offset[1] += val[1] / Math.pow(2, zoomlevel); - return source; - }; - - - source.name = function() { - var id_safe = source.id.replace(/\./g, ''); - return _t('imagery.' + id_safe + '.name', { default: _name }); - }; - - - source.description = function() { - var id_safe = source.id.replace(/\./g, ''); - return _t('imagery.' + id_safe + '.description', { default: _description }); - }; - - - source.best = function() { - return _best; - }; - - - source.area = function() { - if (!data.polygon) return Number.MAX_VALUE; // worldwide - var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] }); - return isNaN(area) ? 0 : area; - }; - - - source.imageryUsed = function() { - return name || source.id; - }; - - - source.template = function(val) { - if (!arguments.length) return _template; - if (source.id === 'custom') { - _template = val; - } - return source; - }; - - - source.url = function(coord) { - var result = _template; - if (result === '') return result; // source 'none' - - - // Guess a type based on the tokens present in the template - // (This is for 'custom' source, where we don't know) - if (!source.type) { - if (/\{(proj|wkid|bbox)\}/.test(_template)) { - source.type = 'wms'; - source.projection = 'EPSG:3857'; // guess - } else if (/\{(x|y)\}/.test(_template)) { - source.type = 'tms'; - } else if (/\{u\}/.test(_template)) { - source.type = 'bing'; - } - } - - - if (source.type === 'wms') { - var tileToProjectedCoords = (function(x, y, z) { - //polyfill for IE11, PhantomJS - var sinh = Math.sinh || function(x) { - var y = Math.exp(x); - return (y - 1 / y) / 2; - }; - - var zoomSize = Math.pow(2, z); - var lon = x / zoomSize * Math.PI * 2 - Math.PI; - var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize))); - - switch (source.projection) { - case 'EPSG:4326': - return { - x: lon * 180 / Math.PI, - y: lat * 180 / Math.PI - }; - default: // EPSG:3857 and synonyms - var mercCoords = mercatorRaw(lon, lat); - return { - x: 20037508.34 / Math.PI * mercCoords[0], - y: 20037508.34 / Math.PI * mercCoords[1] - }; - } - }); - - var tileSize = source.tileSize; - var projection = source.projection; - var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]); - var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]); - - result = result.replace(/\{(\w+)\}/g, function (token, key) { - switch (key) { - case 'width': - case 'height': - return tileSize; - case 'proj': - return projection; - case 'wkid': - return projection.replace(/^EPSG:/, ''); - case 'bbox': - return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y; - case 'w': - return minXmaxY.x; - case 's': - return maxXminY.y; - case 'n': - return maxXminY.x; - case 'e': - return minXmaxY.y; - default: - return token; - } - }); - - } else if (source.type === 'tms') { - result = result - .replace('{x}', coord[0]) - .replace('{y}', coord[1]) - // TMS-flipped y coordinate - .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1) - .replace(/\{z(oom)?\}/, coord[2]) - // only fetch retina tiles for retina screens - .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : ''); - - } else if (source.type === 'bing') { - result = result - .replace('{u}', function() { - var u = ''; - for (var zoom = coord[2]; zoom > 0; zoom--) { - var b = 0; - var mask = 1 << (zoom - 1); - if ((coord[0] & mask) !== 0) b++; - if ((coord[1] & mask) !== 0) b += 2; - u += b.toString(); - } - return u; - }); - } - - // these apply to any type.. - result = result.replace(/\{switch:([^}]+)\}/, function(s, r) { - var subdomains = r.split(','); - return subdomains[(coord[0] + coord[1]) % subdomains.length]; - }); - - - return result; - }; - - - source.validZoom = function(z) { - return source.zoomExtent[0] <= z && - (source.overzoom || source.zoomExtent[1] > z); - }; - - - source.isLocatorOverlay = function() { - return source.id === 'mapbox_locator_overlay'; - }; - - - /* hides a source from the list, but leaves it available for use */ - source.isHidden = function() { - return source.id === 'DigitalGlobe-Premium-vintage' || - source.id === 'DigitalGlobe-Standard-vintage'; - }; - - - source.copyrightNotices = function() {}; - - - source.getMetadata = function(center, tileCoord, callback) { - var vintage = { - start: localeDateString(source.startDate), - end: localeDateString(source.endDate) - }; - vintage.range = vintageRange(vintage); - - var metadata = { vintage: vintage }; - callback(null, metadata); - }; - - - return source; - } - - - rendererBackgroundSource.Bing = function(data, dispatch) { - // http://msdn.microsoft.com/en-us/library/ff701716.aspx - // http://msdn.microsoft.com/en-us/library/ff701701.aspx - - data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z'; - - var bing = rendererBackgroundSource(data); - // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc - var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD - - - var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key; - var cache = {}; - var inflight = {}; - var providers = []; - - d3_json(url) - .then(function(json) { - providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) { - return { - attribution: provider.attribution, - areas: provider.coverageAreas.map(function(area) { - return { - zoom: [area.zoomMin, area.zoomMax], - extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]]) - }; - }) - }; - }); - dispatch.call('change'); - }) - .catch(function() { - /* ignore */ - }); - - - bing.copyrightNotices = function(zoom, extent) { - zoom = Math.min(zoom, 21); - return providers.filter(function(provider) { - return provider.areas.some(function(area) { - return extent.intersects(area.extent) && - area.zoom[0] <= zoom && - area.zoom[1] >= zoom; - }); - }).map(function(provider) { - return provider.attribution; - }).join(', '); - }; - - - bing.getMetadata = function(center, tileCoord, callback) { - var tileID = tileCoord.slice(0, 3).join('/'); - var zoom = Math.min(tileCoord[2], 21); - var centerPoint = center[1] + ',' + center[0]; // lat,lng - var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + - '?zl=' + zoom + '&key=' + key; - - if (inflight[tileID]) return; - - if (!cache[tileID]) { - cache[tileID] = {}; - } - if (cache[tileID] && cache[tileID].metadata) { - return callback(null, cache[tileID].metadata); - } - - inflight[tileID] = true; - d3_json(url) - .then(function(result) { - delete inflight[tileID]; - if (!result) { - throw new Error('Unknown Error'); - } - var vintage = { - start: localeDateString(result.resourceSets[0].resources[0].vintageStart), - end: localeDateString(result.resourceSets[0].resources[0].vintageEnd) - }; - vintage.range = vintageRange(vintage); - - var metadata = { vintage: vintage }; - cache[tileID].metadata = metadata; - if (callback) callback(null, metadata); - }) - .catch(function(err) { - delete inflight[tileID]; - if (callback) callback(err.message); - }); - }; - - - bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details'; - - - return bing; - }; - - - - rendererBackgroundSource.Esri = function(data) { - // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works) - if (data.template.match(/blankTile/) === null) { - data.template = data.template + '?blankTile=false'; - } - - var esri = rendererBackgroundSource(data); - var cache = {}; - var inflight = {}; - var _prevCenter; - - // use a tilemap service to set maximum zoom for esri tiles dynamically - // https://developers.arcgis.com/documentation/tiled-elevation-service/ - esri.fetchTilemap = function(center) { - // skip if we have already fetched a tilemap within 5km - if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return; - _prevCenter = center; - - // tiles are available globally to zoom level 19, afterward they may or may not be present - var z = 20; - - // first generate a random url using the template - var dummyUrl = esri.url([1,2,3]); - - // calculate url z/y/x from the lat/long of the center of the map - var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z))); - var y = (Math.floor((1 - Math.log(Math.tan(center[1] * Math.PI / 180) + 1 / Math.cos(center[1] * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, z))); - - // fetch an 8x8 grid to leverage cache - var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8'; - - // make the request and introspect the response from the tilemap server - d3_json(tilemapUrl) - .then(function(tilemap) { - if (!tilemap) { - throw new Error('Unknown Error'); - } - var hasTiles = true; - for (var i = 0; i < tilemap.data.length; i++) { - // 0 means an individual tile in the grid doesn't exist - if (!tilemap.data[i]) { - hasTiles = false; - break; - } - } - - // if any tiles are missing at level 20 we restrict maxZoom to 19 - esri.zoomExtent[1] = (hasTiles ? 22 : 19); - }) - .catch(function() { - /* ignore */ - }); - }; - - - esri.getMetadata = function(center, tileCoord, callback) { - var tileID = tileCoord.slice(0, 3).join('/'); - var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]); - var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be) - var unknown = _t('info_panels.background.unknown'); - var metadataLayer; - var vintage = {}; - var metadata = {}; - - if (inflight[tileID]) return; - - switch (true) { - case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'): - metadataLayer = 4; - break; - case zoom >= 19: - metadataLayer = 3; - break; - case zoom >= 17: - metadataLayer = 2; - break; - case zoom >= 13: - metadataLayer = 0; - break; - default: - metadataLayer = 99; - } - - var url; - // build up query using the layer appropriate to the current zoom - if (esri.id === 'EsriWorldImagery') { - url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/'; - } else if (esri.id === 'EsriWorldImageryClarity') { - url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/'; - } - - url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json'; - - if (!cache[tileID]) { - cache[tileID] = {}; - } - if (cache[tileID] && cache[tileID].metadata) { - return callback(null, cache[tileID].metadata); - } - - // accurate metadata is only available >= 13 - if (metadataLayer === 99) { - vintage = { - start: null, - end: null, - range: null - }; - metadata = { - vintage: null, - source: unknown, - description: unknown, - resolution: unknown, - accuracy: unknown - }; - - callback(null, metadata); - - } else { - inflight[tileID] = true; - d3_json(url) - .then(function(result) { - delete inflight[tileID]; - if (!result) { - throw new Error('Unknown Error'); - } else if (result.features && result.features.length < 1) { - throw new Error('No Results'); - } else if (result.error && result.error.message) { - throw new Error(result.error.message); - } - - // pass through the discrete capture date from metadata - var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2); - vintage = { - start: captureDate, - end: captureDate, - range: captureDate - }; - metadata = { - vintage: vintage, - source: clean(result.features[0].attributes.NICE_NAME), - description: clean(result.features[0].attributes.NICE_DESC), - resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)), - accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4)) - }; - - // append units - meters - if (isFinite(metadata.resolution)) { - metadata.resolution += ' m'; - } - if (isFinite(metadata.accuracy)) { - metadata.accuracy += ' m'; - } - - cache[tileID].metadata = metadata; - if (callback) callback(null, metadata); - }) - .catch(function(err) { - delete inflight[tileID]; - if (callback) callback(err.message); - }); - } - - - function clean(val) { - return String(val).trim() || unknown; - } - }; - - return esri; - }; - - - rendererBackgroundSource.None = function() { - var source = rendererBackgroundSource({ id: 'none', template: '' }); - - - source.name = function() { - return _t('background.none'); - }; - - - source.imageryUsed = function() { - return null; - }; - - - source.area = function() { - return -1; // sources in background pane are sorted by area - }; - - - return source; - }; - - - rendererBackgroundSource.Custom = function(template) { - var source = rendererBackgroundSource({ id: 'custom', template: template }); - - - source.name = function() { - return _t('background.custom'); - }; - - - source.imageryUsed = function() { - // sanitize personal connection tokens - #6801 - var cleaned = source.template(); - - // from query string parameters - if (cleaned.indexOf('?') !== -1) { - var parts = cleaned.split('?', 2); - var qs = utilStringQs(parts[1]); - - ['access_token', 'connectId', 'token'].forEach(function(param) { - if (qs[param]) { - qs[param] = '{apikey}'; - } - }); - cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode - } - - // from wms/wmts api path parameters - cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}'); - - return 'Custom (' + cleaned + ' )'; - }; - - - source.area = function() { - return -2; // sources in background pane are sorted by area - }; - - - return source; - }; - - function rendererTileLayer(context) { - var transformProp = utilPrefixCSSProperty('Transform'); - var tiler = utilTiler(); - - var _tileSize = 256; - var _projection; - var _cache = {}; - var _tileOrigin; - var _zoom; - var _source; - - - function tileSizeAtZoom(d, z) { - var EPSILON = 0.002; // close seams - return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON; - } - - - function atZoom(t, distance) { - var power = Math.pow(2, distance); - return [ - Math.floor(t[0] * power), - Math.floor(t[1] * power), - t[2] + distance - ]; - } - - - function lookUp(d) { - for (var up = -1; up > -d[2]; up--) { - var tile = atZoom(d, up); - if (_cache[_source.url(tile)] !== false) { - return tile; - } - } - } - - - function uniqueBy(a, n) { - var o = []; - var seen = {}; - for (var i = 0; i < a.length; i++) { - if (seen[a[i][n]] === undefined) { - o.push(a[i]); - seen[a[i][n]] = true; - } - } - return o; - } - - - function addSource(d) { - d.push(_source.url(d)); - return d; - } - - - // Update tiles based on current state of `projection`. - function background(selection) { - _zoom = geoScaleToZoom(_projection.scale(), _tileSize); - - var pixelOffset; - if (_source) { - pixelOffset = [ - _source.offset()[0] * Math.pow(2, _zoom), - _source.offset()[1] * Math.pow(2, _zoom) - ]; - } else { - pixelOffset = [0, 0]; - } - - var translate = [ - _projection.translate()[0] + pixelOffset[0], - _projection.translate()[1] + pixelOffset[1] - ]; - - tiler - .scale(_projection.scale() * 2 * Math.PI) - .translate(translate); - - _tileOrigin = [ - _projection.scale() * Math.PI - translate[0], - _projection.scale() * Math.PI - translate[1] - ]; - - render(selection); - } - - - // Derive the tiles onscreen, remove those offscreen and position them. - // Important that this part not depend on `_projection` because it's - // rentered when tiles load/error (see #644). - function render(selection) { - if (!_source) return; - var requests = []; - var showDebug = context.getDebug('tile') && !_source.overlay; - - if (_source.validZoom(_zoom)) { - tiler.skipNullIsland(!!_source.overlay); - - tiler().forEach(function(d) { - addSource(d); - if (d[3] === '') return; - if (typeof d[3] !== 'string') return; // Workaround for #2295 - requests.push(d); - if (_cache[d[3]] === false && lookUp(d)) { - requests.push(addSource(lookUp(d))); - } - }); - - requests = uniqueBy(requests, 3).filter(function(r) { - // don't re-request tiles which have failed in the past - return _cache[r[3]] !== false; - }); - } - - function load(d) { - _cache[d[3]] = true; - select(this) - .on('error', null) - .on('load', null) - .classed('tile-loaded', true); - render(selection); - } - - function error(d) { - _cache[d[3]] = false; - select(this) - .on('error', null) - .on('load', null) - .remove(); - render(selection); - } - - function imageTransform(d) { - var ts = _tileSize * Math.pow(2, _zoom - d[2]); - var scale = tileSizeAtZoom(d, _zoom); - return 'translate(' + - ((d[0] * ts) - _tileOrigin[0]) + 'px,' + - ((d[1] * ts) - _tileOrigin[1]) + 'px) ' + - 'scale(' + scale + ',' + scale + ')'; - } - - function tileCenter(d) { - var ts = _tileSize * Math.pow(2, _zoom - d[2]); - return [ - ((d[0] * ts) - _tileOrigin[0] + (ts / 2)), - ((d[1] * ts) - _tileOrigin[1] + (ts / 2)) - ]; - } - - function debugTransform(d) { - var coord = tileCenter(d); - return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)'; - } - - - // Pick a representative tile near the center of the viewport - // (This is useful for sampling the imagery vintage) - var dims = tiler.size(); - var mapCenter = [dims[0] / 2, dims[1] / 2]; - var minDist = Math.max(dims[0], dims[1]); - var nearCenter; - - requests.forEach(function(d) { - var c = tileCenter(d); - var dist = geoVecLength(c, mapCenter); - if (dist < minDist) { - minDist = dist; - nearCenter = d; - } - }); - - - var image = selection.selectAll('img') - .data(requests, function(d) { return d[3]; }); - - image.exit() - .style(transformProp, imageTransform) - .classed('tile-removing', true) - .classed('tile-center', false) - .each(function() { - var tile = select(this); - window.setTimeout(function() { - if (tile.classed('tile-removing')) { - tile.remove(); - } - }, 300); - }); - - image.enter() - .append('img') - .attr('class', 'tile') - .attr('draggable', 'false') - .style('width', _tileSize + 'px') - .style('height', _tileSize + 'px') - .attr('src', function(d) { return d[3]; }) - .on('error', error) - .on('load', load) - .merge(image) - .style(transformProp, imageTransform) - .classed('tile-debug', showDebug) - .classed('tile-removing', false) - .classed('tile-center', function(d) { return d === nearCenter; }); - - - - var debug = selection.selectAll('.tile-label-debug') - .data(showDebug ? requests : [], function(d) { return d[3]; }); - - debug.exit() - .remove(); - - if (showDebug) { - var debugEnter = debug.enter() - .append('div') - .attr('class', 'tile-label-debug'); - - debugEnter - .append('div') - .attr('class', 'tile-label-debug-coord'); - - debugEnter - .append('div') - .attr('class', 'tile-label-debug-vintage'); - - debug = debug.merge(debugEnter); - - debug - .style(transformProp, debugTransform); - - debug - .selectAll('.tile-label-debug-coord') - .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; }); - - debug - .selectAll('.tile-label-debug-vintage') - .each(function(d) { - var span = select(this); - var center = context.projection.invert(tileCenter(d)); - _source.getMetadata(center, d, function(err, result) { - span.text((result && result.vintage && result.vintage.range) || - _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown') - ); - }); - }); - } - - } - - - background.projection = function(val) { - if (!arguments.length) return _projection; - _projection = val; - return background; - }; - - - background.dimensions = function(val) { - if (!arguments.length) return tiler.size(); - tiler.size(val); - return background; - }; - - - background.source = function(val) { - if (!arguments.length) return _source; - _source = val; - _tileSize = _source.tileSize; - _cache = {}; - tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent); - return background; - }; - - - return background; - } - - let _imageryIndex = null; - - function rendererBackground(context) { - const dispatch$1 = dispatch('change'); - const detected = utilDetect(); - const baseLayer = rendererTileLayer(context).projection(context.projection); - let _isValid = true; - let _overlayLayers = []; - let _brightness = 1; - let _contrast = 1; - let _saturation = 1; - let _sharpness = 1; - - - function ensureImageryIndex() { - return _mainFileFetcher.get('imagery') - .then(sources => { - if (_imageryIndex) return _imageryIndex; - - _imageryIndex = { - imagery: sources, - features: {} - }; - - // use which-polygon to support efficient index and querying for imagery - const features = sources.map(source => { - if (!source.polygon) return null; - // workaround for editor-layer-index weirdness.. - // Add an extra array nest to each element in `source.polygon` - // so the rings are not treated as a bunch of holes: - // what we have: [ [[outer],[hole],[hole]] ] - // what we want: [ [[outer]],[[outer]],[[outer]] ] - const rings = source.polygon.map(ring => [ring]); - - const feature = { - type: 'Feature', - properties: { id: source.id }, - geometry: { type: 'MultiPolygon', coordinates: rings } - }; - - _imageryIndex.features[source.id] = feature; - return feature; - - }).filter(Boolean); - - _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features }); - - - // Instantiate `rendererBackgroundSource` objects for each source - _imageryIndex.backgrounds = sources.map(source => { - if (source.type === 'bing') { - return rendererBackgroundSource.Bing(source, dispatch$1); - } else if (/^EsriWorldImagery/.test(source.id)) { - return rendererBackgroundSource.Esri(source); - } else { - return rendererBackgroundSource(source); - } - }); - - // Add 'None' - _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); - - // Add 'Custom' - let template = corePreferences('background-custom-template') || ''; - const custom = rendererBackgroundSource.Custom(template); - _imageryIndex.backgrounds.unshift(custom); - - return _imageryIndex; - }); - } - - - function background(selection) { - const currSource = baseLayer.source(); - - // If we are displaying an Esri basemap at high zoom, - // check its tilemap to see how high the zoom can go - if (context.map().zoom() > 18) { - if (currSource && /^EsriWorldImagery/.test(currSource.id)) { - const center = context.map().center(); - currSource.fetchTilemap(center); - } - } - - // Is the imagery valid here? - #4827 - const sources = background.sources(context.map().extent()); - const wasValid = _isValid; - _isValid = !!sources.filter(d => d === currSource).length; - - if (wasValid !== _isValid) { // change in valid status - background.updateImagery(); - } - - - let baseFilter = ''; - if (detected.cssfilters) { - if (_brightness !== 1) { - baseFilter += ` brightness(${_brightness})`; - } - if (_contrast !== 1) { - baseFilter += ` contrast(${_contrast})`; - } - if (_saturation !== 1) { - baseFilter += ` saturate(${_saturation})`; - } - if (_sharpness < 1) { // gaussian blur - const blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness); - baseFilter += ` blur(${blur}px)`; - } - } - - let base = selection.selectAll('.layer-background') - .data([0]); - - base = base.enter() - .insert('div', '.layer-data') - .attr('class', 'layer layer-background') - .merge(base); - - if (detected.cssfilters) { - base.style('filter', baseFilter || null); - } else { - base.style('opacity', _brightness); - } - - - let imagery = base.selectAll('.layer-imagery') - .data([0]); - - imagery.enter() - .append('div') - .attr('class', 'layer layer-imagery') - .merge(imagery) - .call(baseLayer); - - - let maskFilter = ''; - let mixBlendMode = ''; - if (detected.cssfilters && _sharpness > 1) { // apply unsharp mask - mixBlendMode = 'overlay'; - maskFilter = 'saturate(0) blur(3px) invert(1)'; - - let contrast = _sharpness - 1; - maskFilter += ` contrast(${contrast})`; - - let brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1); - maskFilter += ` brightness(${brightness})`; - } - - let mask = base.selectAll('.layer-unsharp-mask') - .data(detected.cssfilters && _sharpness > 1 ? [0] : []); - - mask.exit() - .remove(); - - mask.enter() - .append('div') - .attr('class', 'layer layer-mask layer-unsharp-mask') - .merge(mask) - .call(baseLayer) - .style('filter', maskFilter || null) - .style('mix-blend-mode', mixBlendMode || null); - - - let overlays = selection.selectAll('.layer-overlay') - .data(_overlayLayers, d => d.source().name()); - - overlays.exit() - .remove(); - - overlays.enter() - .insert('div', '.layer-data') - .attr('class', 'layer layer-overlay') - .merge(overlays) - .each((layer, i, nodes) => select(nodes[i]).call(layer)); - } - - - background.updateImagery = function() { - let currSource = baseLayer.source(); - if (context.inIntro() || !currSource) return; - - let o = _overlayLayers - .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden()) - .map(d => d.source().id) - .join(','); - - const meters = geoOffsetToMeters(currSource.offset()); - const EPSILON = 0.01; - const x = +meters[0].toFixed(2); - const y = +meters[1].toFixed(2); - let hash = utilStringQs(window.location.hash); - - let id = currSource.id; - if (id === 'custom') { - id = `custom:${currSource.template()}`; - } - - if (id) { - hash.background = id; - } else { - delete hash.background; - } - - if (o) { - hash.overlays = o; - } else { - delete hash.overlays; - } - - if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) { - hash.offset = `${x},${y}`; - } else { - delete hash.offset; - } - - if (!window.mocha) { - window.location.replace('#' + utilQsString(hash, true)); - } - - let imageryUsed = []; - let photoOverlaysUsed = []; - - const currUsed = currSource.imageryUsed(); - if (currUsed && _isValid) { - imageryUsed.push(currUsed); - } - - _overlayLayers - .filter(d => !d.source().isLocatorOverlay() && !d.source().isHidden()) - .forEach(d => imageryUsed.push(d.source().imageryUsed())); - - const dataLayer = context.layers().layer('data'); - if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) { - imageryUsed.push(dataLayer.getSrc()); - } - - const photoOverlayLayers = { - streetside: 'Bing Streetside', - mapillary: 'Mapillary Images', - 'mapillary-map-features': 'Mapillary Map Features', - 'mapillary-signs': 'Mapillary Signs', - openstreetcam: 'OpenStreetCam Images' - }; - - for (let layerID in photoOverlayLayers) { - const layer = context.layers().layer(layerID); - if (layer && layer.enabled()) { - photoOverlaysUsed.push(layerID); - imageryUsed.push(photoOverlayLayers[layerID]); - } - } - - context.history().imageryUsed(imageryUsed); - context.history().photoOverlaysUsed(photoOverlaysUsed); - }; - - - background.sources = (extent, zoom, includeCurrent) => { - if (!_imageryIndex) return []; // called before init()? - - let visible = {}; - (_imageryIndex.query.bbox(extent.rectangle(), true) || []) - .forEach(d => visible[d.id] = true); - - const currSource = baseLayer.source(); - - return _imageryIndex.backgrounds.filter(source => { - if (!source.polygon) return true; // always include imagery with worldwide coverage - if (includeCurrent && currSource === source) return true; // optionally include the current imagery - if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms - return visible[source.id]; // include imagery visible in given extent - }); - }; - - - background.dimensions = (val) => { - if (!val) return; - baseLayer.dimensions(val); - _overlayLayers.forEach(layer => layer.dimensions(val)); - }; - - - background.baseLayerSource = function(d) { - if (!arguments.length) return baseLayer.source(); - - // test source against OSM imagery blacklists.. - const osm = context.connection(); - if (!osm) return background; - - const blacklists = osm.imageryBlacklists(); - const template = d.template(); - let fail = false; - let tested = 0; - let regex; - - for (let i = 0; i < blacklists.length; i++) { - try { - regex = new RegExp(blacklists[i]); - fail = regex.test(template); - tested++; - if (fail) break; - } catch (e) { - /* noop */ - } - } - - // ensure at least one test was run. - if (!tested) { - regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'); - fail = regex.test(template); - } - - baseLayer.source(!fail ? d : background.findSource('none')); - dispatch$1.call('change'); - background.updateImagery(); - return background; - }; - - - background.findSource = (id) => { - if (!id || !_imageryIndex) return null; // called before init()? - return _imageryIndex.backgrounds.find(d => d.id && d.id === id); - }; - - - background.bing = () => { - background.baseLayerSource(background.findSource('Bing')); - }; - - - background.showsLayer = (d) => { - const currSource = baseLayer.source(); - if (!d || !currSource) return false; - return d.id === currSource.id || _overlayLayers.some(layer => d.id === layer.source().id); - }; - - - background.overlayLayerSources = () => { - return _overlayLayers.map(layer => layer.source()); - }; - - - background.toggleOverlayLayer = (d) => { - let layer; - for (let i = 0; i < _overlayLayers.length; i++) { - layer = _overlayLayers[i]; - if (layer.source() === d) { - _overlayLayers.splice(i, 1); - dispatch$1.call('change'); - background.updateImagery(); - return; - } - } - - layer = rendererTileLayer(context) - .source(d) - .projection(context.projection) - .dimensions(baseLayer.dimensions() - ); - - _overlayLayers.push(layer); - dispatch$1.call('change'); - background.updateImagery(); - }; - - - background.nudge = (d, zoom) => { - const currSource = baseLayer.source(); - if (currSource) { - currSource.nudge(d, zoom); - dispatch$1.call('change'); - background.updateImagery(); - } - return background; - }; - - - background.offset = function(d) { - const currSource = baseLayer.source(); - if (!arguments.length) { - return (currSource && currSource.offset()) || [0, 0]; - } - if (currSource) { - currSource.offset(d); - dispatch$1.call('change'); - background.updateImagery(); - } - return background; - }; - - - background.brightness = function(d) { - if (!arguments.length) return _brightness; - _brightness = d; - if (context.mode()) dispatch$1.call('change'); - return background; - }; - - - background.contrast = function(d) { - if (!arguments.length) return _contrast; - _contrast = d; - if (context.mode()) dispatch$1.call('change'); - return background; - }; - - - background.saturation = function(d) { - if (!arguments.length) return _saturation; - _saturation = d; - if (context.mode()) dispatch$1.call('change'); - return background; - }; - - - background.sharpness = function(d) { - if (!arguments.length) return _sharpness; - _sharpness = d; - if (context.mode()) dispatch$1.call('change'); - return background; - }; - - let _loadPromise; - - background.ensureLoaded = () => { - - if (_loadPromise) return _loadPromise; - - function parseMapParams(qmap) { - if (!qmap) return false; - const params = qmap.split('/').map(Number); - if (params.length < 3 || params.some(isNaN)) return false; - return geoExtent([params[2], params[1]]); // lon,lat - } - - const hash = utilStringQs(window.location.hash); - const requested = hash.background || hash.layer; - let extent = parseMapParams(hash.map); - - return _loadPromise = ensureImageryIndex() - .then(imageryIndex => { - const first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0]; - - let best; - if (!requested && extent) { - best = background.sources(extent).find(s => s.best()); - } - - // Decide which background layer to display - if (requested && requested.indexOf('custom:') === 0) { - const template = requested.replace(/^custom:/, ''); - const custom = background.findSource('custom'); - background.baseLayerSource(custom.template(template)); - corePreferences('background-custom-template', template); - } else { - background.baseLayerSource( - background.findSource(requested) || - best || - background.findSource(corePreferences('background-last-used')) || - background.findSource('Bing') || - first || - background.findSource('none') - ); - } - - const locator = imageryIndex.backgrounds.find(d => d.overlay && d.default); - if (locator) { - background.toggleOverlayLayer(locator); - } - - const overlays = (hash.overlays || '').split(','); - overlays.forEach(overlay => { - overlay = background.findSource(overlay); - if (overlay) { - background.toggleOverlayLayer(overlay); - } - }); - - if (hash.gpx) { - const gpx = context.layers().layer('data'); - if (gpx) { - gpx.url(hash.gpx, '.gpx'); - } - } - - if (hash.offset) { - const offset = hash.offset - .replace(/;/g, ',') - .split(',') - .map(n => !isNaN(n) && n); - - if (offset.length === 2) { - background.offset(geoMetersToOffset(offset)); - } - } - }) - .catch(() => { /* ignore */ }); - }; - - - return utilRebind(background, dispatch$1, 'on'); - } - - function rendererFeatures(context) { - var dispatch$1 = dispatch('change', 'redraw'); - var features = utilRebind({}, dispatch$1, 'on'); - var _deferred = new Set(); - - var traffic_roads = { - 'motorway': true, - 'motorway_link': true, - 'trunk': true, - 'trunk_link': true, - 'primary': true, - 'primary_link': true, - 'secondary': true, - 'secondary_link': true, - 'tertiary': true, - 'tertiary_link': true, - 'residential': true, - 'unclassified': true, - 'living_street': true - }; - - var service_roads = { - 'service': true, - 'road': true, - 'track': true - }; - - var paths = { - 'path': true, - 'footway': true, - 'cycleway': true, - 'bridleway': true, - 'steps': true, - 'pedestrian': true - }; - - var past_futures = { - 'proposed': true, - 'construction': true, - 'abandoned': true, - 'dismantled': true, - 'disused': true, - 'razed': true, - 'demolished': true, - 'obliterated': true - }; - - var _cullFactor = 1; - var _cache = {}; - var _rules = {}; - var _stats = {}; - var _keys = []; - var _hidden = []; - var _forceVisible = {}; - - - function update() { - if (!window.mocha) { - var hash = utilStringQs(window.location.hash); - var disabled = features.disabled(); - if (disabled.length) { - hash.disable_features = disabled.join(','); - } else { - delete hash.disable_features; - } - window.location.replace('#' + utilQsString(hash, true)); - corePreferences('disabled-features', disabled.join(',')); - } - _hidden = features.hidden(); - dispatch$1.call('change'); - dispatch$1.call('redraw'); - } - - - function defineRule(k, filter, max) { - var isEnabled = true; - - _keys.push(k); - _rules[k] = { - filter: filter, - enabled: isEnabled, // whether the user wants it enabled.. - count: 0, - currentMax: (max || Infinity), - defaultMax: (max || Infinity), - enable: function() { this.enabled = true; this.currentMax = this.defaultMax; }, - disable: function() { this.enabled = false; this.currentMax = 0; }, - hidden: function() { - return (this.count === 0 && !this.enabled) || - this.count > this.currentMax * _cullFactor; - }, - autoHidden: function() { return this.hidden() && this.currentMax > 0; } - }; - } - - - defineRule('points', function isPoint(tags, geometry) { - return geometry === 'point'; - }, 200); - - defineRule('traffic_roads', function isTrafficRoad(tags) { - return traffic_roads[tags.highway]; - }); - - defineRule('service_roads', function isServiceRoad(tags) { - return service_roads[tags.highway]; - }); - - defineRule('paths', function isPath(tags) { - return paths[tags.highway]; - }); - - defineRule('buildings', function isBuilding(tags) { - return ( - (!!tags.building && tags.building !== 'no') || - tags.parking === 'multi-storey' || - tags.parking === 'sheds' || - tags.parking === 'carports' || - tags.parking === 'garage_boxes' - ); - }, 250); - - defineRule('building_parts', function isBuildingPart(tags) { - return tags['building:part']; - }); - - defineRule('indoor', function isIndoor(tags) { - return tags.indoor; - }); - - defineRule('landuse', function isLanduse(tags, geometry) { - return geometry === 'area' && - !_rules.buildings.filter(tags) && - !_rules.building_parts.filter(tags) && - !_rules.indoor.filter(tags) && - !_rules.water.filter(tags) && - !_rules.pistes.filter(tags); - }); - - defineRule('boundaries', function isBoundary(tags) { - return ( - !!tags.boundary - ) && !( - traffic_roads[tags.highway] || - service_roads[tags.highway] || - paths[tags.highway] || - tags.waterway || - tags.railway || - tags.landuse || - tags.natural || - tags.building || - tags.power - ); - }); - - defineRule('water', function isWater(tags) { - return ( - !!tags.waterway || - tags.natural === 'water' || - tags.natural === 'coastline' || - tags.natural === 'bay' || - tags.landuse === 'pond' || - tags.landuse === 'basin' || - tags.landuse === 'reservoir' || - tags.landuse === 'salt_pond' - ); - }); - - defineRule('rail', function isRail(tags) { - return ( - !!tags.railway || - tags.landuse === 'railway' - ) && !( - traffic_roads[tags.highway] || - service_roads[tags.highway] || - paths[tags.highway] - ); - }); - - defineRule('pistes', function isPiste(tags) { - return tags['piste:type']; - }); - - defineRule('aerialways', function isPiste(tags) { - return tags.aerialway && - tags.aerialway !== 'yes' && - tags.aerialway !== 'station'; - }); - - defineRule('power', function isPower(tags) { - return !!tags.power; - }); - - // contains a past/future tag, but not in active use as a road/path/cycleway/etc.. - defineRule('past_future', function isPastFuture(tags) { - if ( - traffic_roads[tags.highway] || - service_roads[tags.highway] || - paths[tags.highway] - ) { return false; } - - var strings = Object.keys(tags); - - for (var i = 0; i < strings.length; i++) { - var s = strings[i]; - if (past_futures[s] || past_futures[tags[s]]) { return true; } - } - return false; - }); - - // Lines or areas that don't match another feature filter. - // IMPORTANT: The 'others' feature must be the last one defined, - // so that code in getMatches can skip this test if `hasMatch = true` - defineRule('others', function isOther(tags, geometry) { - return (geometry === 'line' || geometry === 'area'); - }); - - - - features.features = function() { - return _rules; - }; - - - features.keys = function() { - return _keys; - }; - - - features.enabled = function(k) { - if (!arguments.length) { - return _keys.filter(function(k) { return _rules[k].enabled; }); - } - return _rules[k] && _rules[k].enabled; - }; - - - features.disabled = function(k) { - if (!arguments.length) { - return _keys.filter(function(k) { return !_rules[k].enabled; }); - } - return _rules[k] && !_rules[k].enabled; - }; - - - features.hidden = function(k) { - if (!arguments.length) { - return _keys.filter(function(k) { return _rules[k].hidden(); }); - } - return _rules[k] && _rules[k].hidden(); - }; - - - features.autoHidden = function(k) { - if (!arguments.length) { - return _keys.filter(function(k) { return _rules[k].autoHidden(); }); - } - return _rules[k] && _rules[k].autoHidden(); - }; - - - features.enable = function(k) { - if (_rules[k] && !_rules[k].enabled) { - _rules[k].enable(); - update(); - } - }; - - features.enableAll = function() { - var didEnable = false; - for (var k in _rules) { - if (!_rules[k].enabled) { - didEnable = true; - _rules[k].enable(); - } - } - if (didEnable) update(); - }; - - - features.disable = function(k) { - if (_rules[k] && _rules[k].enabled) { - _rules[k].disable(); - update(); - } - }; - - features.disableAll = function() { - var didDisable = false; - for (var k in _rules) { - if (_rules[k].enabled) { - didDisable = true; - _rules[k].disable(); - } - } - if (didDisable) update(); - }; - - - features.toggle = function(k) { - if (_rules[k]) { - (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k])); - update(); - } - }; - - - features.resetStats = function() { - for (var i = 0; i < _keys.length; i++) { - _rules[_keys[i]].count = 0; - } - dispatch$1.call('change'); - }; - - - features.gatherStats = function(d, resolver, dimensions) { - var needsRedraw = false; - var types = utilArrayGroupBy(d, 'type'); - var entities = [].concat(types.relation || [], types.way || [], types.node || []); - var currHidden, geometry, matches, i, j; - - for (i = 0; i < _keys.length; i++) { - _rules[_keys[i]].count = 0; - } - - // adjust the threshold for point/building culling based on viewport size.. - // a _cullFactor of 1 corresponds to a 1000x1000px viewport.. - _cullFactor = dimensions[0] * dimensions[1] / 1000000; - - for (i = 0; i < entities.length; i++) { - geometry = entities[i].geometry(resolver); - matches = Object.keys(features.getMatches(entities[i], resolver, geometry)); - for (j = 0; j < matches.length; j++) { - _rules[matches[j]].count++; - } - } - - currHidden = features.hidden(); - if (currHidden !== _hidden) { - _hidden = currHidden; - needsRedraw = true; - dispatch$1.call('change'); - } - - return needsRedraw; - }; - - - features.stats = function() { - for (var i = 0; i < _keys.length; i++) { - _stats[_keys[i]] = _rules[_keys[i]].count; - } - - return _stats; - }; - - - features.clear = function(d) { - for (var i = 0; i < d.length; i++) { - features.clearEntity(d[i]); - } - }; - - - features.clearEntity = function(entity) { - delete _cache[osmEntity.key(entity)]; - }; - - - features.reset = function() { - Array.from(_deferred).forEach(function(handle) { - window.cancelIdleCallback(handle); - _deferred.delete(handle); - }); - - _cache = {}; - }; - - // only certain relations are worth checking - function relationShouldBeChecked(relation) { - // multipolygon features have `area` geometry and aren't checked here - return relation.tags.type === 'boundary'; - } - - features.getMatches = function(entity, resolver, geometry) { - if (geometry === 'vertex' || - (geometry === 'relation' && !relationShouldBeChecked(entity))) return {}; - - var ent = osmEntity.key(entity); - if (!_cache[ent]) { - _cache[ent] = {}; - } - - if (!_cache[ent].matches) { - var matches = {}; - var hasMatch = false; - - for (var i = 0; i < _keys.length; i++) { - if (_keys[i] === 'others') { - if (hasMatch) continue; - - // If an entity... - // 1. is a way that hasn't matched other 'interesting' feature rules, - if (entity.type === 'way') { - var parents = features.getParents(entity, resolver, geometry); - - // 2a. belongs only to a single multipolygon relation - if ((parents.length === 1 && parents[0].isMultipolygon()) || - // 2b. or belongs only to boundary relations - (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) { - - // ...then match whatever feature rules the parent relation has matched. - // see #2548, #2887 - // - // IMPORTANT: - // For this to work, getMatches must be called on relations before ways. - // - var pkey = osmEntity.key(parents[0]); - if (_cache[pkey] && _cache[pkey].matches) { - matches = Object.assign({}, _cache[pkey].matches); // shallow copy - continue; - } - } - } - } - - if (_rules[_keys[i]].filter(entity.tags, geometry)) { - matches[_keys[i]] = hasMatch = true; - } - } - _cache[ent].matches = matches; - } - - return _cache[ent].matches; - }; - - - features.getParents = function(entity, resolver, geometry) { - if (geometry === 'point') return []; - - var ent = osmEntity.key(entity); - if (!_cache[ent]) { - _cache[ent] = {}; - } - - if (!_cache[ent].parents) { - var parents = []; - if (geometry === 'vertex') { - parents = resolver.parentWays(entity); - } else { // 'line', 'area', 'relation' - parents = resolver.parentRelations(entity); - } - _cache[ent].parents = parents; - } - return _cache[ent].parents; - }; - - - features.isHiddenPreset = function(preset, geometry) { - if (!_hidden.length) return false; - if (!preset.tags) return false; - - var test = preset.setTags({}, geometry); - for (var key in _rules) { - if (_rules[key].filter(test, geometry)) { - if (_hidden.indexOf(key) !== -1) { - return key; - } - return false; - } - } - return false; - }; - - - features.isHiddenFeature = function(entity, resolver, geometry) { - if (!_hidden.length) return false; - if (!entity.version) return false; - if (_forceVisible[entity.id]) return false; - - var matches = Object.keys(features.getMatches(entity, resolver, geometry)); - return matches.length && matches.every(function(k) { return features.hidden(k); }); - }; - - - features.isHiddenChild = function(entity, resolver, geometry) { - if (!_hidden.length) return false; - if (!entity.version || geometry === 'point') return false; - if (_forceVisible[entity.id]) return false; - - var parents = features.getParents(entity, resolver, geometry); - if (!parents.length) return false; - - for (var i = 0; i < parents.length; i++) { - if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) { - return false; - } - } - return true; - }; - - - features.hasHiddenConnections = function(entity, resolver) { - if (!_hidden.length) return false; - - var childNodes, connections; - if (entity.type === 'midpoint') { - childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])]; - connections = []; - } else { - childNodes = entity.nodes ? resolver.childNodes(entity) : []; - connections = features.getParents(entity, resolver, entity.geometry(resolver)); - } - - // gather ways connected to child nodes.. - connections = childNodes.reduce(function(result, e) { - return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result; - }, connections); - - return connections.some(function(e) { - return features.isHidden(e, resolver, e.geometry(resolver)); - }); - }; - - - features.isHidden = function(entity, resolver, geometry) { - if (!_hidden.length) return false; - if (!entity.version) return false; - - var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature); - return fn(entity, resolver, geometry); - }; - - - features.filter = function(d, resolver) { - if (!_hidden.length) return d; - - var result = []; - for (var i = 0; i < d.length; i++) { - var entity = d[i]; - if (!features.isHidden(entity, resolver, entity.geometry(resolver))) { - result.push(entity); - } - } - return result; - }; - - - features.forceVisible = function(entityIDs) { - if (!arguments.length) return Object.keys(_forceVisible); - - _forceVisible = {}; - for (var i = 0; i < entityIDs.length; i++) { - _forceVisible[entityIDs[i]] = true; - var entity = context.hasEntity(entityIDs[i]); - if (entity && entity.type === 'relation') { - // also show relation members (one level deep) - for (var j in entity.members) { - _forceVisible[entity.members[j].id] = true; - } - } - } - return features; - }; - - - features.init = function() { - var storage = corePreferences('disabled-features'); - if (storage) { - var storageDisabled = storage.replace(/;/g, ',').split(','); - storageDisabled.forEach(features.disable); - } - - var hash = utilStringQs(window.location.hash); - if (hash.disable_features) { - var hashDisabled = hash.disable_features.replace(/;/g, ',').split(','); - hashDisabled.forEach(features.disable); - } - }; - - - // warm up the feature matching cache upon merging fetched data - context.history().on('merge.features', function(newEntities) { - if (!newEntities) return; - var handle = window.requestIdleCallback(function() { - var graph = context.graph(); - var types = utilArrayGroupBy(newEntities, 'type'); - // ensure that getMatches is called on relations before ways - var entities = [].concat(types.relation || [], types.way || [], types.node || []); - for (var i = 0; i < entities.length; i++) { - var geometry = entities[i].geometry(graph); - features.getMatches(entities[i], graph, geometry); - } - }); - _deferred.add(handle); - }); - - - return features; - } - - // Touch targets control which other vertices we can drag a vertex onto. - // - // - the activeID - nope - // - 1 away (adjacent) to the activeID - yes (vertices will be merged) - // - 2 away from the activeID - nope (would create a self intersecting segment) - // - all others on a linear way - yes - // - all others on a closed way - nope (would create a self intersecting polygon) - // - // returns - // 0 = active vertex - no touch/connect - // 1 = passive vertex - yes touch/connect - // 2 = adjacent vertex - yes but pay attention segmenting a line here - // - function svgPassiveVertex(node, graph, activeID) { - if (!activeID) return 1; - if (activeID === node.id) return 0; - - var parents = graph.parentWays(node); - - var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max; - - for (i = 0; i < parents.length; i++) { - nodes = parents[i].nodes; - isClosed = parents[i].isClosed(); - for (j = 0; j < nodes.length; j++) { // find this vertex, look nearby - if (nodes[j] === node.id) { - ix1 = j - 2; - ix2 = j - 1; - ix3 = j + 1; - ix4 = j + 2; - - if (isClosed) { // wraparound if needed - max = nodes.length - 1; - if (ix1 < 0) ix1 = max + ix1; - if (ix2 < 0) ix2 = max + ix2; - if (ix3 > max) ix3 = ix3 - max; - if (ix4 > max) ix4 = ix4 - max; - } - - if (nodes[ix1] === activeID) return 0; // no - prevent self intersect - else if (nodes[ix2] === activeID) return 2; // ok - adjacent - else if (nodes[ix3] === activeID) return 2; // ok - adjacent - else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect - else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect - } - } - } - - return 1; // ok - } - - - function svgMarkerSegments(projection, graph, dt, - shouldReverse, - bothDirections) { - return function(entity) { - var i = 0; - var offset = dt; - var segments = []; - var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream; - var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; }); - var a, b; - - if (shouldReverse(entity)) { - coordinates.reverse(); - } - - d3_geoStream({ - type: 'LineString', - coordinates: coordinates - }, projection.stream(clip({ - lineStart: function() {}, - lineEnd: function() { a = null; }, - point: function(x, y) { - b = [x, y]; - - if (a) { - var span = geoVecLength(a, b) - offset; - - if (span >= 0) { - var heading = geoVecAngle(a, b); - var dx = dt * Math.cos(heading); - var dy = dt * Math.sin(heading); - var p = [ - a[0] + offset * Math.cos(heading), - a[1] + offset * Math.sin(heading) - ]; - - // gather coordinates - var coord = [a, p]; - for (span -= dt; span >= 0; span -= dt) { - p = geoVecAdd(p, [dx, dy]); - coord.push(p); - } - coord.push(b); - - // generate svg paths - var segment = ''; - var j; - - for (j = 0; j < coord.length; j++) { - segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; - } - segments.push({ id: entity.id, index: i++, d: segment }); - - if (bothDirections(entity)) { - segment = ''; - for (j = coord.length - 1; j >= 0; j--) { - segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1]; - } - segments.push({ id: entity.id, index: i++, d: segment }); - } - } - - offset = -span; - } - - a = b; - } - }))); - - return segments; - }; - } - - - function svgPath(projection, graph, isArea) { - - // Explanation of magic numbers: - // "padding" here allows space for strokes to extend beyond the viewport, - // so that the stroke isn't drawn along the edge of the viewport when - // the shape is clipped. - // - // When drawing lines, pad viewport by 5px. - // When drawing areas, pad viewport by 65px in each direction to allow - // for 60px area fill stroke (see ".fill-partial path.fill" css rule) - - var cache = {}; - var padding = isArea ? 65 : 5; - var viewport = projection.clipExtent(); - var paddedExtent = [ - [viewport[0][0] - padding, viewport[0][1] - padding], - [viewport[1][0] + padding, viewport[1][1] + padding] - ]; - var clip = d3_geoIdentity().clipExtent(paddedExtent).stream; - var project = projection.stream; - var path = d3_geoPath() - .projection({stream: function(output) { return project(clip(output)); }}); - - var svgpath = function(entity) { - if (entity.id in cache) { - return cache[entity.id]; - } else { - return cache[entity.id] = path(entity.asGeoJSON(graph)); - } - }; - - svgpath.geojson = function(d) { - if (d.__featurehash__ !== undefined) { - if (d.__featurehash__ in cache) { - return cache[d.__featurehash__]; - } else { - return cache[d.__featurehash__] = path(d); - } - } else { - return path(d); - } - }; - - return svgpath; - } - - - function svgPointTransform(projection) { - var svgpoint = function(entity) { - // http://jsperf.com/short-array-join - var pt = projection(entity.loc); - return 'translate(' + pt[0] + ',' + pt[1] + ')'; - }; - - svgpoint.geojson = function(d) { - return svgpoint(d.properties.entity); - }; - - return svgpoint; - } - - - function svgRelationMemberTags(graph) { - return function(entity) { - var tags = entity.tags; - var shouldCopyMultipolygonTags = !entity.hasInterestingTags(); - graph.parentRelations(entity).forEach(function(relation) { - var type = relation.tags.type; - if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') { - tags = Object.assign({}, relation.tags, tags); - } - }); - return tags; - }; - } - - - function svgSegmentWay(way, graph, activeID) { - // When there is no activeID, we can memoize this expensive computation - if (activeID === undefined) { - return graph.transient(way, 'waySegments', getWaySegments); - } else { - return getWaySegments(); - } - - function getWaySegments() { - var isActiveWay = (way.nodes.indexOf(activeID) !== -1); - var features = { passive: [], active: [] }; - var start = {}; - var end = {}; - var node, type; - - for (var i = 0; i < way.nodes.length; i++) { - node = graph.entity(way.nodes[i]); - type = svgPassiveVertex(node, graph, activeID); - end = { node: node, type: type }; - - if (start.type !== undefined) { - if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) { // one adjacent vertex - pushActive(start, end, i); - } else if (start.type === 0 && end.type === 0) { // both active vertices - pushActive(start, end, i); - } else { - pushPassive(start, end, i); - } - } - - start = end; - } - - return features; - - function pushActive(start, end, index) { - features.active.push({ - type: 'Feature', - id: way.id + '-' + index + '-nope', - properties: { - nope: true, - target: true, - entity: way, - nodes: [start.node, end.node], - index: index - }, - geometry: { - type: 'LineString', - coordinates: [start.node.loc, end.node.loc] - } - }); - } - - function pushPassive(start, end, index) { - features.passive.push({ - type: 'Feature', - id: way.id + '-' + index, - properties: { - target: true, - entity: way, - nodes: [start.node, end.node], - index: index - }, - geometry: { - type: 'LineString', - coordinates: [start.node.loc, end.node.loc] - } - }); - } - } - } - - function svgTagClasses() { - var primaries = [ - 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway', - 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse', - 'leisure', 'military', 'place', 'man_made', 'route', 'attraction', - 'building:part', 'indoor' - ]; - var statuses = [ - // nonexistent, might be built - 'proposed', 'planned', - // under maintentance or between groundbreaking and opening - 'construction', - // existent but not functional - 'disused', - // dilapidated to nonexistent - 'abandoned', - // nonexistent, still may appear in imagery - 'dismantled', 'razed', 'demolished', 'obliterated', - // existent occasionally, e.g. stormwater drainage basin - 'intermittent' - ]; - var secondaries = [ - 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', - 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', - 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', - 'man_made', 'indoor' - ]; - var _tags = function(entity) { return entity.tags; }; - - - var tagClasses = function(selection) { - selection.each(function tagClassesEach(entity) { - var value = this.className; - - if (value.baseVal !== undefined) { - value = value.baseVal; - } - - var t = _tags(entity); - - var computed = tagClasses.getClassesString(t, value); - - if (computed !== value) { - select(this).attr('class', computed); - } - }); - }; - - - tagClasses.getClassesString = function(t, value) { - var primary, status; - var i, j, k, v; - - // in some situations we want to render perimeter strokes a certain way - var overrideGeometry; - if (/\bstroke\b/.test(value)) { - if (!!t.barrier && t.barrier !== 'no') { - overrideGeometry = 'line'; - } - } - - // preserve base classes (nothing with `tag-`) - var classes = value.trim().split(/\s+/) - .filter(function(klass) { - return klass.length && !/^tag-/.test(klass); - }) - .map(function(klass) { // special overrides for some perimeter strokes - return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass; - }); - - // pick at most one primary classification tag.. - for (i = 0; i < primaries.length; i++) { - k = primaries[i]; - v = t[k]; - if (!v || v === 'no') continue; - - if (k === 'piste:type') { // avoid a ':' in the class name - k = 'piste'; - } else if (k === 'building:part') { // avoid a ':' in the class name - k = 'building_part'; - } - - primary = k; - if (statuses.indexOf(v) !== -1) { // e.g. `railway=abandoned` - status = v; - classes.push('tag-' + k); - } else { - classes.push('tag-' + k); - classes.push('tag-' + k + '-' + v); - } - - break; - } - - if (!primary) { - for (i = 0; i < statuses.length; i++) { - for (j = 0; j < primaries.length; j++) { - k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes` - v = t[k]; - if (!v || v === 'no') continue; - - status = statuses[i]; - break; - } - } - } - - // add at most one status tag, only if relates to primary tag.. - if (!status) { - for (i = 0; i < statuses.length; i++) { - k = statuses[i]; - v = t[k]; - if (!v || v === 'no') continue; - - if (v === 'yes') { // e.g. `railway=rail + abandoned=yes` - status = k; - } - else if (primary && primary === v) { // e.g. `railway=rail + abandoned=railway` - status = k; - } else if (!primary && primaries.indexOf(v) !== -1) { // e.g. `abandoned=railway` - status = k; - primary = v; - classes.push('tag-' + v); - } // else ignore e.g. `highway=path + abandoned=railway` - - if (status) break; - } - } - - if (status) { - classes.push('tag-status'); - classes.push('tag-status-' + status); - } - - // add any secondary tags - for (i = 0; i < secondaries.length; i++) { - k = secondaries[i]; - v = t[k]; - if (!v || v === 'no' || k === primary) continue; - classes.push('tag-' + k); - classes.push('tag-' + k + '-' + v); - } - - // For highways, look for surface tagging.. - if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') { - var surface = t.highway === 'track' ? 'unpaved' : 'paved'; - for (k in t) { - v = t[k]; - if (k in osmPavedTags) { - surface = osmPavedTags[k][v] ? 'paved' : 'unpaved'; - } - if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) { - surface = 'semipaved'; - } - } - classes.push('tag-' + surface); - } - - // If this is a wikidata-tagged item, add a class for that.. - if (t.wikidata || t['brand:wikidata']) { - classes.push('tag-wikidata'); - } - - return classes.join(' ').trim(); - }; - - - tagClasses.tags = function(val) { - if (!arguments.length) return _tags; - _tags = val; - return tagClasses; - }; - - return tagClasses; - } - - // Patterns only work in Firefox when set directly on element. - // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632) - var patterns = { - // tag - pattern name - // -or- - // tag - value - pattern name - // -or- - // tag - value - rules (optional tag-values, pattern name) - // (matches earlier rules first, so fallback should be last entry) - amenity: { - grave_yard: 'cemetery', - fountain: 'water_standing' - }, - landuse: { - cemetery: [ - { religion: 'christian', pattern: 'cemetery_christian' }, - { religion: 'buddhist', pattern: 'cemetery_buddhist' }, - { religion: 'muslim', pattern: 'cemetery_muslim' }, - { religion: 'jewish', pattern: 'cemetery_jewish' }, - { pattern: 'cemetery' } - ], - construction: 'construction', - farmland: 'farmland', - farmyard: 'farmyard', - forest: [ - { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' }, - { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' }, - { leaf_type: 'leafless', pattern: 'forest_leafless' }, - { pattern: 'forest' } // same as 'leaf_type:mixed' - ], - grave_yard: 'cemetery', - grass: [ - { golf: 'green', pattern: 'golf_green' }, - { pattern: 'grass' }, - ], - landfill: 'landfill', - meadow: 'meadow', - military: 'construction', - orchard: 'orchard', - quarry: 'quarry', - vineyard: 'vineyard' - }, - natural: { - beach: 'beach', - grassland: 'grass', - sand: 'beach', - scrub: 'scrub', - water: [ - { water: 'pond', pattern: 'pond' }, - { water: 'reservoir', pattern: 'water_standing' }, - { pattern: 'waves' } - ], - wetland: [ - { wetland: 'marsh', pattern: 'wetland_marsh' }, - { wetland: 'swamp', pattern: 'wetland_swamp' }, - { wetland: 'bog', pattern: 'wetland_bog' }, - { wetland: 'reedbed', pattern: 'wetland_reedbed' }, - { pattern: 'wetland' } - ], - wood: [ - { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' }, - { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' }, - { leaf_type: 'leafless', pattern: 'forest_leafless' }, - { pattern: 'forest' } // same as 'leaf_type:mixed' - ] - }, - traffic_calming: { - island: [ - { surface: 'grass', pattern: 'grass' }, - ], - chicane: [ - { surface: 'grass', pattern: 'grass' }, - ], - choker: [ - { surface: 'grass', pattern: 'grass' }, - ] - } - }; - - function svgTagPattern(tags) { - // Skip pattern filling if this is a building (buildings don't get patterns applied) - if (tags.building && tags.building !== 'no') { - return null; - } - - for (var tag in patterns) { - var entityValue = tags[tag]; - if (!entityValue) continue; - - if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name - return 'pattern-' + patterns[tag]; - } else { - var values = patterns[tag]; - for (var value in values) { - if (entityValue !== value) continue; - - var rules = values[value]; - if (typeof rules === 'string') { // short syntax - pattern name - return 'pattern-' + rules; - } - - // long syntax - rule array - for (var ruleKey in rules) { - var rule = rules[ruleKey]; - - var pass = true; - for (var criterion in rule) { - if (criterion !== 'pattern') { // reserved for pattern name - // The only rule is a required tag-value pair - var v = tags[criterion]; - if (!v || v !== rule[criterion]) { - pass = false; - break; - } - } - } - - if (pass) { - return 'pattern-' + rule.pattern; - } - } - } - } - } - - return null; - } - - function svgAreas(projection, context) { - - - function getPatternStyle(tags) { - var imageID = svgTagPattern(tags); - if (imageID) { - return 'url("#ideditor-' + imageID + '")'; - } - return ''; - } - - - function drawTargets(selection, graph, entities, filter) { - var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; - var getPath = svgPath(projection).geojson; - var activeID = context.activeID(); - var base = context.history().base(); - - // The targets and nopes will be MultiLineString sub-segments of the ways - var data = { targets: [], nopes: [] }; - - entities.forEach(function(way) { - var features = svgSegmentWay(way, graph, activeID); - data.targets.push.apply(data.targets, features.passive); - data.nopes.push.apply(data.nopes, features.active); - }); - - - // Targets allow hover and vertex snapping - var targetData = data.targets.filter(getPath); - var targets = selection.selectAll('.area.target-allowed') - .filter(function(d) { return filter(d.properties.entity); }) - .data(targetData, function key(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - var segmentWasEdited = function(d) { - var wayID = d.properties.entity.id; - // if the whole line was edited, don't draw segment changes - if (!base.entities[wayID] || - !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) { - return false; - } - return d.properties.nodes.some(function(n) { - return !base.entities[n.id] || - !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc); - }); - }; - - // enter/update - targets.enter() - .append('path') - .merge(targets) - .attr('d', getPath) - .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; }) - .classed('segment-edited', segmentWasEdited); - - - // NOPE - var nopeData = data.nopes.filter(getPath); - var nopes = selection.selectAll('.area.target-nope') - .filter(function(d) { return filter(d.properties.entity); }) - .data(nopeData, function key(d) { return d.id; }); - - // exit - nopes.exit() - .remove(); - - // enter/update - nopes.enter() - .append('path') - .merge(nopes) - .attr('d', getPath) - .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; }) - .classed('segment-edited', segmentWasEdited); - } - - - function drawAreas(selection, graph, entities, filter) { - var path = svgPath(projection, graph, true); - var areas = {}; - var multipolygon; - var base = context.history().base(); - - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - if (entity.geometry(graph) !== 'area') continue; - - multipolygon = osmIsOldMultipolygonOuterMember(entity, graph); - if (multipolygon) { - areas[multipolygon.id] = { - entity: multipolygon.mergeTags(entity.tags), - area: Math.abs(entity.area(graph)) - }; - } else if (!areas[entity.id]) { - areas[entity.id] = { - entity: entity, - area: Math.abs(entity.area(graph)) - }; - } - } - - var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); }); - fills.sort(function areaSort(a, b) { return b.area - a.area; }); - fills = fills.map(function(a) { return a.entity; }); - - var strokes = fills.filter(function(area) { return area.type === 'way'; }); - - var data = { - clip: fills, - shadow: strokes, - stroke: strokes, - fill: fills - }; - - var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm') - .filter(filter) - .data(data.clip, osmEntity.key); - - clipPaths.exit() - .remove(); - - var clipPathsEnter = clipPaths.enter() - .append('clipPath') - .attr('class', 'clipPath-osm') - .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; }); - - clipPathsEnter - .append('path'); - - clipPaths.merge(clipPathsEnter) - .selectAll('path') - .attr('d', path); - - - var drawLayer = selection.selectAll('.layer-osm.areas'); - var touchLayer = selection.selectAll('.layer-touch.areas'); - - // Draw areas.. - var areagroup = drawLayer - .selectAll('g.areagroup') - .data(['fill', 'shadow', 'stroke']); - - areagroup = areagroup.enter() - .append('g') - .attr('class', function(d) { return 'areagroup area-' + d; }) - .merge(areagroup); - - var paths = areagroup - .selectAll('path') - .filter(filter) - .data(function(layer) { return data[layer]; }, osmEntity.key); - - paths.exit() - .remove(); - - - var fillpaths = selection.selectAll('.area-fill path.area').nodes(); - var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left; - - function sortedByArea(entity) { - if (this._parent.__data__ === 'fill') { - return fillpaths[bisect(fillpaths, -entity.area(graph))]; - } - } - - paths = paths.enter() - .insert('path', sortedByArea) - .merge(paths) - .each(function(entity) { - var layer = this.parentNode.__data__; - this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id); - - if (layer === 'fill') { - this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)'); - this.style.fill = this.style.stroke = getPatternStyle(entity.tags); - } - }) - .classed('added', function(d) { - return !base.entities[d.id]; - }) - .classed('geometry-edited', function(d) { - return graph.entities[d.id] && - base.entities[d.id] && - !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes); - }) - .classed('retagged', function(d) { - return graph.entities[d.id] && - base.entities[d.id] && - !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); - }) - .call(svgTagClasses()) - .attr('d', path); - - - // Draw touch targets.. - touchLayer - .call(drawTargets, graph, data.stroke, filter); - } - - return drawAreas; - } - - //[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] - //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] - //[5] Name ::= NameStartChar (NameChar)* - var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/;//\u10000-\uEFFFF - var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); - var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); - //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ - //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') - - //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE - //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE - var S_TAG = 0;//tag name offerring - var S_ATTR = 1;//attr name offerring - var S_ATTR_SPACE=2;//attr name end and space offer - var S_EQ = 3;//=space? - var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) - var S_ATTR_END = 5;//attr value end and no space(quot end) - var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) - var S_TAG_CLOSE = 7;//closed el - - function XMLReader(){ - - } - - XMLReader.prototype = { - parse:function(source,defaultNSMap,entityMap){ - var domBuilder = this.domBuilder; - domBuilder.startDocument(); - _copy(defaultNSMap ,defaultNSMap = {}); - parse(source,defaultNSMap,entityMap, - domBuilder,this.errorHandler); - domBuilder.endDocument(); - } - }; - function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ - function fixedFromCharCode(code) { - // String.prototype.fromCharCode does not supports - // > 2 bytes unicode chars directly - if (code > 0xffff) { - code -= 0x10000; - var surrogate1 = 0xd800 + (code >> 10) - , surrogate2 = 0xdc00 + (code & 0x3ff); - - return String.fromCharCode(surrogate1, surrogate2); - } else { - return String.fromCharCode(code); - } - } - function entityReplacer(a){ - var k = a.slice(1,-1); - if(k in entityMap){ - return entityMap[k]; - }else if(k.charAt(0) === '#'){ - return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) - }else { - errorHandler.error('entity not found:'+a); - return a; - } - } - function appendText(end){//has some bugs - if(end>start){ - var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); - locator&&position(start); - domBuilder.characters(xt,0,end-start); - start = end; - } - } - function position(p,m){ - while(p>=lineEnd && (m = linePattern.exec(source))){ - lineStart = m.index; - lineEnd = lineStart + m[0].length; - locator.lineNumber++; - //console.log('line++:',locator,startPos,endPos) - } - locator.columnNumber = p-lineStart+1; - } - var lineStart = 0; - var lineEnd = 0; - var linePattern = /.*(?:\r\n?|\n)|.*$/g; - var locator = domBuilder.locator; - - var parseStack = [{currentNSMap:defaultNSMapCopy}]; - var closeMap = {}; - var start = 0; - while(true){ - try{ - var tagStart = source.indexOf('<',start); - if(tagStart<0){ - if(!source.substr(start).match(/^\s*$/)){ - var doc = domBuilder.doc; - var text = doc.createTextNode(source.substr(start)); - doc.appendChild(text); - domBuilder.currentElement = text; - } - return; - } - if(tagStart>start){ - appendText(tagStart); - } - switch(source.charAt(tagStart+1)){ - case '/': - var end = source.indexOf('>',tagStart+3); - var tagName = source.substring(tagStart+2,end); - var config = parseStack.pop(); - if(end<0){ - - tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); - //console.error('#@@@@@@'+tagName) - errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); - end = tagStart+1+tagName.length; - }else if(tagName.match(/\s - locator&&position(tagStart); - end = parseInstruction(source,tagStart,domBuilder); - break; - case '!':// start){ - start = end; - }else { - //TODO: 这里有可能sax回退,有位置错误风险 - appendText(Math.max(tagStart,start)+1); - } - } - } - function copyLocator(f,t){ - t.lineNumber = f.lineNumber; - t.columnNumber = f.columnNumber; - return t; - } - - /** - * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); - * @return end of the elementStartPart(end of elementEndPart for selfClosed el) - */ - function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ - var attrName; - var value; - var p = ++start; - var s = S_TAG;//status - while(true){ - var c = source.charAt(p); - switch(c){ - case '=': - if(s === S_ATTR){//attrName - attrName = source.slice(start,p); - s = S_EQ; - }else if(s === S_ATTR_SPACE){ - s = S_EQ; - }else { - //fatalError: equal must after attrName or space after attrName - throw new Error('attribute equal must after attrName'); - } - break; - case '\'': - case '"': - if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE - ){//equal - if(s === S_ATTR){ - errorHandler.warning('attribute value must after "="'); - attrName = source.slice(start,p); - } - start = p+1; - p = source.indexOf(c,start); - if(p>0){ - value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); - el.add(attrName,value,start-1); - s = S_ATTR_END; - }else { - //fatalError: no end quot match - throw new Error('attribute value no end \''+c+'\' match'); - } - }else if(s == S_ATTR_NOQUOT_VALUE){ - value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); - //console.log(attrName,value,start,p) - el.add(attrName,value,start); - //console.dir(el) - errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); - start = p+1; - s = S_ATTR_END; - }else { - //fatalError: no equal before - throw new Error('attribute value must after "="'); - } - break; - case '/': - switch(s){ - case S_TAG: - el.setTagName(source.slice(start,p)); - case S_ATTR_END: - case S_TAG_SPACE: - case S_TAG_CLOSE: - s =S_TAG_CLOSE; - el.closed = true; - case S_ATTR_NOQUOT_VALUE: - case S_ATTR: - case S_ATTR_SPACE: - break; - //case S_EQ: - default: - throw new Error("attribute invalid close char('/')") - } - break; - case ''://end document - //throw new Error('unexpected end of input') - errorHandler.error('unexpected end of input'); - if(s == S_TAG){ - el.setTagName(source.slice(start,p)); - } - return p; - case '>': - switch(s){ - case S_TAG: - el.setTagName(source.slice(start,p)); - case S_ATTR_END: - case S_TAG_SPACE: - case S_TAG_CLOSE: - break;//normal - case S_ATTR_NOQUOT_VALUE://Compatible state - case S_ATTR: - value = source.slice(start,p); - if(value.slice(-1) === '/'){ - el.closed = true; - value = value.slice(0,-1); - } - case S_ATTR_SPACE: - if(s === S_ATTR_SPACE){ - value = attrName; - } - if(s == S_ATTR_NOQUOT_VALUE){ - errorHandler.warning('attribute "'+value+'" missed quot(")!!'); - el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start); - }else { - if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){ - errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!'); - } - el.add(value,value,start); - } - break; - case S_EQ: - throw new Error('attribute value missed!!'); - } - // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) - return p; - /*xml space '\x20' | #x9 | #xD | #xA; */ - case '\u0080': - c = ' '; - default: - if(c<= ' '){//space - switch(s){ - case S_TAG: - el.setTagName(source.slice(start,p));//tagName - s = S_TAG_SPACE; - break; - case S_ATTR: - attrName = source.slice(start,p); - s = S_ATTR_SPACE; - break; - case S_ATTR_NOQUOT_VALUE: - var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); - errorHandler.warning('attribute "'+value+'" missed quot(")!!'); - el.add(attrName,value,start); - case S_ATTR_END: - s = S_TAG_SPACE; - break; - //case S_TAG_SPACE: - //case S_EQ: - //case S_ATTR_SPACE: - // void();break; - //case S_TAG_CLOSE: - //ignore warning - } - }else {//not space - //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE - //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE - switch(s){ - //case S_TAG:void();break; - //case S_ATTR:void();break; - //case S_ATTR_NOQUOT_VALUE:void();break; - case S_ATTR_SPACE: - var tagName = el.tagName; - if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ - errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!'); - } - el.add(attrName,attrName,start); - start = p; - s = S_ATTR; - break; - case S_ATTR_END: - errorHandler.warning('attribute space is required"'+attrName+'"!!'); - case S_TAG_SPACE: - s = S_ATTR; - start = p; - break; - case S_EQ: - s = S_ATTR_NOQUOT_VALUE; - start = p; - break; - case S_TAG_CLOSE: - throw new Error("elements closed character '/' and '>' must be connected to"); - } - } - }//end outer switch - //console.log('p++',p) - p++; - } - } - /** - * @return true if has new namespace define - */ - function appendElement(el,domBuilder,currentNSMap){ - var tagName = el.tagName; - var localNSMap = null; - //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; - var i = el.length; - while(i--){ - var a = el[i]; - var qName = a.qName; - var value = a.value; - var nsp = qName.indexOf(':'); - if(nsp>0){ - var prefix = a.prefix = qName.slice(0,nsp); - var localName = qName.slice(nsp+1); - var nsPrefix = prefix === 'xmlns' && localName; - }else { - localName = qName; - prefix = null; - nsPrefix = qName === 'xmlns' && ''; - } - //can not set prefix,because prefix !== '' - a.localName = localName ; - //prefix == null for no ns prefix attribute - if(nsPrefix !== false){//hack!! - if(localNSMap == null){ - localNSMap = {}; - //console.log(currentNSMap,0) - _copy(currentNSMap,currentNSMap={}); - //console.log(currentNSMap,1) - } - currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; - a.uri = 'http://www.w3.org/2000/xmlns/'; - domBuilder.startPrefixMapping(nsPrefix, value); - } - } - var i = el.length; - while(i--){ - a = el[i]; - var prefix = a.prefix; - if(prefix){//no prefix attribute has no namespace - if(prefix === 'xml'){ - a.uri = 'http://www.w3.org/XML/1998/namespace'; - }if(prefix !== 'xmlns'){ - a.uri = currentNSMap[prefix || '']; - - //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} - } - } - } - var nsp = tagName.indexOf(':'); - if(nsp>0){ - prefix = el.prefix = tagName.slice(0,nsp); - localName = el.localName = tagName.slice(nsp+1); - }else { - prefix = null;//important!! - localName = el.localName = tagName; - } - //no prefix element has default namespace - var ns = el.uri = currentNSMap[prefix || '']; - domBuilder.startElement(ns,localName,tagName,el); - //endPrefixMapping and startPrefixMapping have not any help for dom builder - //localNSMap = null - if(el.closed){ - domBuilder.endElement(ns,localName,tagName); - if(localNSMap){ - for(prefix in localNSMap){ - domBuilder.endPrefixMapping(prefix); - } - } - }else { - el.currentNSMap = currentNSMap; - el.localNSMap = localNSMap; - //parseStack.push(el); - return true; - } - } - function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ - if(/^(?:script|textarea)$/i.test(tagName)){ - var elEndStart = source.indexOf('',elStartEnd); - var text = source.substring(elStartEnd+1,elEndStart); - if(/[&<]/.test(text)){ - if(/^script$/i.test(tagName)){ - //if(!/\]\]>/.test(text)){ - //lexHandler.startCDATA(); - domBuilder.characters(text,0,text.length); - //lexHandler.endCDATA(); - return elEndStart; - //} - }//}else{//text area - text = text.replace(/&#?\w+;/g,entityReplacer); - domBuilder.characters(text,0,text.length); - return elEndStart; - //} - - } - } - return elStartEnd+1; - } - function fixSelfClosed(source,elStartEnd,tagName,closeMap){ - //if(tagName in closeMap){ - var pos = closeMap[tagName]; - if(pos == null){ - //console.log(tagName) - pos = source.lastIndexOf(''); - if(pos',start+4); - //append comment source.substring(4,end)//"); - case DOCUMENT_TYPE_NODE: - var pubid = node.publicId; - var sysid = node.systemId; - buf.push(''); - }else if(sysid && sysid!='.'){ - buf.push(' SYSTEM "',sysid,'">'); - }else { - var sub = node.internalSubset; - if(sub){ - buf.push(" [",sub,"]"); - } - buf.push(">"); - } - return; - case PROCESSING_INSTRUCTION_NODE: - return buf.push( ""); - case ENTITY_REFERENCE_NODE: - return buf.push( '&',node.nodeName,';'); - //case ENTITY_NODE: - //case NOTATION_NODE: - default: - buf.push('??',node.nodeName); - } - } - function importNode(doc,node,deep){ - var node2; - switch (node.nodeType) { - case ELEMENT_NODE: - node2 = node.cloneNode(false); - node2.ownerDocument = doc; - //var attrs = node2.attributes; - //var len = attrs.length; - //for(var i=0;i','amp':'&','quot':'"','apos':"'"}; - if(locator){ - domBuilder.setDocumentLocator(locator); - } - - sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); - sax.domBuilder = options.domBuilder || domBuilder; - if(/\/x?html?$/.test(mimeType)){ - entityMap.nbsp = '\xa0'; - entityMap.copy = '\xa9'; - defaultNSMap['']= 'http://www.w3.org/1999/xhtml'; - } - defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; - if(source){ - sax.parse(source,defaultNSMap,entityMap); - }else { - sax.errorHandler.error("invalid doc source"); - } - return domBuilder.doc; - }; - function buildErrorHandler(errorImpl,domBuilder,locator){ - if(!errorImpl){ - if(domBuilder instanceof DOMHandler){ - return domBuilder; - } - errorImpl = domBuilder ; - } - var errorHandler = {}; - var isCallback = errorImpl instanceof Function; - locator = locator||{}; - function build(key){ - var fn = errorImpl[key]; - if(!fn && isCallback){ - fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl; - } - errorHandler[key] = fn && function(msg){ - fn('[xmldom '+key+']\t'+msg+_locator(locator)); - }||function(){}; - } - build('warning'); - build('error'); - build('fatalError'); - return errorHandler; - } - - //console.log('#\n\n\n\n\n\n\n####') - /** - * +ContentHandler+ErrorHandler - * +LexicalHandler+EntityResolver2 - * -DeclHandler-DTDHandler - * - * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler - * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 - * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html - */ - function DOMHandler() { - this.cdata = false; - } - function position(locator,node){ - node.lineNumber = locator.lineNumber; - node.columnNumber = locator.columnNumber; - } - /** - * @see org.xml.sax.ContentHandler#startDocument - * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html - */ - DOMHandler.prototype = { - startDocument : function() { - this.doc = new DOMImplementation().createDocument(null, null, null); - if (this.locator) { - this.doc.documentURI = this.locator.systemId; - } - }, - startElement:function(namespaceURI, localName, qName, attrs) { - var doc = this.doc; - var el = doc.createElementNS(namespaceURI, qName||localName); - var len = attrs.length; - appendElement(this, el); - this.currentElement = el; - - this.locator && position(this.locator,el); - for (var i = 0 ; i < len; i++) { - var namespaceURI = attrs.getURI(i); - var value = attrs.getValue(i); - var qName = attrs.getQName(i); - var attr = doc.createAttributeNS(namespaceURI, qName); - this.locator &&position(attrs.getLocator(i),attr); - attr.value = attr.nodeValue = value; - el.setAttributeNode(attr); - } - }, - endElement:function(namespaceURI, localName, qName) { - var current = this.currentElement; - var tagName = current.tagName; - this.currentElement = current.parentNode; - }, - startPrefixMapping:function(prefix, uri) { - }, - endPrefixMapping:function(prefix) { - }, - processingInstruction:function(target, data) { - var ins = this.doc.createProcessingInstruction(target, data); - this.locator && position(this.locator,ins); - appendElement(this, ins); - }, - ignorableWhitespace:function(ch, start, length) { - }, - characters:function(chars, start, length) { - chars = _toString.apply(this,arguments); - //console.log(chars) - if(chars){ - if (this.cdata) { - var charNode = this.doc.createCDATASection(chars); - } else { - var charNode = this.doc.createTextNode(chars); - } - if(this.currentElement){ - this.currentElement.appendChild(charNode); - }else if(/^\s*$/.test(chars)){ - this.doc.appendChild(charNode); - //process xml - } - this.locator && position(this.locator,charNode); - } - }, - skippedEntity:function(name) { - }, - endDocument:function() { - this.doc.normalize(); - }, - setDocumentLocator:function (locator) { - if(this.locator = locator){// && !('lineNumber' in locator)){ - locator.lineNumber = 0; - } - }, - //LexicalHandler - comment:function(chars, start, length) { - chars = _toString.apply(this,arguments); - var comm = this.doc.createComment(chars); - this.locator && position(this.locator,comm); - appendElement(this, comm); - }, - - startCDATA:function() { - //used in characters() methods - this.cdata = true; - }, - endCDATA:function() { - this.cdata = false; - }, - - startDTD:function(name, publicId, systemId) { - var impl = this.doc.implementation; - if (impl && impl.createDocumentType) { - var dt = impl.createDocumentType(name, publicId, systemId); - this.locator && position(this.locator,dt); - appendElement(this, dt); - } - }, - /** - * @see org.xml.sax.ErrorHandler - * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html - */ - warning:function(error) { - console.warn('[xmldom warning]\t'+error,_locator(this.locator)); - }, - error:function(error) { - console.error('[xmldom error]\t'+error,_locator(this.locator)); - }, - fatalError:function(error) { - console.error('[xmldom fatalError]\t'+error,_locator(this.locator)); - throw error; - } - }; - function _locator(l){ - if(l){ - return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' - } - } - function _toString(chars,start,length){ - if(typeof chars == 'string'){ - return chars.substr(start,length) - }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") - if(chars.length >= start+length || start){ - return new java.lang.String(chars,start,length)+''; - } - return chars; - } - } - - /* - * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html - * used method of org.xml.sax.ext.LexicalHandler: - * #comment(chars, start, length) - * #startCDATA() - * #endCDATA() - * #startDTD(name, publicId, systemId) - * - * - * IGNORED method of org.xml.sax.ext.LexicalHandler: - * #endDTD() - * #startEntity(name) - * #endEntity(name) - * - * - * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html - * IGNORED method of org.xml.sax.ext.DeclHandler - * #attributeDecl(eName, aName, type, mode, value) - * #elementDecl(name, model) - * #externalEntityDecl(name, publicId, systemId) - * #internalEntityDecl(name, value) - * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html - * IGNORED method of org.xml.sax.EntityResolver2 - * #resolveEntity(String name,String publicId,String baseURI,String systemId) - * #resolveEntity(publicId, systemId) - * #getExternalSubset(name, baseURI) - * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html - * IGNORED method of org.xml.sax.DTDHandler - * #notationDecl(name, publicId, systemId) {}; - * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; - */ - "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ - DOMHandler.prototype[key] = function(){return null}; - }); - - /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ - function appendElement (hander,node) { - if (!hander.currentElement) { - hander.doc.appendChild(node); - } else { - hander.currentElement.appendChild(node); - } - }//appendChild and setAttributeNS are preformance key - - //if(typeof require == 'function'){ - var XMLReader = sax.XMLReader; - var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation; - exports.XMLSerializer = dom.XMLSerializer ; - exports.DOMParser = DOMParser; - //} - }); - - var togeojson = createCommonjsModule(function (module, exports) { - var toGeoJSON = (function() { - - var removeSpace = /\s*/g, - trimSpace = /^\s*|\s*$/g, - splitSpace = /\s+/; - // generate a short, numeric hash of a string - function okhash(x) { - if (!x || !x.length) return 0; - for (var i = 0, h = 0; i < x.length; i++) { - h = ((h << 5) - h) + x.charCodeAt(i) | 0; - } return h; - } - // all Y children of X - function get(x, y) { return x.getElementsByTagName(y); } - function attr(x, y) { return x.getAttribute(y); } - function attrf(x, y) { return parseFloat(attr(x, y)); } - // one Y child of X, if any, otherwise null - function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; } - // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize - function norm(el) { if (el.normalize) { el.normalize(); } return el; } - // cast array x into numbers - function numarray(x) { - for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); } - return o; - } - // get the content of a text node, if any - function nodeVal(x) { - if (x) { norm(x); } - return (x && x.textContent) || ''; - } - // get the contents of multiple text nodes, if present - function getMulti(x, ys) { - var o = {}, n, k; - for (k = 0; k < ys.length; k++) { - n = get1(x, ys[k]); - if (n) o[ys[k]] = nodeVal(n); - } - return o; - } - // add properties of Y to X, overwriting if present in both - function extend(x, y) { for (var k in y) x[k] = y[k]; } - // get one coordinate from a coordinate array, if any - function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); } - // get all coordinates from a coordinate array as [[],[]] - function coord(v) { - var coords = v.replace(trimSpace, '').split(splitSpace), - o = []; - for (var i = 0; i < coords.length; i++) { - o.push(coord1(coords[i])); - } - return o; - } - function coordPair(x) { - var ll = [attrf(x, 'lon'), attrf(x, 'lat')], - ele = get1(x, 'ele'), - // handle namespaced attribute in browser - heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'), - time = get1(x, 'time'), - e; - if (ele) { - e = parseFloat(nodeVal(ele)); - if (!isNaN(e)) { - ll.push(e); - } - } - return { - coordinates: ll, - time: time ? nodeVal(time) : null, - heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null - }; - } - - // create a new feature collection parent object - function fc() { - return { - type: 'FeatureCollection', - features: [] - }; - } - - var serializer; - if (typeof XMLSerializer !== 'undefined') { - /* istanbul ignore next */ - serializer = new XMLSerializer(); - // only require xmldom in a node environment - } else if ( typeof process === 'object' && !process.browser) { - serializer = new (domParser.XMLSerializer)(); - } - function xml2str(str) { - // IE9 will create a new XMLSerializer but it'll crash immediately. - // This line is ignored because we don't run coverage tests in IE9 - /* istanbul ignore next */ - if (str.xml !== undefined) return str.xml; - return serializer.serializeToString(str); - } - - var t = { - kml: function(doc) { - - var gj = fc(), - // styleindex keeps track of hashed styles in order to match features - styleIndex = {}, styleByHash = {}, - // stylemapindex keeps track of style maps to expose in properties - styleMapIndex = {}, - // atomic geospatial types supported by KML - MultiGeometry is - // handled separately - geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'], - // all root placemarks in the file - placemarks = get(doc, 'Placemark'), - styles = get(doc, 'Style'), - styleMaps = get(doc, 'StyleMap'); - - for (var k = 0; k < styles.length; k++) { - var hash = okhash(xml2str(styles[k])).toString(16); - styleIndex['#' + attr(styles[k], 'id')] = hash; - styleByHash[hash] = styles[k]; - } - for (var l = 0; l < styleMaps.length; l++) { - styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16); - var pairs = get(styleMaps[l], 'Pair'); - var pairsMap = {}; - for (var m = 0; m < pairs.length; m++) { - pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl')); - } - styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap; - - } - for (var j = 0; j < placemarks.length; j++) { - gj.features = gj.features.concat(getPlacemark(placemarks[j])); - } - function kmlColor(v) { - var color, opacity; - v = v || ''; - if (v.substr(0, 1) === '#') { v = v.substr(1); } - if (v.length === 6 || v.length === 3) { color = v; } - if (v.length === 8) { - opacity = parseInt(v.substr(0, 2), 16) / 255; - color = '#' + v.substr(6, 2) + - v.substr(4, 2) + - v.substr(2, 2); - } - return [color, isNaN(opacity) ? undefined : opacity]; - } - function gxCoord(v) { return numarray(v.split(' ')); } - function gxCoords(root) { - var elems = get(root, 'coord'), coords = [], times = []; - if (elems.length === 0) elems = get(root, 'gx:coord'); - for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i]))); - var timeElems = get(root, 'when'); - for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j])); - return { - coords: coords, - times: times - }; - } - function getGeometry(root) { - var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = []; - if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); } - if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); } - if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); } - for (i = 0; i < geotypes.length; i++) { - geomNodes = get(root, geotypes[i]); - if (geomNodes) { - for (j = 0; j < geomNodes.length; j++) { - geomNode = geomNodes[j]; - if (geotypes[i] === 'Point') { - geoms.push({ - type: 'Point', - coordinates: coord1(nodeVal(get1(geomNode, 'coordinates'))) - }); - } else if (geotypes[i] === 'LineString') { - geoms.push({ - type: 'LineString', - coordinates: coord(nodeVal(get1(geomNode, 'coordinates'))) - }); - } else if (geotypes[i] === 'Polygon') { - var rings = get(geomNode, 'LinearRing'), - coords = []; - for (k = 0; k < rings.length; k++) { - coords.push(coord(nodeVal(get1(rings[k], 'coordinates')))); - } - geoms.push({ - type: 'Polygon', - coordinates: coords - }); - } else if (geotypes[i] === 'Track' || - geotypes[i] === 'gx:Track') { - var track = gxCoords(geomNode); - geoms.push({ - type: 'LineString', - coordinates: track.coords - }); - if (track.times.length) coordTimes.push(track.times); - } - } - } - } - return { - geoms: geoms, - coordTimes: coordTimes - }; - } - function getPlacemark(root) { - var geomsAndTimes = getGeometry(root), i, properties = {}, - name = nodeVal(get1(root, 'name')), - address = nodeVal(get1(root, 'address')), - styleUrl = nodeVal(get1(root, 'styleUrl')), - description = nodeVal(get1(root, 'description')), - timeSpan = get1(root, 'TimeSpan'), - timeStamp = get1(root, 'TimeStamp'), - extendedData = get1(root, 'ExtendedData'), - lineStyle = get1(root, 'LineStyle'), - polyStyle = get1(root, 'PolyStyle'), - visibility = get1(root, 'visibility'); - - if (!geomsAndTimes.geoms.length) return []; - if (name) properties.name = name; - if (address) properties.address = address; - if (styleUrl) { - if (styleUrl[0] !== '#') { - styleUrl = '#' + styleUrl; - } - - properties.styleUrl = styleUrl; - if (styleIndex[styleUrl]) { - properties.styleHash = styleIndex[styleUrl]; - } - if (styleMapIndex[styleUrl]) { - properties.styleMapHash = styleMapIndex[styleUrl]; - properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal]; - } - // Try to populate the lineStyle or polyStyle since we got the style hash - var style = styleByHash[properties.styleHash]; - if (style) { - if (!lineStyle) lineStyle = get1(style, 'LineStyle'); - if (!polyStyle) polyStyle = get1(style, 'PolyStyle'); - } - } - if (description) properties.description = description; - if (timeSpan) { - var begin = nodeVal(get1(timeSpan, 'begin')); - var end = nodeVal(get1(timeSpan, 'end')); - properties.timespan = { begin: begin, end: end }; - } - if (timeStamp) { - properties.timestamp = nodeVal(get1(timeStamp, 'when')); - } - if (lineStyle) { - var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))), - color = linestyles[0], - opacity = linestyles[1], - width = parseFloat(nodeVal(get1(lineStyle, 'width'))); - if (color) properties.stroke = color; - if (!isNaN(opacity)) properties['stroke-opacity'] = opacity; - if (!isNaN(width)) properties['stroke-width'] = width; - } - if (polyStyle) { - var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))), - pcolor = polystyles[0], - popacity = polystyles[1], - fill = nodeVal(get1(polyStyle, 'fill')), - outline = nodeVal(get1(polyStyle, 'outline')); - if (pcolor) properties.fill = pcolor; - if (!isNaN(popacity)) properties['fill-opacity'] = popacity; - if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; - if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; - } - if (extendedData) { - var datas = get(extendedData, 'Data'), - simpleDatas = get(extendedData, 'SimpleData'); - - for (i = 0; i < datas.length; i++) { - properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value')); - } - for (i = 0; i < simpleDatas.length; i++) { - properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]); - } - } - if (visibility) { - properties.visibility = nodeVal(visibility); - } - if (geomsAndTimes.coordTimes.length) { - properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ? - geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes; - } - var feature = { - type: 'Feature', - geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : { - type: 'GeometryCollection', - geometries: geomsAndTimes.geoms - }, - properties: properties - }; - if (attr(root, 'id')) feature.id = attr(root, 'id'); - return [feature]; - } - return gj; - }, - gpx: function(doc) { - var i, - tracks = get(doc, 'trk'), - routes = get(doc, 'rte'), - waypoints = get(doc, 'wpt'), - // a feature collection - gj = fc(), - feature; - for (i = 0; i < tracks.length; i++) { - feature = getTrack(tracks[i]); - if (feature) gj.features.push(feature); - } - for (i = 0; i < routes.length; i++) { - feature = getRoute(routes[i]); - if (feature) gj.features.push(feature); - } - for (i = 0; i < waypoints.length; i++) { - gj.features.push(getPoint(waypoints[i])); - } - function getPoints(node, pointname) { - var pts = get(node, pointname), - line = [], - times = [], - heartRates = [], - l = pts.length; - if (l < 2) return {}; // Invalid line in GeoJSON - for (var i = 0; i < l; i++) { - var c = coordPair(pts[i]); - line.push(c.coordinates); - if (c.time) times.push(c.time); - if (c.heartRate) heartRates.push(c.heartRate); - } - return { - line: line, - times: times, - heartRates: heartRates - }; - } - function getTrack(node) { - var segments = get(node, 'trkseg'), - track = [], - times = [], - heartRates = [], - line; - for (var i = 0; i < segments.length; i++) { - line = getPoints(segments[i], 'trkpt'); - if (line) { - if (line.line) track.push(line.line); - if (line.times && line.times.length) times.push(line.times); - if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates); - } - } - if (track.length === 0) return; - var properties = getProperties(node); - extend(properties, getLineStyle(get1(node, 'extensions'))); - if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times; - if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; - return { - type: 'Feature', - properties: properties, - geometry: { - type: track.length === 1 ? 'LineString' : 'MultiLineString', - coordinates: track.length === 1 ? track[0] : track - } - }; - } - function getRoute(node) { - var line = getPoints(node, 'rtept'); - if (!line.line) return; - var prop = getProperties(node); - extend(prop, getLineStyle(get1(node, 'extensions'))); - var routeObj = { - type: 'Feature', - properties: prop, - geometry: { - type: 'LineString', - coordinates: line.line - } - }; - return routeObj; - } - function getPoint(node) { - var prop = getProperties(node); - extend(prop, getMulti(node, ['sym'])); - return { - type: 'Feature', - properties: prop, - geometry: { - type: 'Point', - coordinates: coordPair(node).coordinates - } - }; - } - function getLineStyle(extensions) { - var style = {}; - if (extensions) { - var lineStyle = get1(extensions, 'line'); - if (lineStyle) { - var color = nodeVal(get1(lineStyle, 'color')), - opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))), - width = parseFloat(nodeVal(get1(lineStyle, 'width'))); - if (color) style.stroke = color; - if (!isNaN(opacity)) style['stroke-opacity'] = opacity; - // GPX width is in mm, convert to px with 96 px per inch - if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4; - } - } - return style; - } - function getProperties(node) { - var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']), - links = get(node, 'link'); - if (links.length) prop.links = []; - for (var i = 0, link; i < links.length; i++) { - link = { href: attr(links[i], 'href') }; - extend(link, getMulti(links[i], ['text', 'type'])); - prop.links.push(link); - } - return prop; - } - return gj; - } - }; - return t; - })(); - - module.exports = toGeoJSON; - }); - - var _initialized = false; - var _enabled = false; - var _geojson; - - - function svgData(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var _showLabels = true; - var detected = utilDetect(); - var layer = select(null); - var _vtService; - var _fileList; - var _template; - var _src; - - - function init() { - if (_initialized) return; // run once - - _geojson = {}; - _enabled = true; - - function over() { - event.stopPropagation(); - event.preventDefault(); - event.dataTransfer.dropEffect = 'copy'; - } - - context.container() - .attr('dropzone', 'copy') - .on('drop.svgData', function() { - event.stopPropagation(); - event.preventDefault(); - if (!detected.filedrop) return; - drawData.fileList(event.dataTransfer.files); - }) - .on('dragenter.svgData', over) - .on('dragexit.svgData', over) - .on('dragover.svgData', over); - - _initialized = true; - } - - - function getService() { - if (services.vectorTile && !_vtService) { - _vtService = services.vectorTile; - _vtService.event.on('loadedData', throttledRedraw); - } else if (!services.vectorTile && _vtService) { - _vtService = null; - } - - return _vtService; - } - - - function showLayer() { - layerOn(); - - layer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end', function () { dispatch.call('change'); }); - } - - - function hideLayer() { - throttledRedraw.cancel(); - - layer - .transition() - .duration(250) - .style('opacity', 0) - .on('end', layerOff); - } - - - function layerOn() { - layer.style('display', 'block'); - } - - - function layerOff() { - layer.selectAll('.viewfield-group').remove(); - layer.style('display', 'none'); - } - - - // ensure that all geojson features in a collection have IDs - function ensureIDs(gj) { - if (!gj) return null; - - if (gj.type === 'FeatureCollection') { - for (var i = 0; i < gj.features.length; i++) { - ensureFeatureID(gj.features[i]); - } - } else { - ensureFeatureID(gj); - } - return gj; - } - - - // ensure that each single Feature object has a unique ID - function ensureFeatureID(feature) { - if (!feature) return; - feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature)); - return feature; - } - - - // Prefer an array of Features instead of a FeatureCollection - function getFeatures(gj) { - if (!gj) return []; - - if (gj.type === 'FeatureCollection') { - return gj.features; - } else { - return [gj]; - } - } - - - function featureKey(d) { - return d.__featurehash__; - } - - - function isPolygon(d) { - return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon'; - } - - - function clipPathID(d) { - return 'ideditor-data-' + d.__featurehash__ + '-clippath'; - } - - - function featureClasses(d) { - return [ - 'data' + d.__featurehash__, - d.geometry.type, - isPolygon(d) ? 'area' : '', - d.__layerID__ || '' - ].filter(Boolean).join(' '); - } - - - function drawData(selection) { - var vtService = getService(); - var getPath = svgPath(projection).geojson; - var getAreaPath = svgPath(projection, null, true).geojson; - var hasData = drawData.hasData(); - - layer = selection.selectAll('.layer-mapdata') - .data(_enabled && hasData ? [0] : []); - - layer.exit() - .remove(); - - layer = layer.enter() - .append('g') - .attr('class', 'layer-mapdata') - .merge(layer); - - var surface = context.surface(); - if (!surface || surface.empty()) return; // not ready to draw yet, starting up - - - // Gather data - var geoData, polygonData; - if (_template && vtService) { // fetch data from vector tile service - var sourceID = _template; - vtService.loadTiles(sourceID, _template, projection); - geoData = vtService.data(sourceID, projection); - } else { - geoData = getFeatures(_geojson); - } - geoData = geoData.filter(getPath); - polygonData = geoData.filter(isPolygon); - - - // Draw clip paths for polygons - var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data') - .data(polygonData, featureKey); - - clipPaths.exit() - .remove(); - - var clipPathsEnter = clipPaths.enter() - .append('clipPath') - .attr('class', 'clipPath-data') - .attr('id', clipPathID); - - clipPathsEnter - .append('path'); - - clipPaths.merge(clipPathsEnter) - .selectAll('path') - .attr('d', getAreaPath); - - - // Draw fill, shadow, stroke layers - var datagroups = layer - .selectAll('g.datagroup') - .data(['fill', 'shadow', 'stroke']); - - datagroups = datagroups.enter() - .append('g') - .attr('class', function(d) { return 'datagroup datagroup-' + d; }) - .merge(datagroups); - - - // Draw paths - var pathData = { - fill: polygonData, - shadow: geoData, - stroke: geoData - }; - - var paths = datagroups - .selectAll('path') - .data(function(layer) { return pathData[layer]; }, featureKey); - - // exit - paths.exit() - .remove(); - - // enter/update - paths = paths.enter() - .append('path') - .attr('class', function(d) { - var datagroup = this.parentNode.__data__; - return 'pathdata ' + datagroup + ' ' + featureClasses(d); - }) - .attr('clip-path', function(d) { - var datagroup = this.parentNode.__data__; - return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null; - }) - .merge(paths) - .attr('d', function(d) { - var datagroup = this.parentNode.__data__; - return datagroup === 'fill' ? getAreaPath(d) : getPath(d); - }); - - - // Draw labels - layer - .call(drawLabels, 'label-halo', geoData) - .call(drawLabels, 'label', geoData); - - - function drawLabels(selection, textClass, data) { - var labelPath = d3_geoPath(projection); - var labelData = data.filter(function(d) { - return _showLabels && d.properties && (d.properties.desc || d.properties.name); - }); - - var labels = selection.selectAll('text.' + textClass) - .data(labelData, featureKey); - - // exit - labels.exit() - .remove(); - - // enter/update - labels = labels.enter() - .append('text') - .attr('class', function(d) { return textClass + ' ' + featureClasses(d); }) - .merge(labels) - .text(function(d) { - return d.properties.desc || d.properties.name; - }) - .attr('x', function(d) { - var centroid = labelPath.centroid(d); - return centroid[0] + 11; - }) - .attr('y', function(d) { - var centroid = labelPath.centroid(d); - return centroid[1]; - }); - } - } - - - function getExtension(fileName) { - if (!fileName) return; - - var re = /\.(gpx|kml|(geo)?json)$/i; - var match = fileName.toLowerCase().match(re); - return match && match.length && match[0]; - } - - - function xmlToDom(textdata) { - return (new DOMParser()).parseFromString(textdata, 'text/xml'); - } - - - drawData.setFile = function(extension, data) { - _template = null; - _fileList = null; - _geojson = null; - _src = null; - - var gj; - switch (extension) { - case '.gpx': - gj = togeojson.gpx(xmlToDom(data)); - break; - case '.kml': - gj = togeojson.kml(xmlToDom(data)); - break; - case '.geojson': - case '.json': - gj = JSON.parse(data); - break; - } - - gj = gj || {}; - if (Object.keys(gj).length) { - _geojson = ensureIDs(gj); - _src = extension + ' data file'; - this.fitZoom(); - } - - dispatch.call('change'); - return this; - }; - - - drawData.showLabels = function(val) { - if (!arguments.length) return _showLabels; - - _showLabels = val; - return this; - }; - - - drawData.enabled = function(val) { - if (!arguments.length) return _enabled; - - _enabled = val; - if (_enabled) { - showLayer(); - } else { - hideLayer(); - } - - dispatch.call('change'); - return this; - }; - - - drawData.hasData = function() { - var gj = _geojson || {}; - return !!(_template || Object.keys(gj).length); - }; - - - drawData.template = function(val, src) { - if (!arguments.length) return _template; - - // test source against OSM imagery blacklists.. - var osm = context.connection(); - if (osm) { - var blacklists = osm.imageryBlacklists(); - var fail = false; - var tested = 0; - var regex; - - for (var i = 0; i < blacklists.length; i++) { - try { - regex = new RegExp(blacklists[i]); - fail = regex.test(val); - tested++; - if (fail) break; - } catch (e) { - /* noop */ - } - } - - // ensure at least one test was run. - if (!tested) { - regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'); - fail = regex.test(val); - } - } - - _template = val; - _fileList = null; - _geojson = null; - - // strip off the querystring/hash from the template, - // it often includes the access token - _src = src || ('vectortile:' + val.split(/[?#]/)[0]); - - dispatch.call('change'); - return this; - }; - - - drawData.geojson = function(gj, src) { - if (!arguments.length) return _geojson; - - _template = null; - _fileList = null; - _geojson = null; - _src = null; - - gj = gj || {}; - if (Object.keys(gj).length) { - _geojson = ensureIDs(gj); - _src = src || 'unknown.geojson'; - } - - dispatch.call('change'); - return this; - }; - - - drawData.fileList = function(fileList) { - if (!arguments.length) return _fileList; - - _template = null; - _fileList = fileList; - _geojson = null; - _src = null; - - if (!fileList || !fileList.length) return this; - var f = fileList[0]; - var extension = getExtension(f.name); - var reader = new FileReader(); - reader.onload = (function() { - return function(e) { - drawData.setFile(extension, e.target.result); - }; - })(); - - reader.readAsText(f); - - return this; - }; - - - drawData.url = function(url, defaultExtension) { - _template = null; - _fileList = null; - _geojson = null; - _src = null; - - // strip off any querystring/hash from the url before checking extension - var testUrl = url.split(/[?#]/)[0]; - var extension = getExtension(testUrl) || defaultExtension; - if (extension) { - _template = null; - d3_text(url) - .then(function(data) { - drawData.setFile(extension, data); - }) - .catch(function() { - /* ignore */ - }); - - } else { - drawData.template(url); - } - - return this; - }; - - - drawData.getSrc = function() { - return _src || ''; - }; - - - drawData.fitZoom = function() { - var features = getFeatures(_geojson); - if (!features.length) return; - - var map = context.map(); - var viewport = map.trimmedExtent().polygon(); - var coords = features.reduce(function(coords, feature) { - var geom = feature.geometry; - if (!geom) return coords; - - var c = geom.coordinates; - - /* eslint-disable no-fallthrough */ - switch (geom.type) { - case 'Point': - c = [c]; - case 'MultiPoint': - case 'LineString': - break; - - case 'MultiPolygon': - c = utilArrayFlatten(c); - case 'Polygon': - case 'MultiLineString': - c = utilArrayFlatten(c); - break; - } - /* eslint-enable no-fallthrough */ - - return utilArrayUnion(coords, c); - }, []); - - if (!geoPolygonIntersectsPolygon(viewport, coords, true)) { - var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords })); - map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); - } - - return this; - }; - - - init(); - return drawData; - } - - function svgDebug(projection, context) { - - function drawDebug(selection) { - const showTile = context.getDebug('tile'); - const showCollision = context.getDebug('collision'); - const showImagery = context.getDebug('imagery'); - const showTouchTargets = context.getDebug('target'); - const showDownloaded = context.getDebug('downloaded'); - - let debugData = []; - if (showTile) { - debugData.push({ class: 'red', label: 'tile' }); - } - if (showCollision) { - debugData.push({ class: 'yellow', label: 'collision' }); - } - if (showImagery) { - debugData.push({ class: 'orange', label: 'imagery' }); - } - if (showTouchTargets) { - debugData.push({ class: 'pink', label: 'touchTargets' }); - } - if (showDownloaded) { - debugData.push({ class: 'purple', label: 'downloaded' }); - } - - - let legend = context.container().select('.main-content') - .selectAll('.debug-legend') - .data(debugData.length ? [0] : []); - - legend.exit() - .remove(); - - legend = legend.enter() - .append('div') - .attr('class', 'fillD debug-legend') - .merge(legend); - - - let legendItems = legend.selectAll('.debug-legend-item') - .data(debugData, d => d.label); - - legendItems.exit() - .remove(); - - legendItems.enter() - .append('span') - .attr('class', d => `debug-legend-item ${d.class}`) - .text(d => d.label); - - - let layer = selection.selectAll('.layer-debug') - .data(showImagery || showDownloaded ? [0] : []); - - layer.exit() - .remove(); - - layer = layer.enter() - .append('g') - .attr('class', 'layer-debug') - .merge(layer); - - - // imagery - const extent = context.map().extent(); - _mainFileFetcher.get('imagery') - .then(d => { - const hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || []; - const features = hits.map(d => d.features[d.id]); - - let imagery = layer.selectAll('path.debug-imagery') - .data(features); - - imagery.exit() - .remove(); - - imagery.enter() - .append('path') - .attr('class', 'debug-imagery debug orange'); - }) - .catch(() => { /* ignore */ }); - - // downloaded - const osm = context.connection(); - let dataDownloaded = []; - if (osm && showDownloaded) { - const rtree = osm.caches('get').tile.rtree; - dataDownloaded = rtree.all().map(bbox => { - return { - type: 'Feature', - properties: { id: bbox.id }, - geometry: { - type: 'Polygon', - coordinates: [[ - [ bbox.minX, bbox.minY ], - [ bbox.minX, bbox.maxY ], - [ bbox.maxX, bbox.maxY ], - [ bbox.maxX, bbox.minY ], - [ bbox.minX, bbox.minY ] - ]] - } - }; - }); - } - - let downloaded = layer - .selectAll('path.debug-downloaded') - .data(showDownloaded ? dataDownloaded : []); - - downloaded.exit() - .remove(); - - downloaded.enter() - .append('path') - .attr('class', 'debug-downloaded debug purple'); - - // update - layer.selectAll('path') - .attr('d', svgPath(projection).geojson); - } - - - // This looks strange because `enabled` methods on other layers are - // chainable getter/setters, and this one is just a getter. - drawDebug.enabled = function() { - if (!arguments.length) { - return context.getDebug('tile') || - context.getDebug('collision') || - context.getDebug('imagery') || - context.getDebug('target') || - context.getDebug('downloaded'); - } else { - return this; - } - }; - - - return drawDebug; - } - - let _layerEnabled = false; - let _qaService; - - function svgKeepRight(projection, context, dispatch) { - const throttledRedraw = throttle(() => dispatch.call('change'), 1000); - const minZoom = 12; - - let touchLayer = select(null); - let drawLayer = select(null); - let layerVisible = false; - - function markerPath(selection, klass) { - selection - .attr('class', klass) - .attr('transform', 'translate(-4, -24)') - .attr('d', 'M11.6,6.2H7.1l1.4-5.1C8.6,0.6,8.1,0,7.5,0H2.2C1.7,0,1.3,0.3,1.3,0.8L0,10.2c-0.1,0.6,0.4,1.1,0.9,1.1h4.6l-1.8,7.6C3.6,19.4,4.1,20,4.7,20c0.3,0,0.6-0.2,0.8-0.5l6.9-11.9C12.7,7,12.3,6.2,11.6,6.2z'); - } - - // Loosely-coupled keepRight service for fetching issues. - function getService() { - if (services.keepRight && !_qaService) { - _qaService = services.keepRight; - _qaService.on('loaded', throttledRedraw); - } else if (!services.keepRight && _qaService) { - _qaService = null; - } - - return _qaService; - } - - // Show the markers - function editOn() { - if (!layerVisible) { - layerVisible = true; - drawLayer - .style('display', 'block'); - } - } - - // Immediately remove the markers and their touch targets - function editOff() { - if (layerVisible) { - layerVisible = false; - drawLayer - .style('display', 'none'); - drawLayer.selectAll('.qaItem.keepRight') - .remove(); - touchLayer.selectAll('.qaItem.keepRight') - .remove(); - } - } - - // Enable the layer. This shows the markers and transitions them to visible. - function layerOn() { - editOn(); - - drawLayer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', () => dispatch.call('change')); - } - - // Disable the layer. This transitions the layer invisible and then hides the markers. - function layerOff() { - throttledRedraw.cancel(); - drawLayer.interrupt(); - touchLayer.selectAll('.qaItem.keepRight') - .remove(); - - drawLayer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', () => { - editOff(); - dispatch.call('change'); - }); - } - - // Update the issue markers - function updateMarkers() { - if (!layerVisible || !_layerEnabled) return; - - const service = getService(); - const selectedID = context.selectedErrorID(); - const data = (service ? service.getItems(projection) : []); - const getTransform = svgPointTransform(projection); - - // Draw markers.. - const markers = drawLayer.selectAll('.qaItem.keepRight') - .data(data, d => d.id); - - // exit - markers.exit() - .remove(); - - // enter - const markersEnter = markers.enter() - .append('g') - .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.parentIssueType}`); - - markersEnter - .append('ellipse') - .attr('cx', 0.5) - .attr('cy', 1) - .attr('rx', 6.5) - .attr('ry', 3) - .attr('class', 'stroke'); - - markersEnter - .append('path') - .call(markerPath, 'shadow'); - - markersEnter - .append('use') - .attr('class', 'qaItem-fill') - .attr('width', '20px') - .attr('height', '20px') - .attr('x', '-8px') - .attr('y', '-22px') - .attr('xlink:href', '#iD-icon-bolt'); - - // update - markers - .merge(markersEnter) - .sort(sortY) - .classed('selected', d => d.id === selectedID) - .attr('transform', getTransform); - - - // Draw targets.. - if (touchLayer.empty()) return; - const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - - const targets = touchLayer.selectAll('.qaItem.keepRight') - .data(data, d => d.id); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('rect') - .attr('width', '20px') - .attr('height', '20px') - .attr('x', '-8px') - .attr('y', '-22px') - .merge(targets) - .sort(sortY) - .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`) - .attr('transform', getTransform); - - - function sortY(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : (a.severity === 'error' && b.severity !== 'error') ? 1 - : (b.severity === 'error' && a.severity !== 'error') ? -1 - : b.loc[1] - a.loc[1]; - } - } - - // Draw the keepRight layer and schedule loading issues and updating markers. - function drawKeepRight(selection) { - const service = getService(); - - const surface = context.surface(); - if (surface && !surface.empty()) { - touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); - } - - drawLayer = selection.selectAll('.layer-keepRight') - .data(service ? [0] : []); - - drawLayer.exit() - .remove(); - - drawLayer = drawLayer.enter() - .append('g') - .attr('class', 'layer-keepRight') - .style('display', _layerEnabled ? 'block' : 'none') - .merge(drawLayer); - - if (_layerEnabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - service.loadIssues(projection); - updateMarkers(); - } else { - editOff(); - } - } - } - - // Toggles the layer on and off - drawKeepRight.enabled = function(val) { - if (!arguments.length) return _layerEnabled; - - _layerEnabled = val; - if (_layerEnabled) { - layerOn(); - } else { - layerOff(); - if (context.selectedErrorID()) { - context.enter(modeBrowse(context)); - } - } - - dispatch.call('change'); - return this; - }; - - drawKeepRight.supported = () => !!getService(); - - return drawKeepRight; - } - - function svgGeolocate(projection) { - var layer = select(null); - var _position; - - - function init() { - if (svgGeolocate.initialized) return; // run once - svgGeolocate.enabled = false; - svgGeolocate.initialized = true; - } - - function showLayer() { - layer.style('display', 'block'); - } - - - function hideLayer() { - layer - .transition() - .duration(250) - .style('opacity', 0); - } - - function layerOn() { - layer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1); - - } - - function layerOff() { - layer.style('display', 'none'); - } - - function transform(d) { - return svgPointTransform(projection)(d); - } - - function accuracy(accuracy, loc) { // converts accuracy to pixels... - var degreesRadius = geoMetersToLat(accuracy), - tangentLoc = [loc[0], loc[1] + degreesRadius], - projectedTangent = projection(tangentLoc), - projectedLoc = projection([loc[0], loc[1]]); - - // southern most point will have higher pixel value... - return Math.round(projectedLoc[1] - projectedTangent[1]).toString(); - } - - function update() { - var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] }; - - var groups = layer.selectAll('.geolocations').selectAll('.geolocation') - .data([geolocation]); - - groups.exit() - .remove(); - - var pointsEnter = groups.enter() - .append('g') - .attr('class', 'geolocation'); - - pointsEnter - .append('circle') - .attr('class', 'geolocate-radius') - .attr('dx', '0') - .attr('dy', '0') - .attr('fill', 'rgb(15,128,225)') - .attr('fill-opacity', '0.3') - .attr('r', '0'); - - pointsEnter - .append('circle') - .attr('dx', '0') - .attr('dy', '0') - .attr('fill', 'rgb(15,128,225)') - .attr('stroke', 'white') - .attr('stroke-width', '1.5') - .attr('r', '6'); - - groups.merge(pointsEnter) - .attr('transform', transform); - - layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc)); - } - - function drawLocation(selection) { - var enabled = svgGeolocate.enabled; - - layer = selection.selectAll('.layer-geolocate') - .data([0]); - - layer.exit() - .remove(); - - var layerEnter = layer.enter() - .append('g') - .attr('class', 'layer-geolocate') - .style('display', enabled ? 'block' : 'none'); - - layerEnter - .append('g') - .attr('class', 'geolocations'); - - layer = layerEnter - .merge(layer); - - if (enabled) { - update(); - } else { - layerOff(); - } - } - - drawLocation.enabled = function (position, enabled) { - if (!arguments.length) return svgGeolocate.enabled; - _position = position; - svgGeolocate.enabled = enabled; - if (svgGeolocate.enabled) { - showLayer(); - layerOn(); - } else { - hideLayer(); - } - return this; - }; - - init(); - return drawLocation; - } - - function svgLabels(projection, context) { - var path = d3_geoPath(projection); - var detected = utilDetect(); - var baselineHack = (detected.ie || - detected.browser.toLowerCase() === 'edge' || - (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70)); - var _rdrawn = new RBush(); - var _rskipped = new RBush(); - var _textWidthCache = {}; - var _entitybboxes = {}; - - // Listed from highest to lowest priority - var labelStack = [ - ['line', 'aeroway', '*', 12], - ['line', 'highway', 'motorway', 12], - ['line', 'highway', 'trunk', 12], - ['line', 'highway', 'primary', 12], - ['line', 'highway', 'secondary', 12], - ['line', 'highway', 'tertiary', 12], - ['line', 'highway', '*', 12], - ['line', 'railway', '*', 12], - ['line', 'waterway', '*', 12], - ['area', 'aeroway', '*', 12], - ['area', 'amenity', '*', 12], - ['area', 'building', '*', 12], - ['area', 'historic', '*', 12], - ['area', 'leisure', '*', 12], - ['area', 'man_made', '*', 12], - ['area', 'natural', '*', 12], - ['area', 'shop', '*', 12], - ['area', 'tourism', '*', 12], - ['area', 'camp_site', '*', 12], - ['point', 'aeroway', '*', 10], - ['point', 'amenity', '*', 10], - ['point', 'building', '*', 10], - ['point', 'historic', '*', 10], - ['point', 'leisure', '*', 10], - ['point', 'man_made', '*', 10], - ['point', 'natural', '*', 10], - ['point', 'shop', '*', 10], - ['point', 'tourism', '*', 10], - ['point', 'camp_site', '*', 10], - ['line', 'name', '*', 12], - ['area', 'name', '*', 12], - ['point', 'name', '*', 10] - ]; - - - function shouldSkipIcon(preset) { - var noIcons = ['building', 'landuse', 'natural']; - return noIcons.some(function(s) { - return preset.id.indexOf(s) >= 0; - }); - } - - - function get(array, prop) { - return function(d, i) { return array[i][prop]; }; - } - - - function textWidth(text, size, elem) { - var c = _textWidthCache[size]; - if (!c) c = _textWidthCache[size] = {}; - - if (c[text]) { - return c[text]; - - } else if (elem) { - c[text] = elem.getComputedTextLength(); - return c[text]; - - } else { - var str = encodeURIComponent(text).match(/%[CDEFcdef]/g); - if (str === null) { - return size / 3 * 2 * text.length; - } else { - return size / 3 * (2 * text.length + str.length); - } - } - } - - - function drawLinePaths(selection, entities, filter, classes, labels) { - var paths = selection.selectAll('path') - .filter(filter) - .data(entities, osmEntity.key); - - // exit - paths.exit() - .remove(); - - // enter/update - paths.enter() - .append('path') - .style('stroke-width', get(labels, 'font-size')) - .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; }) - .attr('class', classes) - .merge(paths) - .attr('d', get(labels, 'lineString')); - } - - - function drawLineLabels(selection, entities, filter, classes, labels) { - var texts = selection.selectAll('text.' + classes) - .filter(filter) - .data(entities, osmEntity.key); - - // exit - texts.exit() - .remove(); - - // enter - texts.enter() - .append('text') - .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; }) - .attr('dy', baselineHack ? '0.35em' : null) - .append('textPath') - .attr('class', 'textpath'); - - // update - selection.selectAll('text.' + classes).selectAll('.textpath') - .filter(filter) - .data(entities, osmEntity.key) - .attr('startOffset', '50%') - .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; }) - .text(utilDisplayNameForPath); - } - - - function drawPointLabels(selection, entities, filter, classes, labels) { - var texts = selection.selectAll('text.' + classes) - .filter(filter) - .data(entities, osmEntity.key); - - // exit - texts.exit() - .remove(); - - // enter/update - texts.enter() - .append('text') - .attr('class', function(d, i) { - return classes + ' ' + labels[i].classes + ' ' + d.id; - }) - .merge(texts) - .attr('x', get(labels, 'x')) - .attr('y', get(labels, 'y')) - .style('text-anchor', get(labels, 'textAnchor')) - .text(utilDisplayName) - .each(function(d, i) { - textWidth(utilDisplayName(d), labels[i].height, this); - }); - } - - - function drawAreaLabels(selection, entities, filter, classes, labels) { - entities = entities.filter(hasText); - labels = labels.filter(hasText); - drawPointLabels(selection, entities, filter, classes, labels); - - function hasText(d, i) { - return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y'); - } - } - - - function drawAreaIcons(selection, entities, filter, classes, labels) { - var icons = selection.selectAll('use.' + classes) - .filter(filter) - .data(entities, osmEntity.key); - - // exit - icons.exit() - .remove(); - - // enter/update - icons.enter() - .append('use') - .attr('class', 'icon ' + classes) - .attr('width', '17px') - .attr('height', '17px') - .merge(icons) - .attr('transform', get(labels, 'transform')) - .attr('xlink:href', function(d) { - var preset = _mainPresetIndex.match(d, context.graph()); - var picon = preset && preset.icon; - - if (!picon) { - return ''; - } else { - var isMaki = /^maki-/.test(picon); - return '#' + picon + (isMaki ? '-15' : ''); - } - }); - } - - - function drawCollisionBoxes(selection, rtree, which) { - var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow'); - - var gj = []; - if (context.getDebug('collision')) { - gj = rtree.all().map(function(d) { - return { type: 'Polygon', coordinates: [[ - [d.minX, d.minY], - [d.maxX, d.minY], - [d.maxX, d.maxY], - [d.minX, d.maxY], - [d.minX, d.minY] - ]]}; - }); - } - - var boxes = selection.selectAll('.' + which) - .data(gj); - - // exit - boxes.exit() - .remove(); - - // enter/update - boxes.enter() - .append('path') - .attr('class', classes) - .merge(boxes) - .attr('d', d3_geoPath()); - } - - - function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) { - var wireframe = context.surface().classed('fill-wireframe'); - var zoom = geoScaleToZoom(projection.scale()); - - var labelable = []; - var renderNodeAs = {}; - var i, j, k, entity, geometry; - - for (i = 0; i < labelStack.length; i++) { - labelable.push([]); - } - - if (fullRedraw) { - _rdrawn.clear(); - _rskipped.clear(); - _entitybboxes = {}; - - } else { - for (i = 0; i < entities.length; i++) { - entity = entities[i]; - var toRemove = [] - .concat(_entitybboxes[entity.id] || []) - .concat(_entitybboxes[entity.id + 'I'] || []); - - for (j = 0; j < toRemove.length; j++) { - _rdrawn.remove(toRemove[j]); - _rskipped.remove(toRemove[j]); - } - } - } - - // Loop through all the entities to do some preprocessing - for (i = 0; i < entities.length; i++) { - entity = entities[i]; - geometry = entity.geometry(graph); - - // Insert collision boxes around interesting points/vertices - if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) { - var hasDirections = entity.directions(graph, projection).length; - var markerPadding; - - if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) { - renderNodeAs[entity.id] = 'point'; - markerPadding = 20; // extra y for marker height - } else { - renderNodeAs[entity.id] = 'vertex'; - markerPadding = 0; - } - - var coord = projection(entity.loc); - var nodePadding = 10; - var bbox = { - minX: coord[0] - nodePadding, - minY: coord[1] - nodePadding - markerPadding, - maxX: coord[0] + nodePadding, - maxY: coord[1] + nodePadding - }; - - doInsert(bbox, entity.id + 'P'); - } - - // From here on, treat vertices like points - if (geometry === 'vertex') { - geometry = 'point'; - } - - // Determine which entities are label-able - var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph); - var icon = preset && !shouldSkipIcon(preset) && preset.icon; - - if (!icon && !utilDisplayName(entity)) - continue; - - for (k = 0; k < labelStack.length; k++) { - var matchGeom = labelStack[k][0]; - var matchKey = labelStack[k][1]; - var matchVal = labelStack[k][2]; - var hasVal = entity.tags[matchKey]; - - if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) { - labelable[k].push(entity); - break; - } - } - } - - var positions = { - point: [], - line: [], - area: [] - }; - - var labelled = { - point: [], - line: [], - area: [] - }; - - // Try and find a valid label for labellable entities - for (k = 0; k < labelable.length; k++) { - var fontSize = labelStack[k][3]; - - for (i = 0; i < labelable[k].length; i++) { - entity = labelable[k][i]; - geometry = entity.geometry(graph); - - var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName; - var name = getName(entity); - var width = name && textWidth(name, fontSize); - var p = null; - - if (geometry === 'point' || geometry === 'vertex') { - // no point or vertex labels in wireframe mode - // no vertex labels at low zooms (vertices have no icons) - if (wireframe) continue; - var renderAs = renderNodeAs[entity.id]; - if (renderAs === 'vertex' && zoom < 17) continue; - - p = getPointLabel(entity, width, fontSize, renderAs); - - } else if (geometry === 'line') { - p = getLineLabel(entity, width, fontSize); - - } else if (geometry === 'area') { - p = getAreaLabel(entity, width, fontSize); - } - - if (p) { - if (geometry === 'vertex') { geometry = 'point'; } // treat vertex like point - p.classes = geometry + ' tag-' + labelStack[k][1]; - positions[geometry].push(p); - labelled[geometry].push(entity); - } - } - } - - - function isInterestingVertex(entity) { - var selectedIDs = context.selectedIDs(); - - return entity.hasInterestingTags() || - entity.isEndpoint(graph) || - entity.isConnected(graph) || - selectedIDs.indexOf(entity.id) !== -1 || - graph.parentWays(entity).some(function(parent) { - return selectedIDs.indexOf(parent.id) !== -1; - }); - } - - - function getPointLabel(entity, width, height, geometry) { - var y = (geometry === 'point' ? -12 : 0); - var pointOffsets = { - ltr: [15, y, 'start'], - rtl: [-15, y, 'end'] - }; - - var textDirection = _mainLocalizer.textDirection(); - - var coord = projection(entity.loc); - var textPadding = 2; - var offset = pointOffsets[textDirection]; - var p = { - height: height, - width: width, - x: coord[0] + offset[0], - y: coord[1] + offset[1], - textAnchor: offset[2] - }; - - // insert a collision box for the text label.. - var bbox; - if (textDirection === 'rtl') { - bbox = { - minX: p.x - width - textPadding, - minY: p.y - (height / 2) - textPadding, - maxX: p.x + textPadding, - maxY: p.y + (height / 2) + textPadding - }; - } else { - bbox = { - minX: p.x - textPadding, - minY: p.y - (height / 2) - textPadding, - maxX: p.x + width + textPadding, - maxY: p.y + (height / 2) + textPadding - }; - } - - if (tryInsert([bbox], entity.id, true)) { - return p; - } - } - - - function getLineLabel(entity, width, height) { - var viewport = geoExtent(context.projection.clipExtent()).polygon(); - var points = graph.childNodes(entity) - .map(function(node) { return projection(node.loc); }); - var length = geoPathLength(points); - - if (length < width + 20) return; - - // % along the line to attempt to place the label - var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, - 25, 75, 20, 80, 15, 95, 10, 90, 5, 95]; - var padding = 3; - - for (var i = 0; i < lineOffsets.length; i++) { - var offset = lineOffsets[i]; - var middle = offset / 100 * length; - var start = middle - width / 2; - - if (start < 0 || start + width > length) continue; - - // generate subpath and ignore paths that are invalid or don't cross viewport. - var sub = subpath(points, start, start + width); - if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) { - continue; - } - - var isReverse = reverse(sub); - if (isReverse) { - sub = sub.reverse(); - } - - var bboxes = []; - var boxsize = (height + 2) / 2; - - for (var j = 0; j < sub.length - 1; j++) { - var a = sub[j]; - var b = sub[j + 1]; - - // split up the text into small collision boxes - var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2)); - - for (var box = 0; box < num; box++) { - var p = geoVecInterp(a, b, box / num); - var x0 = p[0] - boxsize - padding; - var y0 = p[1] - boxsize - padding; - var x1 = p[0] + boxsize + padding; - var y1 = p[1] + boxsize + padding; - - bboxes.push({ - minX: Math.min(x0, x1), - minY: Math.min(y0, y1), - maxX: Math.max(x0, x1), - maxY: Math.max(y0, y1) - }); - } - } - - if (tryInsert(bboxes, entity.id, false)) { // accept this one - return { - 'font-size': height + 2, - lineString: lineString(sub), - startOffset: offset + '%' - }; - } - } - - function reverse(p) { - var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]); - return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2); - } - - function lineString(points) { - return 'M' + points.join('L'); - } - - function subpath(points, from, to) { - var sofar = 0; - var start, end, i0, i1; - - for (var i = 0; i < points.length - 1; i++) { - var a = points[i]; - var b = points[i + 1]; - var current = geoVecLength(a, b); - var portion; - if (!start && sofar + current >= from) { - portion = (from - sofar) / current; - start = [ - a[0] + portion * (b[0] - a[0]), - a[1] + portion * (b[1] - a[1]) - ]; - i0 = i + 1; - } - if (!end && sofar + current >= to) { - portion = (to - sofar) / current; - end = [ - a[0] + portion * (b[0] - a[0]), - a[1] + portion * (b[1] - a[1]) - ]; - i1 = i + 1; - } - sofar += current; - } - - var result = points.slice(i0, i1); - result.unshift(start); - result.push(end); - return result; - } - } - - - function getAreaLabel(entity, width, height) { - var centroid = path.centroid(entity.asGeoJSON(graph, true)); - var extent = entity.extent(graph); - var areaWidth = projection(extent[1])[0] - projection(extent[0])[0]; - - if (isNaN(centroid[0]) || areaWidth < 20) return; - - var preset = _mainPresetIndex.match(entity, context.graph()); - var picon = preset && preset.icon; - var iconSize = 17; - var padding = 2; - var p = {}; - - if (picon) { // icon and label.. - if (addIcon()) { - addLabel(iconSize + padding); - return p; - } - } else { // label only.. - if (addLabel(0)) { - return p; - } - } - - - function addIcon() { - var iconX = centroid[0] - (iconSize / 2); - var iconY = centroid[1] - (iconSize / 2); - var bbox = { - minX: iconX, - minY: iconY, - maxX: iconX + iconSize, - maxY: iconY + iconSize - }; - - if (tryInsert([bbox], entity.id + 'I', true)) { - p.transform = 'translate(' + iconX + ',' + iconY + ')'; - return true; - } - return false; - } - - function addLabel(yOffset) { - if (width && areaWidth >= width + 20) { - var labelX = centroid[0]; - var labelY = centroid[1] + yOffset; - var bbox = { - minX: labelX - (width / 2) - padding, - minY: labelY - (height / 2) - padding, - maxX: labelX + (width / 2) + padding, - maxY: labelY + (height / 2) + padding - }; - - if (tryInsert([bbox], entity.id, true)) { - p.x = labelX; - p.y = labelY; - p.textAnchor = 'middle'; - p.height = height; - return true; - } - } - return false; - } - } - - - // force insert a singular bounding box - // singular box only, no array, id better be unique - function doInsert(bbox, id) { - bbox.id = id; - - var oldbox = _entitybboxes[id]; - if (oldbox) { - _rdrawn.remove(oldbox); - } - _entitybboxes[id] = bbox; - _rdrawn.insert(bbox); - } - - - function tryInsert(bboxes, id, saveSkipped) { - var skipped = false; - - for (var i = 0; i < bboxes.length; i++) { - var bbox = bboxes[i]; - bbox.id = id; - - // Check that label is visible - if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) { - skipped = true; - break; - } - if (_rdrawn.collides(bbox)) { - skipped = true; - break; - } - } - - _entitybboxes[id] = bboxes; - - if (skipped) { - if (saveSkipped) { - _rskipped.load(bboxes); - } - } else { - _rdrawn.load(bboxes); - } - - return !skipped; - } - - - var layer = selection.selectAll('.layer-osm.labels'); - layer.selectAll('.labels-group') - .data(['halo', 'label', 'debug']) - .enter() - .append('g') - .attr('class', function(d) { return 'labels-group ' + d; }); - - var halo = layer.selectAll('.labels-group.halo'); - var label = layer.selectAll('.labels-group.label'); - var debug = layer.selectAll('.labels-group.debug'); - - // points - drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point); - drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); - - // lines - drawLinePaths(layer, labelled.line, filter, '', positions.line); - drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line); - drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); - - // areas - drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area); - drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area); - drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area); - drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); - - // debug - drawCollisionBoxes(debug, _rskipped, 'debug-skipped'); - drawCollisionBoxes(debug, _rdrawn, 'debug-drawn'); - - layer.call(filterLabels); - } - - - function filterLabels(selection) { - var drawLayer = selection.selectAll('.layer-osm.labels'); - var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label'); - - layers.selectAll('.nolabel') - .classed('nolabel', false); - - var mouse = context.map().mouse(); - var graph = context.graph(); - var selectedIDs = context.selectedIDs(); - var ids = []; - var pad, bbox; - - // hide labels near the mouse - if (mouse) { - pad = 20; - bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad }; - var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; }); - ids.push.apply(ids, nearMouse); - } - - // hide labels on selected nodes (they look weird when dragging / haloed) - for (var i = 0; i < selectedIDs.length; i++) { - var entity = graph.hasEntity(selectedIDs[i]); - if (entity && entity.type === 'node') { - ids.push(selectedIDs[i]); - } - } - - layers.selectAll(utilEntitySelector(ids)) - .classed('nolabel', true); - - - // draw the mouse bbox if debugging is on.. - var debug = selection.selectAll('.labels-group.debug'); - var gj = []; - if (context.getDebug('collision')) { - gj = bbox ? [{ - type: 'Polygon', - coordinates: [[ - [bbox.minX, bbox.minY], - [bbox.maxX, bbox.minY], - [bbox.maxX, bbox.maxY], - [bbox.minX, bbox.maxY], - [bbox.minX, bbox.minY] - ]] - }] : []; - } - - var box = debug.selectAll('.debug-mouse') - .data(gj); - - // exit - box.exit() - .remove(); - - // enter/update - box.enter() - .append('path') - .attr('class', 'debug debug-mouse yellow') - .merge(box) - .attr('d', d3_geoPath()); - } - - - var throttleFilterLabels = throttle(filterLabels, 100); - - - drawLabels.observe = function(selection) { - var listener = function() { throttleFilterLabels(selection); }; - selection.on('mousemove.hidelabels', listener); - context.on('enter.hidelabels', listener); - }; - - - drawLabels.off = function(selection) { - throttleFilterLabels.cancel(); - selection.on('mousemove.hidelabels', null); - context.on('enter.hidelabels', null); - }; - - - return drawLabels; - } - - let _layerEnabled$1 = false; - let _qaService$1; - - function svgImproveOSM(projection, context, dispatch) { - const throttledRedraw = throttle(() => dispatch.call('change'), 1000); - const minZoom = 12; - - let touchLayer = select(null); - let drawLayer = select(null); - let layerVisible = false; - - function markerPath(selection, klass) { - selection - .attr('class', klass) - .attr('transform', 'translate(-10, -28)') - .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); - } - - // Loosely-coupled improveOSM service for fetching issues - function getService() { - if (services.improveOSM && !_qaService$1) { - _qaService$1 = services.improveOSM; - _qaService$1.on('loaded', throttledRedraw); - } else if (!services.improveOSM && _qaService$1) { - _qaService$1 = null; - } - - return _qaService$1; - } - - // Show the markers - function editOn() { - if (!layerVisible) { - layerVisible = true; - drawLayer - .style('display', 'block'); - } - } - - // Immediately remove the markers and their touch targets - function editOff() { - if (layerVisible) { - layerVisible = false; - drawLayer - .style('display', 'none'); - drawLayer.selectAll('.qaItem.improveOSM') - .remove(); - touchLayer.selectAll('.qaItem.improveOSM') - .remove(); - } - } - - // Enable the layer. This shows the markers and transitions them to visible. - function layerOn() { - editOn(); - - drawLayer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', () => dispatch.call('change')); - } - - // Disable the layer. This transitions the layer invisible and then hides the markers. - function layerOff() { - throttledRedraw.cancel(); - drawLayer.interrupt(); - touchLayer.selectAll('.qaItem.improveOSM') - .remove(); - - drawLayer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', () => { - editOff(); - dispatch.call('change'); - }); - } - - // Update the issue markers - function updateMarkers() { - if (!layerVisible || !_layerEnabled$1) return; - - const service = getService(); - const selectedID = context.selectedErrorID(); - const data = (service ? service.getItems(projection) : []); - const getTransform = svgPointTransform(projection); - - // Draw markers.. - const markers = drawLayer.selectAll('.qaItem.improveOSM') - .data(data, d => d.id); - - // exit - markers.exit() - .remove(); - - // enter - const markersEnter = markers.enter() - .append('g') - .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - markersEnter - .append('polygon') - .call(markerPath, 'shadow'); - - markersEnter - .append('ellipse') - .attr('cx', 0) - .attr('cy', 0) - .attr('rx', 4.5) - .attr('ry', 2) - .attr('class', 'stroke'); - - markersEnter - .append('polygon') - .attr('fill', 'currentColor') - .call(markerPath, 'qaItem-fill'); - - markersEnter - .append('use') - .attr('transform', 'translate(-6.5, -23)') - .attr('class', 'icon-annotation') - .attr('width', '13px') - .attr('height', '13px') - .attr('xlink:href', d => { - const picon = d.icon; - - if (!picon) { - return ''; - } else { - const isMaki = /^maki-/.test(picon); - return `#${picon}${isMaki ? '-11' : ''}`; - } - }); - - // update - markers - .merge(markersEnter) - .sort(sortY) - .classed('selected', d => d.id === selectedID) - .attr('transform', getTransform); - - - // Draw targets.. - if (touchLayer.empty()) return; - const fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - - const targets = touchLayer.selectAll('.qaItem.improveOSM') - .data(data, d => d.id); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('rect') - .attr('width', '20px') - .attr('height', '30px') - .attr('x', '-10px') - .attr('y', '-28px') - .merge(targets) - .sort(sortY) - .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`) - .attr('transform', getTransform); - - function sortY(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : b.loc[1] - a.loc[1]; - } - } - - // Draw the ImproveOSM layer and schedule loading issues and updating markers. - function drawImproveOSM(selection) { - const service = getService(); - - const surface = context.surface(); - if (surface && !surface.empty()) { - touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); - } - - drawLayer = selection.selectAll('.layer-improveOSM') - .data(service ? [0] : []); - - drawLayer.exit() - .remove(); - - drawLayer = drawLayer.enter() - .append('g') - .attr('class', 'layer-improveOSM') - .style('display', _layerEnabled$1 ? 'block' : 'none') - .merge(drawLayer); - - if (_layerEnabled$1) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - service.loadIssues(projection); - updateMarkers(); - } else { - editOff(); - } - } - } - - // Toggles the layer on and off - drawImproveOSM.enabled = function(val) { - if (!arguments.length) return _layerEnabled$1; - - _layerEnabled$1 = val; - if (_layerEnabled$1) { - layerOn(); - } else { - layerOff(); - if (context.selectedErrorID()) { - context.enter(modeBrowse(context)); - } - } - - dispatch.call('change'); - return this; - }; - - drawImproveOSM.supported = () => !!getService(); - - return drawImproveOSM; - } - - let _layerEnabled$2 = false; - let _qaService$2; - - function svgOsmose(projection, context, dispatch) { - const throttledRedraw = throttle(() => dispatch.call('change'), 1000); - const minZoom = 12; - - let touchLayer = select(null); - let drawLayer = select(null); - let layerVisible = false; - - function markerPath(selection, klass) { - selection - .attr('class', klass) - .attr('transform', 'translate(-10, -28)') - .attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6'); - } - - // Loosely-coupled osmose service for fetching issues - function getService() { - if (services.osmose && !_qaService$2) { - _qaService$2 = services.osmose; - _qaService$2.on('loaded', throttledRedraw); - } else if (!services.osmose && _qaService$2) { - _qaService$2 = null; - } - - return _qaService$2; - } - - // Show the markers - function editOn() { - if (!layerVisible) { - layerVisible = true; - drawLayer - .style('display', 'block'); - } - } - - // Immediately remove the markers and their touch targets - function editOff() { - if (layerVisible) { - layerVisible = false; - drawLayer - .style('display', 'none'); - drawLayer.selectAll('.qaItem.osmose') - .remove(); - touchLayer.selectAll('.qaItem.osmose') - .remove(); - } - } - - // Enable the layer. This shows the markers and transitions them to visible. - function layerOn() { - editOn(); - - drawLayer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', () => dispatch.call('change')); - } - - // Disable the layer. This transitions the layer invisible and then hides the markers. - function layerOff() { - throttledRedraw.cancel(); - drawLayer.interrupt(); - touchLayer.selectAll('.qaItem.osmose') - .remove(); - - drawLayer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', () => { - editOff(); - dispatch.call('change'); - }); - } - - // Update the issue markers - function updateMarkers() { - if (!layerVisible || !_layerEnabled$2) return; - - const service = getService(); - const selectedID = context.selectedErrorID(); - const data = (service ? service.getItems(projection) : []); - const getTransform = svgPointTransform(projection); - - // Draw markers.. - const markers = drawLayer.selectAll('.qaItem.osmose') - .data(data, d => d.id); - - // exit - markers.exit() - .remove(); - - // enter - const markersEnter = markers.enter() - .append('g') - .attr('class', d => `qaItem ${d.service} itemId-${d.id} itemType-${d.itemType}`); - - markersEnter - .append('polygon') - .call(markerPath, 'shadow'); - - markersEnter - .append('ellipse') - .attr('cx', 0) - .attr('cy', 0) - .attr('rx', 4.5) - .attr('ry', 2) - .attr('class', 'stroke'); - - markersEnter - .append('polygon') - .attr('fill', d => service.getColor(d.item)) - .call(markerPath, 'qaItem-fill'); - - markersEnter - .append('use') - .attr('transform', 'translate(-6.5, -23)') - .attr('class', 'icon-annotation') - .attr('width', '13px') - .attr('height', '13px') - .attr('xlink:href', d => { - const picon = d.icon; - - if (!picon) { - return ''; - } else { - const isMaki = /^maki-/.test(picon); - return `#${picon}${isMaki ? '-11' : ''}`; - } - }); - - // update - markers - .merge(markersEnter) - .sort(sortY) - .classed('selected', d => d.id === selectedID) - .attr('transform', getTransform); - - // Draw targets.. - if (touchLayer.empty()) return; - const fillClass = context.getDebug('target') ? 'pink' : 'nocolor'; - - const targets = touchLayer.selectAll('.qaItem.osmose') - .data(data, d => d.id); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('rect') - .attr('width', '20px') - .attr('height', '30px') - .attr('x', '-10px') - .attr('y', '-28px') - .merge(targets) - .sort(sortY) - .attr('class', d => `qaItem ${d.service} target ${fillClass} itemId-${d.id}`) - .attr('transform', getTransform); - - function sortY(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : b.loc[1] - a.loc[1]; - } - } - - // Draw the Osmose layer and schedule loading issues and updating markers. - function drawOsmose(selection) { - const service = getService(); - - const surface = context.surface(); - if (surface && !surface.empty()) { - touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); - } - - drawLayer = selection.selectAll('.layer-osmose') - .data(service ? [0] : []); - - drawLayer.exit() - .remove(); - - drawLayer = drawLayer.enter() - .append('g') - .attr('class', 'layer-osmose') - .style('display', _layerEnabled$2 ? 'block' : 'none') - .merge(drawLayer); - - if (_layerEnabled$2) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - service.loadIssues(projection); - updateMarkers(); - } else { - editOff(); - } - } - } - - // Toggles the layer on and off - drawOsmose.enabled = function(val) { - if (!arguments.length) return _layerEnabled$2; - - _layerEnabled$2 = val; - if (_layerEnabled$2) { - // Strings supplied by Osmose fetched before showing layer for first time - // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented - // Also, If layer is toggled quickly multiple requests are sent - getService().loadStrings() - .then(layerOn) - .catch(err => { - console.log(err); // eslint-disable-line no-console - }); - } else { - layerOff(); - if (context.selectedErrorID()) { - context.enter(modeBrowse(context)); - } - } - - dispatch.call('change'); - return this; - }; - - drawOsmose.supported = () => !!getService(); - - return drawOsmose; - } - - function svgStreetside(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var minZoom = 14; - var minMarkerZoom = 16; - var minViewfieldZoom = 18; - var layer = select(null); - var _viewerYaw = 0; - var _selectedSequence = null; - var _streetside; - - /** - * init(). - */ - function init() { - if (svgStreetside.initialized) return; // run once - svgStreetside.enabled = false; - svgStreetside.initialized = true; - } - - /** - * getService(). - */ - function getService() { - if (services.streetside && !_streetside) { - _streetside = services.streetside; - _streetside.event - .on('viewerChanged.svgStreetside', viewerChanged) - .on('loadedBubbles.svgStreetside', throttledRedraw); - } else if (!services.streetside && _streetside) { - _streetside = null; - } - - return _streetside; - } - - /** - * showLayer(). - */ - function showLayer() { - var service = getService(); - if (!service) return; - - editOn(); - - layer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end', function () { dispatch.call('change'); }); - } - - /** - * hideLayer(). - */ - function hideLayer() { - throttledRedraw.cancel(); - - layer - .transition() - .duration(250) - .style('opacity', 0) - .on('end', editOff); - } - - /** - * editOn(). - */ - function editOn() { - layer.style('display', 'block'); - } - - /** - * editOff(). - */ - function editOff() { - layer.selectAll('.viewfield-group').remove(); - layer.style('display', 'none'); - } - - /** - * click() Handles 'bubble' point click event. - */ - function click(d) { - var service = getService(); - if (!service) return; - - // try to preserve the viewer rotation when staying on the same sequence - if (d.sequenceKey !== _selectedSequence) { - _viewerYaw = 0; // reset - } - _selectedSequence = d.sequenceKey; - - service - .selectImage(context, d) - .then(response => { - if (response.status === 'ok'){ - service.showViewer(context, _viewerYaw); - } - }); - - - context.map().centerEase(d.loc); - } - - /** - * mouseover(). - */ - function mouseover(d) { - var service = getService(); - if (service) service.setStyles(context, d); - } - - /** - * mouseout(). - */ - function mouseout() { - var service = getService(); - if (service) service.setStyles(context, null); - } - - /** - * transform(). - */ - function transform(d) { - var t = svgPointTransform(projection)(d); - var rot = d.ca + _viewerYaw; - if (rot) { - t += ' rotate(' + Math.floor(rot) + ',0,0)'; - } - return t; - } - - - function viewerChanged() { - var service = getService(); - if (!service) return; - - var viewer = service.viewer(); - if (!viewer) return; - - // update viewfield rotation - _viewerYaw = viewer.getYaw(); - - // avoid updating if the map is currently transformed - // e.g. during drags or easing. - if (context.map().isTransformed()) return; - - layer.selectAll('.viewfield-group.currentView') - .attr('transform', transform); - } - - - context.photos().on('change.streetside', update); - - /** - * update(). - */ - function update() { - var viewer = context.container().select('.photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - var z = ~~context.map().zoom(); - var showMarkers = (z >= minMarkerZoom); - var showViewfields = (z >= minViewfieldZoom); - var service = getService(); - - var sequences = []; - var bubbles = []; - - if (context.photos().showsPanoramic()) { - sequences = (service ? service.sequences(projection) : []); - bubbles = (service && showMarkers ? service.bubbles(projection) : []); - } - - var traces = layer.selectAll('.sequences').selectAll('.sequence') - .data(sequences, function(d) { return d.properties.key; }); - - // exit - traces.exit() - .remove(); - - // enter/update - traces = traces.enter() - .append('path') - .attr('class', 'sequence') - .merge(traces) - .attr('d', svgPath(projection).geojson); - - - var groups = layer.selectAll('.markers').selectAll('.viewfield-group') - .data(bubbles, function(d) { - // force reenter once bubbles are attached to a sequence - return d.key + (d.sequenceKey ? 'v1' : 'v0'); - }); - - // exit - groups.exit() - .remove(); - - // enter - var groupsEnter = groups.enter() - .append('g') - .attr('class', 'viewfield-group') - .on('mouseenter', mouseover) - .on('mouseleave', mouseout) - .on('click', click); - - groupsEnter - .append('g') - .attr('class', 'viewfield-scale'); - - // update - var markers = groups - .merge(groupsEnter) - .sort(function(a, b) { - return (a === selected) ? 1 - : (b === selected) ? -1 - : b.loc[1] - a.loc[1]; - }) - .attr('transform', transform) - .select('.viewfield-scale'); - - - markers.selectAll('circle') - .data([0]) - .enter() - .append('circle') - .attr('dx', '0') - .attr('dy', '0') - .attr('r', '6'); - - var viewfields = markers.selectAll('.viewfield') - .data(showViewfields ? [0] : []); - - viewfields.exit() - .remove(); - - // viewfields may or may not be drawn... - // but if they are, draw below the circles - viewfields.enter() - .insert('path', 'circle') - .attr('class', 'viewfield') - .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - } - - /** - * drawImages() - * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called. - * 'svgStreetside()' is called from index.js - */ - function drawImages(selection) { - var enabled = svgStreetside.enabled; - var service = getService(); - - layer = selection.selectAll('.layer-streetside-images') - .data(service ? [0] : []); - - layer.exit() - .remove(); - - var layerEnter = layer.enter() - .append('g') - .attr('class', 'layer-streetside-images') - .style('display', enabled ? 'block' : 'none'); - - layerEnter - .append('g') - .attr('class', 'sequences'); - - layerEnter - .append('g') - .attr('class', 'markers'); - - layer = layerEnter - .merge(layer); - - if (enabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - update(); - service.loadBubbles(projection); - } else { - editOff(); - } - } - } - - - /** - * drawImages.enabled(). - */ - drawImages.enabled = function(_) { - if (!arguments.length) return svgStreetside.enabled; - svgStreetside.enabled = _; - if (svgStreetside.enabled) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - /** - * drawImages.supported(). - */ - drawImages.supported = function() { - return !!getService(); - }; - - init(); - - return drawImages; - } - - function svgMapillaryImages(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var minZoom = 12; - var minMarkerZoom = 16; - var minViewfieldZoom = 18; - var layer = select(null); - var _mapillary; - var viewerCompassAngle; - - - function init() { - if (svgMapillaryImages.initialized) return; // run once - svgMapillaryImages.enabled = false; - svgMapillaryImages.initialized = true; - } - - - function getService() { - if (services.mapillary && !_mapillary) { - _mapillary = services.mapillary; - _mapillary.event.on('loadedImages', throttledRedraw); - _mapillary.event.on('bearingChanged', function(e) { - viewerCompassAngle = e; - - // avoid updating if the map is currently transformed - // e.g. during drags or easing. - if (context.map().isTransformed()) return; - - layer.selectAll('.viewfield-group.currentView') - .filter(function(d) { - return d.pano; - }) - .attr('transform', transform); - }); - } else if (!services.mapillary && _mapillary) { - _mapillary = null; - } - - return _mapillary; - } - - - function showLayer() { - var service = getService(); - if (!service) return; - - editOn(); - - layer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end', function () { dispatch.call('change'); }); - } - - - function hideLayer() { - throttledRedraw.cancel(); - - layer - .transition() - .duration(250) - .style('opacity', 0) - .on('end', editOff); - } - - - function editOn() { - layer.style('display', 'block'); - } - - - function editOff() { - layer.selectAll('.viewfield-group').remove(); - layer.style('display', 'none'); - } - - - function click(d) { - var service = getService(); - if (!service) return; - - service - .selectImage(context, d.key) - .updateViewer(context, d.key) - .showViewer(context); - - context.map().centerEase(d.loc); - } - - - function mouseover(d) { - var service = getService(); - if (service) service.setStyles(context, d); - } - - - function mouseout() { - var service = getService(); - if (service) service.setStyles(context, null); - } - - - function transform(d) { - var t = svgPointTransform(projection)(d); - if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) { - t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)'; - } else if (d.ca) { - t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; - } - return t; - } - - context.photos().on('change.mapillary_images', update); - - function filterImages(images) { - var showsPano = context.photos().showsPanoramic(); - var showsFlat = context.photos().showsFlat(); - if (!showsPano || !showsFlat) { - images = images.filter(function(image) { - if (image.pano) return showsPano; - return showsFlat; - }); - } - return images; - } - - function filterSequences(sequences, service) { - var showsPano = context.photos().showsPanoramic(); - var showsFlat = context.photos().showsFlat(); - if (!showsPano || !showsFlat) { - sequences = sequences.filter(function(sequence) { - if (sequence.properties.hasOwnProperty('pano')) { - if (sequence.properties.pano) return showsPano; - return showsFlat; - } else { - // if the sequence doesn't specify pano or not, search its images - var cProps = sequence.properties.coordinateProperties; - if (cProps && cProps.image_keys && cProps.image_keys.length > 0) { - for (var index in cProps.image_keys) { - var imageKey = cProps.image_keys[index]; - var image = service.cachedImage(imageKey); - if (image && image.hasOwnProperty('pano')) { - if (image.pano) return showsPano; - return showsFlat; - } - } - } - } - }); - } - return sequences; - } - - function update() { - - var z = ~~context.map().zoom(); - var showMarkers = (z >= minMarkerZoom); - var showViewfields = (z >= minViewfieldZoom); - - var service = getService(); - var selectedKey = service && service.getSelectedImageKey(); - var sequences = (service ? service.sequences(projection) : []); - var images = (service && showMarkers ? service.images(projection) : []); - - images = filterImages(images); - sequences = filterSequences(sequences, service); - - var traces = layer.selectAll('.sequences').selectAll('.sequence') - .data(sequences, function(d) { return d.properties.key; }); - - // exit - traces.exit() - .remove(); - - // enter/update - traces = traces.enter() - .append('path') - .attr('class', 'sequence') - .merge(traces) - .attr('d', svgPath(projection).geojson); - - - var groups = layer.selectAll('.markers').selectAll('.viewfield-group') - .data(images, function(d) { return d.key; }); - - // exit - groups.exit() - .remove(); - - // enter - var groupsEnter = groups.enter() - .append('g') - .attr('class', 'viewfield-group') - .on('mouseenter', mouseover) - .on('mouseleave', mouseout) - .on('click', click); - - groupsEnter - .append('g') - .attr('class', 'viewfield-scale'); - - // update - var markers = groups - .merge(groupsEnter) - .sort(function(a, b) { - return (a.key === selectedKey) ? 1 - : (b.key === selectedKey) ? -1 - : b.loc[1] - a.loc[1]; // sort Y - }) - .attr('transform', transform) - .select('.viewfield-scale'); - - - markers.selectAll('circle') - .data([0]) - .enter() - .append('circle') - .attr('dx', '0') - .attr('dy', '0') - .attr('r', '6'); - - var viewfields = markers.selectAll('.viewfield') - .data(showViewfields ? [0] : []); - - viewfields.exit() - .remove(); - - viewfields.enter() // viewfields may or may not be drawn... - .insert('path', 'circle') // but if they are, draw below the circles - .attr('class', 'viewfield') - .classed('pano', function() { return this.parentNode.__data__.pano; }) - .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - } - - - function drawImages(selection) { - var enabled = svgMapillaryImages.enabled; - var service = getService(); - - layer = selection.selectAll('.layer-mapillary') - .data(service ? [0] : []); - - layer.exit() - .remove(); - - var layerEnter = layer.enter() - .append('g') - .attr('class', 'layer-mapillary') - .style('display', enabled ? 'block' : 'none'); - - layerEnter - .append('g') - .attr('class', 'sequences'); - - layerEnter - .append('g') - .attr('class', 'markers'); - - layer = layerEnter - .merge(layer); - - if (enabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - update(); - service.loadImages(projection); - } else { - editOff(); - } - } - } - - - drawImages.enabled = function(_) { - if (!arguments.length) return svgMapillaryImages.enabled; - svgMapillaryImages.enabled = _; - if (svgMapillaryImages.enabled) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - - drawImages.supported = function() { - return !!getService(); - }; - - - init(); - return drawImages; - } - - function svgMapillarySigns(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var minZoom = 12; - var layer = select(null); - var _mapillary; - - - function init() { - if (svgMapillarySigns.initialized) return; // run once - svgMapillarySigns.enabled = false; - svgMapillarySigns.initialized = true; - } - - - function getService() { - if (services.mapillary && !_mapillary) { - _mapillary = services.mapillary; - _mapillary.event.on('loadedSigns', throttledRedraw); - } else if (!services.mapillary && _mapillary) { - _mapillary = null; - } - return _mapillary; - } - - - function showLayer() { - var service = getService(); - if (!service) return; - - editOn(); - } - - - function hideLayer() { - throttledRedraw.cancel(); - editOff(); - } - - - function editOn() { - layer.style('display', 'block'); - } - - - function editOff() { - layer.selectAll('.icon-sign').remove(); - layer.style('display', 'none'); - } - - - function click(d) { - var service = getService(); - if (!service) return; - - context.map().centerEase(d.loc); - - var selectedImageKey = service.getSelectedImageKey(); - var imageKey; - - // Pick one of the images the sign was detected in, - // preference given to an image already selected. - d.detections.forEach(function(detection) { - if (!imageKey || selectedImageKey === detection.image_key) { - imageKey = detection.image_key; - } - }); - - service - .selectImage(context, imageKey) - .updateViewer(context, imageKey) - .showViewer(context); - } - - - function update() { - var service = getService(); - var data = (service ? service.signs(projection) : []); - var selectedImageKey = service.getSelectedImageKey(); - var transform = svgPointTransform(projection); - - var signs = layer.selectAll('.icon-sign') - .data(data, function(d) { return d.key; }); - - // exit - signs.exit() - .remove(); - - // enter - var enter = signs.enter() - .append('g') - .attr('class', 'icon-sign icon-detected') - .on('click', click); - - enter - .append('use') - .attr('width', '24px') - .attr('height', '24px') - .attr('x', '-12px') - .attr('y', '-12px') - .attr('xlink:href', function(d) { return '#' + d.value; }); - - enter - .append('rect') - .attr('width', '24px') - .attr('height', '24px') - .attr('x', '-12px') - .attr('y', '-12px'); - - // update - signs - .merge(enter) - .attr('transform', transform) - .classed('currentView', function(d) { - return d.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - }) - .sort(function(a, b) { - var aSelected = a.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - var bSelected = b.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - if (aSelected === bSelected) { - return b.loc[1] - a.loc[1]; // sort Y - } else if (aSelected) { - return 1; - } - return -1; - }); - } - - - function drawSigns(selection) { - var enabled = svgMapillarySigns.enabled; - var service = getService(); - - layer = selection.selectAll('.layer-mapillary-signs') - .data(service ? [0] : []); - - layer.exit() - .remove(); - - layer = layer.enter() - .append('g') - .attr('class', 'layer-mapillary-signs layer-mapillary-detections') - .style('display', enabled ? 'block' : 'none') - .merge(layer); - - if (enabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - update(); - service.loadSigns(projection); - } else { - editOff(); - } - } - } - - - drawSigns.enabled = function(_) { - if (!arguments.length) return svgMapillarySigns.enabled; - svgMapillarySigns.enabled = _; - if (svgMapillarySigns.enabled) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - - drawSigns.supported = function() { - return !!getService(); - }; - - - init(); - return drawSigns; - } - - function svgMapillaryMapFeatures(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var minZoom = 12; - var layer = select(null); - var _mapillary; - - - function init() { - if (svgMapillaryMapFeatures.initialized) return; // run once - svgMapillaryMapFeatures.enabled = false; - svgMapillaryMapFeatures.initialized = true; - } - - - function getService() { - if (services.mapillary && !_mapillary) { - _mapillary = services.mapillary; - _mapillary.event.on('loadedMapFeatures', throttledRedraw); - } else if (!services.mapillary && _mapillary) { - _mapillary = null; - } - return _mapillary; - } - - - function showLayer() { - var service = getService(); - if (!service) return; - - editOn(); - } - - - function hideLayer() { - throttledRedraw.cancel(); - editOff(); - } - - - function editOn() { - layer.style('display', 'block'); - } - - - function editOff() { - layer.selectAll('.icon-map-feature').remove(); - layer.style('display', 'none'); - } - - - function click(d) { - var service = getService(); - if (!service) return; - - context.map().centerEase(d.loc); - - var selectedImageKey = service.getSelectedImageKey(); - var imageKey; - - // Pick one of the images the map feature was detected in, - // preference given to an image already selected. - d.detections.forEach(function(detection) { - if (!imageKey || selectedImageKey === detection.image_key) { - imageKey = detection.image_key; - } - }); - - service - .selectImage(context, imageKey) - .updateViewer(context, imageKey) - .showViewer(context); - } - - - function update() { - var service = getService(); - var data = (service ? service.mapFeatures(projection) : []); - var selectedImageKey = service && service.getSelectedImageKey(); - var transform = svgPointTransform(projection); - - var mapFeatures = layer.selectAll('.icon-map-feature') - .data(data, function(d) { return d.key; }); - - // exit - mapFeatures.exit() - .remove(); - - // enter - var enter = mapFeatures.enter() - .append('g') - .attr('class', 'icon-map-feature icon-detected') - .on('click', click); - - enter - .append('title') - .text(function(d) { - var id = d.value.replace(/--/g, '.').replace(/-/g, '_'); - return _t('mapillary_map_features.' + id); - }); - - enter - .append('use') - .attr('width', '24px') - .attr('height', '24px') - .attr('x', '-12px') - .attr('y', '-12px') - .attr('xlink:href', function(d) { - if (d.value === 'object--billboard') { - // no billboard icon right now, so use the advertisement icon - return '#object--sign--advertisement'; - } - return '#' + d.value; - }); - - enter - .append('rect') - .attr('width', '24px') - .attr('height', '24px') - .attr('x', '-12px') - .attr('y', '-12px'); - - // update - mapFeatures - .merge(enter) - .attr('transform', transform) - .classed('currentView', function(d) { - return d.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - }) - .sort(function(a, b) { - var aSelected = a.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - var bSelected = b.detections.some(function(detection) { - return detection.image_key === selectedImageKey; - }); - if (aSelected === bSelected) { - return b.loc[1] - a.loc[1]; // sort Y - } else if (aSelected) { - return 1; - } - return -1; - }); - } - - - function drawMapFeatures(selection) { - var enabled = svgMapillaryMapFeatures.enabled; - var service = getService(); - - layer = selection.selectAll('.layer-mapillary-map-features') - .data(service ? [0] : []); - - layer.exit() - .remove(); - - layer = layer.enter() - .append('g') - .attr('class', 'layer-mapillary-map-features layer-mapillary-detections') - .style('display', enabled ? 'block' : 'none') - .merge(layer); - - if (enabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - update(); - service.loadMapFeatures(projection); - } else { - editOff(); - } - } - } - - - drawMapFeatures.enabled = function(_) { - if (!arguments.length) return svgMapillaryMapFeatures.enabled; - svgMapillaryMapFeatures.enabled = _; - if (svgMapillaryMapFeatures.enabled) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - - drawMapFeatures.supported = function() { - return !!getService(); - }; - - - init(); - return drawMapFeatures; - } - - function svgOpenstreetcamImages(projection, context, dispatch) { - var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000); - var minZoom = 12; - var minMarkerZoom = 16; - var minViewfieldZoom = 18; - var layer = select(null); - var _openstreetcam; - - - function init() { - if (svgOpenstreetcamImages.initialized) return; // run once - svgOpenstreetcamImages.enabled = false; - svgOpenstreetcamImages.initialized = true; - } - - - function getService() { - if (services.openstreetcam && !_openstreetcam) { - _openstreetcam = services.openstreetcam; - _openstreetcam.event.on('loadedImages', throttledRedraw); - } else if (!services.openstreetcam && _openstreetcam) { - _openstreetcam = null; - } - - return _openstreetcam; - } - - - function showLayer() { - var service = getService(); - if (!service) return; - - editOn(); - - layer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end', function () { dispatch.call('change'); }); - } - - - function hideLayer() { - throttledRedraw.cancel(); - - layer - .transition() - .duration(250) - .style('opacity', 0) - .on('end', editOff); - } - - - function editOn() { - layer.style('display', 'block'); - } - - - function editOff() { - layer.selectAll('.viewfield-group').remove(); - layer.style('display', 'none'); - } - - - function click(d) { - var service = getService(); - if (!service) return; - - service - .selectImage(context, d) - .updateViewer(context, d) - .showViewer(context); - - context.map().centerEase(d.loc); - } - - - function mouseover(d) { - var service = getService(); - if (service) service.setStyles(context, d); - } - - - function mouseout() { - var service = getService(); - if (service) service.setStyles(context, null); - } - - - function transform(d) { - var t = svgPointTransform(projection)(d); - if (d.ca) { - t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; - } - return t; - } - - - context.photos().on('change.openstreetcam_images', update); - - function update() { - var viewer = context.container().select('.photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - - var z = ~~context.map().zoom(); - var showMarkers = (z >= minMarkerZoom); - var showViewfields = (z >= minViewfieldZoom); - - var service = getService(); - var sequences = []; - var images = []; - - if (context.photos().showsFlat()) { - sequences = (service ? service.sequences(projection) : []); - images = (service && showMarkers ? service.images(projection) : []); - } - - var traces = layer.selectAll('.sequences').selectAll('.sequence') - .data(sequences, function(d) { return d.properties.key; }); - - // exit - traces.exit() - .remove(); - - // enter/update - traces = traces.enter() - .append('path') - .attr('class', 'sequence') - .merge(traces) - .attr('d', svgPath(projection).geojson); - - - var groups = layer.selectAll('.markers').selectAll('.viewfield-group') - .data(images, function(d) { return d.key; }); - - // exit - groups.exit() - .remove(); - - // enter - var groupsEnter = groups.enter() - .append('g') - .attr('class', 'viewfield-group') - .on('mouseenter', mouseover) - .on('mouseleave', mouseout) - .on('click', click); - - groupsEnter - .append('g') - .attr('class', 'viewfield-scale'); - - // update - var markers = groups - .merge(groupsEnter) - .sort(function(a, b) { - return (a === selected) ? 1 - : (b === selected) ? -1 - : b.loc[1] - a.loc[1]; // sort Y - }) - .attr('transform', transform) - .select('.viewfield-scale'); - - - markers.selectAll('circle') - .data([0]) - .enter() - .append('circle') - .attr('dx', '0') - .attr('dy', '0') - .attr('r', '6'); - - var viewfields = markers.selectAll('.viewfield') - .data(showViewfields ? [0] : []); - - viewfields.exit() - .remove(); - - viewfields.enter() // viewfields may or may not be drawn... - .insert('path', 'circle') // but if they are, draw below the circles - .attr('class', 'viewfield') - .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') - .attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'); - } - - - function drawImages(selection) { - var enabled = svgOpenstreetcamImages.enabled, - service = getService(); - - layer = selection.selectAll('.layer-openstreetcam') - .data(service ? [0] : []); - - layer.exit() - .remove(); - - var layerEnter = layer.enter() - .append('g') - .attr('class', 'layer-openstreetcam') - .style('display', enabled ? 'block' : 'none'); - - layerEnter - .append('g') - .attr('class', 'sequences'); - - layerEnter - .append('g') - .attr('class', 'markers'); - - layer = layerEnter - .merge(layer); - - if (enabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - update(); - service.loadImages(projection); - } else { - editOff(); - } - } - } - - - drawImages.enabled = function(_) { - if (!arguments.length) return svgOpenstreetcamImages.enabled; - svgOpenstreetcamImages.enabled = _; - if (svgOpenstreetcamImages.enabled) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - - drawImages.supported = function() { - return !!getService(); - }; - - - init(); - return drawImages; - } - - function svgOsm(projection, context, dispatch) { - var enabled = true; - - - function drawOsm(selection) { - selection.selectAll('.layer-osm') - .data(['covered', 'areas', 'lines', 'points', 'labels']) - .enter() - .append('g') - .attr('class', function(d) { return 'layer-osm ' + d; }); - - selection.selectAll('.layer-osm.points').selectAll('.points-group') - .data(['points', 'midpoints', 'vertices', 'turns']) - .enter() - .append('g') - .attr('class', function(d) { return 'points-group ' + d; }); - } - - - function showLayer() { - var layer = context.surface().selectAll('.data-layer.osm'); - layer.interrupt(); - - layer - .classed('disabled', false) - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', function () { - dispatch.call('change'); - }); - } - - - function hideLayer() { - var layer = context.surface().selectAll('.data-layer.osm'); - layer.interrupt(); - - layer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', function () { - layer.classed('disabled', true); - dispatch.call('change'); - }); - } - - - drawOsm.enabled = function(val) { - if (!arguments.length) return enabled; - enabled = val; - - if (enabled) { - showLayer(); - } else { - hideLayer(); - } - - dispatch.call('change'); - return this; - }; - - - return drawOsm; - } - - var _notesEnabled = false; - var _osmService; - - - function svgNotes(projection, context, dispatch$1) { - if (!dispatch$1) { dispatch$1 = dispatch('change'); } - var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000); - var minZoom = 12; - var touchLayer = select(null); - var drawLayer = select(null); - var _notesVisible = false; - - - function markerPath(selection, klass) { - selection - .attr('class', klass) - .attr('transform', 'translate(-8, -22)') - .attr('d', 'm17.5,0l-15,0c-1.37,0 -2.5,1.12 -2.5,2.5l0,11.25c0,1.37 1.12,2.5 2.5,2.5l3.75,0l0,3.28c0,0.38 0.43,0.6 0.75,0.37l4.87,-3.65l5.62,0c1.37,0 2.5,-1.12 2.5,-2.5l0,-11.25c0,-1.37 -1.12,-2.5 -2.5,-2.5z'); - } - - - // Loosely-coupled osm service for fetching notes. - function getService() { - if (services.osm && !_osmService) { - _osmService = services.osm; - _osmService.on('loadedNotes', throttledRedraw); - } else if (!services.osm && _osmService) { - _osmService = null; - } - - return _osmService; - } - - - // Show the notes - function editOn() { - if (!_notesVisible) { - _notesVisible = true; - drawLayer - .style('display', 'block'); - } - } - - - // Immediately remove the notes and their touch targets - function editOff() { - if (_notesVisible) { - _notesVisible = false; - drawLayer - .style('display', 'none'); - drawLayer.selectAll('.note') - .remove(); - touchLayer.selectAll('.note') - .remove(); - } - } - - - // Enable the layer. This shows the notes and transitions them to visible. - function layerOn() { - editOn(); - - drawLayer - .style('opacity', 0) - .transition() - .duration(250) - .style('opacity', 1) - .on('end interrupt', function () { - dispatch$1.call('change'); - }); - } - - - // Disable the layer. This transitions the layer invisible and then hides the notes. - function layerOff() { - throttledRedraw.cancel(); - drawLayer.interrupt(); - touchLayer.selectAll('.note') - .remove(); - - drawLayer - .transition() - .duration(250) - .style('opacity', 0) - .on('end interrupt', function () { - editOff(); - dispatch$1.call('change'); - }); - } - - - // Update the note markers - function updateMarkers() { - if (!_notesVisible || !_notesEnabled) return; - - var service = getService(); - var selectedID = context.selectedNoteID(); - var data = (service ? service.notes(projection) : []); - var getTransform = svgPointTransform(projection); - - // Draw markers.. - var notes = drawLayer.selectAll('.note') - .data(data, function(d) { return d.status + d.id; }); - - // exit - notes.exit() - .remove(); - - // enter - var notesEnter = notes.enter() - .append('g') - .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; }) - .classed('new', function(d) { return d.id < 0; }); - - notesEnter - .append('ellipse') - .attr('cx', 0.5) - .attr('cy', 1) - .attr('rx', 6.5) - .attr('ry', 3) - .attr('class', 'stroke'); - - notesEnter - .append('path') - .call(markerPath, 'shadow'); - - notesEnter - .append('use') - .attr('class', 'note-fill') - .attr('width', '20px') - .attr('height', '20px') - .attr('x', '-8px') - .attr('y', '-22px') - .attr('xlink:href', '#iD-icon-note'); - - notesEnter.selectAll('.icon-annotation') - .data(function(d) { return [d]; }) - .enter() - .append('use') - .attr('class', 'icon-annotation') - .attr('width', '10px') - .attr('height', '10px') - .attr('x', '-3px') - .attr('y', '-19px') - .attr('xlink:href', function(d) { - return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply')); - }); - - // update - notes - .merge(notesEnter) - .sort(sortY) - .classed('selected', function(d) { - var mode = context.mode(); - var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging - return !isMoving && d.id === selectedID; - }) - .attr('transform', getTransform); - - - // Draw targets.. - if (touchLayer.empty()) return; - var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - - var targets = touchLayer.selectAll('.note') - .data(data, function(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('rect') - .attr('width', '20px') - .attr('height', '20px') - .attr('x', '-8px') - .attr('y', '-22px') - .merge(targets) - .sort(sortY) - .attr('class', function(d) { - var newClass = (d.id < 0 ? 'new' : ''); - return 'note target note-' + d.id + ' ' + fillClass + newClass; - }) - .attr('transform', getTransform); - - - function sortY(a, b) { - return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1]; - } - } - - - // Draw the notes layer and schedule loading notes and updating markers. - function drawNotes(selection) { - var service = getService(); - - var surface = context.surface(); - if (surface && !surface.empty()) { - touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); - } - - drawLayer = selection.selectAll('.layer-notes') - .data(service ? [0] : []); - - drawLayer.exit() - .remove(); - - drawLayer = drawLayer.enter() - .append('g') - .attr('class', 'layer-notes') - .style('display', _notesEnabled ? 'block' : 'none') - .merge(drawLayer); - - if (_notesEnabled) { - if (service && ~~context.map().zoom() >= minZoom) { - editOn(); - service.loadNotes(projection); - updateMarkers(); - } else { - editOff(); - } - } - } - - - // Toggles the layer on and off - drawNotes.enabled = function(val) { - if (!arguments.length) return _notesEnabled; - - _notesEnabled = val; - if (_notesEnabled) { - layerOn(); - } else { - layerOff(); - if (context.selectedNoteID()) { - context.enter(modeBrowse(context)); - } - } - - dispatch$1.call('change'); - return this; - }; - - - return drawNotes; - } - - function svgTouch() { - - function drawTouch(selection) { - selection.selectAll('.layer-touch') - .data(['areas', 'lines', 'points', 'turns', 'markers']) - .enter() - .append('g') - .attr('class', function(d) { return 'layer-touch ' + d; }); - } - - return drawTouch; - } - - function refresh(selection, node) { - var cr = node.getBoundingClientRect(); - var prop = [cr.width, cr.height]; - selection.property('__dimensions__', prop); - return prop; - } - - function utilGetDimensions(selection, force) { - if (!selection || selection.empty()) { - return [0, 0]; - } - var node = selection.node(), - cached = selection.property('__dimensions__'); - return (!cached || force) ? refresh(selection, node) : cached; - } - - - function utilSetDimensions(selection, dimensions) { - if (!selection || selection.empty()) { - return selection; - } - var node = selection.node(); - if (dimensions === null) { - refresh(selection, node); - return selection; - } - return selection - .property('__dimensions__', [dimensions[0], dimensions[1]]) - .attr('width', dimensions[0]) - .attr('height', dimensions[1]); - } - - function svgLayers(projection, context) { - var dispatch$1 = dispatch('change'); - var svg = select(null); - var _layers = [ - { id: 'osm', layer: svgOsm(projection, context, dispatch$1) }, - { id: 'notes', layer: svgNotes(projection, context, dispatch$1) }, - { id: 'data', layer: svgData(projection, context, dispatch$1) }, - { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) }, - { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) }, - { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) }, - { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)}, - { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) }, - { id: 'mapillary-map-features', layer: svgMapillaryMapFeatures(projection, context, dispatch$1) }, - { id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch$1) }, - { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) }, - { id: 'debug', layer: svgDebug(projection, context) }, - { id: 'geolocate', layer: svgGeolocate(projection) }, - { id: 'touch', layer: svgTouch() } - ]; - - - function drawLayers(selection) { - svg = selection.selectAll('.surface') - .data([0]); - - svg = svg.enter() - .append('svg') - .attr('class', 'surface') - .merge(svg); - - var defs = svg.selectAll('.surface-defs') - .data([0]); - - defs.enter() - .append('defs') - .attr('class', 'surface-defs'); - - var groups = svg.selectAll('.data-layer') - .data(_layers); - - groups.exit() - .remove(); - - groups.enter() - .append('g') - .attr('class', function(d) { return 'data-layer ' + d.id; }) - .merge(groups) - .each(function(d) { select(this).call(d.layer); }); - } - - - drawLayers.all = function() { - return _layers; - }; - - - drawLayers.layer = function(id) { - var obj = _layers.find(function(o) { return o.id === id; }); - return obj && obj.layer; - }; - - - drawLayers.only = function(what) { - var arr = [].concat(what); - var all = _layers.map(function(layer) { return layer.id; }); - return drawLayers.remove(utilArrayDifference(all, arr)); - }; - - - drawLayers.remove = function(what) { - var arr = [].concat(what); - arr.forEach(function(id) { - _layers = _layers.filter(function(o) { return o.id !== id; }); - }); - dispatch$1.call('change'); - return this; - }; - - - drawLayers.add = function(what) { - var arr = [].concat(what); - arr.forEach(function(obj) { - if ('id' in obj && 'layer' in obj) { - _layers.push(obj); - } - }); - dispatch$1.call('change'); - return this; - }; - - - drawLayers.dimensions = function(val) { - if (!arguments.length) return utilGetDimensions(svg); - utilSetDimensions(svg, val); - return this; - }; - - - return utilRebind(drawLayers, dispatch$1, 'on'); - } - - function svgLines(projection, context) { - var detected = utilDetect(); - - var highway_stack = { - motorway: 0, - motorway_link: 1, - trunk: 2, - trunk_link: 3, - primary: 4, - primary_link: 5, - secondary: 6, - tertiary: 7, - unclassified: 8, - residential: 9, - service: 10, - footway: 11 - }; - - - function drawTargets(selection, graph, entities, filter) { - var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; - var getPath = svgPath(projection).geojson; - var activeID = context.activeID(); - var base = context.history().base(); - - // The targets and nopes will be MultiLineString sub-segments of the ways - var data = { targets: [], nopes: [] }; - - entities.forEach(function(way) { - var features = svgSegmentWay(way, graph, activeID); - data.targets.push.apply(data.targets, features.passive); - data.nopes.push.apply(data.nopes, features.active); - }); - - - // Targets allow hover and vertex snapping - var targetData = data.targets.filter(getPath); - var targets = selection.selectAll('.line.target-allowed') - .filter(function(d) { return filter(d.properties.entity); }) - .data(targetData, function key(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - var segmentWasEdited = function(d) { - var wayID = d.properties.entity.id; - // if the whole line was edited, don't draw segment changes - if (!base.entities[wayID] || - !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) { - return false; - } - return d.properties.nodes.some(function(n) { - return !base.entities[n.id] || - !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc); - }); - }; - - // enter/update - targets.enter() - .append('path') - .merge(targets) - .attr('d', getPath) - .attr('class', function(d) { - return 'way line target target-allowed ' + targetClass + d.id; - }) - .classed('segment-edited', segmentWasEdited); - - // NOPE - var nopeData = data.nopes.filter(getPath); - var nopes = selection.selectAll('.line.target-nope') - .filter(function(d) { return filter(d.properties.entity); }) - .data(nopeData, function key(d) { return d.id; }); - - // exit - nopes.exit() - .remove(); - - // enter/update - nopes.enter() - .append('path') - .merge(nopes) - .attr('d', getPath) - .attr('class', function(d) { - return 'way line target target-nope ' + nopeClass + d.id; - }) - .classed('segment-edited', segmentWasEdited); - } - - - function drawLines(selection, graph, entities, filter) { - var base = context.history().base(); - - function waystack(a, b) { - var selected = context.selectedIDs(); - var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0; - var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0; - - if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; } - if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; } - return scoreA - scoreB; - } - - - function drawLineGroup(selection, klass, isSelected) { - // Note: Don't add `.selected` class in draw modes - var mode = context.mode(); - var isDrawing = mode && /^draw/.test(mode.id); - var selectedClass = (!isDrawing && isSelected) ? 'selected ' : ''; - - var lines = selection - .selectAll('path') - .filter(filter) - .data(getPathData(isSelected), osmEntity.key); - - lines.exit() - .remove(); - - // Optimization: Call expensive TagClasses only on enter selection. This - // works because osmEntity.key is defined to include the entity v attribute. - lines.enter() - .append('path') - .attr('class', function(d) { - - var prefix = 'way line'; - - // if this line isn't styled by its own tags - if (!d.hasInterestingTags()) { - - var parentRelations = graph.parentRelations(d); - var parentMultipolygons = parentRelations.filter(function(relation) { - return relation.isMultipolygon(); - }); - - // and if it's a member of at least one multipolygon relation - if (parentMultipolygons.length > 0 && - // and only multipolygon relations - parentRelations.length === parentMultipolygons.length) { - // then fudge the classes to style this as an area edge - prefix = 'relation area'; - } - } - - var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : ''; - return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id; - }) - .classed('added', function(d) { - return !base.entities[d.id]; - }) - .classed('geometry-edited', function(d) { - return graph.entities[d.id] && - base.entities[d.id] && - !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes); - }) - .classed('retagged', function(d) { - return graph.entities[d.id] && - base.entities[d.id] && - !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); - }) - .call(svgTagClasses()) - .merge(lines) - .sort(waystack) - .attr('d', getPath) - .call(svgTagClasses().tags(svgRelationMemberTags(graph))); - - return selection; - } - - - function getPathData(isSelected) { - return function() { - var layer = this.parentNode.__data__; - var data = pathdata[layer] || []; - return data.filter(function(d) { - if (isSelected) - return context.selectedIDs().indexOf(d.id) !== -1; - else - return context.selectedIDs().indexOf(d.id) === -1; - }); - }; - } - - function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) { - var markergroup = layergroup - .selectAll('g.' + groupclass) - .data([pathclass]); - - markergroup = markergroup.enter() - .append('g') - .attr('class', groupclass) - .merge(markergroup); - - var markers = markergroup - .selectAll('path') - .filter(filter) - .data( - function data() { return groupdata[this.parentNode.__data__] || []; }, - function key(d) { return [d.id, d.index]; } - ); - - markers.exit() - .remove(); - - markers = markers.enter() - .append('path') - .attr('class', pathclass) - .merge(markers) - .attr('marker-mid', marker) - .attr('d', function(d) { return d.d; }); - - if (detected.ie) { - markers.each(function() { this.parentNode.insertBefore(this, this); }); - } - } - - - var getPath = svgPath(projection, graph); - var ways = []; - var onewaydata = {}; - var sideddata = {}; - var oldMultiPolygonOuters = {}; - - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - var outer = osmOldMultipolygonOuterMember(entity, graph); - if (outer) { - ways.push(entity.mergeTags(outer.tags)); - oldMultiPolygonOuters[outer.id] = true; - } else if (entity.geometry(graph) === 'line') { - ways.push(entity); - } - } - - ways = ways.filter(getPath); - var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); }); - - Object.keys(pathdata).forEach(function(k) { - var v = pathdata[k]; - var onewayArr = v.filter(function(d) { return d.isOneWay(); }); - var onewaySegments = svgMarkerSegments( - projection, graph, 35, - function shouldReverse(entity) { return entity.tags.oneway === '-1'; }, - function bothDirections(entity) { - return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating'; - } - ); - onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments)); - - var sidedArr = v.filter(function(d) { return d.isSided(); }); - var sidedSegments = svgMarkerSegments( - projection, graph, 30, - function shouldReverse() { return false; }, - function bothDirections() { return false; } - ); - sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments)); - }); - - - var covered = selection.selectAll('.layer-osm.covered'); // under areas - var uncovered = selection.selectAll('.layer-osm.lines'); // over areas - var touchLayer = selection.selectAll('.layer-touch.lines'); - - // Draw lines.. - [covered, uncovered].forEach(function(selection) { - var range = (selection === covered ? range$1(-10,0) : range$1(0,11)); - var layergroup = selection - .selectAll('g.layergroup') - .data(range); - - layergroup = layergroup.enter() - .append('g') - .attr('class', function(d) { return 'layergroup layer' + String(d); }) - .merge(layergroup); - - layergroup - .selectAll('g.linegroup') - .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']) - .enter() - .append('g') - .attr('class', function(d) { return 'linegroup line-' + d; }); - - layergroup.selectAll('g.line-shadow') - .call(drawLineGroup, 'shadow', false); - layergroup.selectAll('g.line-casing') - .call(drawLineGroup, 'casing', false); - layergroup.selectAll('g.line-stroke') - .call(drawLineGroup, 'stroke', false); - - layergroup.selectAll('g.line-shadow-highlighted') - .call(drawLineGroup, 'shadow', true); - layergroup.selectAll('g.line-casing-highlighted') - .call(drawLineGroup, 'casing', true); - layergroup.selectAll('g.line-stroke-highlighted') - .call(drawLineGroup, 'stroke', true); - - addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)'); - addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, - function marker(d) { - var category = graph.entity(d.id).sidednessIdentifier(); - return 'url(#ideditor-sided-marker-' + category + ')'; - } - ); - }); - - // Draw touch targets.. - touchLayer - .call(drawTargets, graph, ways, filter); - } - - - return drawLines; - } - - function svgMidpoints(projection, context) { - var targetRadius = 8; - - function drawTargets(selection, graph, entities, filter) { - var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - var getTransform = svgPointTransform(projection).geojson; - - var data = entities.map(function(midpoint) { - return { - type: 'Feature', - id: midpoint.id, - properties: { - target: true, - entity: midpoint - }, - geometry: { - type: 'Point', - coordinates: midpoint.loc - } - }; - }); - - var targets = selection.selectAll('.midpoint.target') - .filter(function(d) { return filter(d.properties.entity); }) - .data(data, function key(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('circle') - .attr('r', targetRadius) - .merge(targets) - .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; }) - .attr('transform', getTransform); - } - - - function drawMidpoints(selection, graph, entities, filter, extent) { - var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints'); - var touchLayer = selection.selectAll('.layer-touch.points'); - - var mode = context.mode(); - if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) { - drawLayer.selectAll('.midpoint').remove(); - touchLayer.selectAll('.midpoint.target').remove(); - return; - } - - var poly = extent.polygon(); - var midpoints = {}; - - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - - if (entity.type !== 'way') continue; - if (!filter(entity)) continue; - if (context.selectedIDs().indexOf(entity.id) < 0) continue; - - var nodes = graph.childNodes(entity); - for (var j = 0; j < nodes.length - 1; j++) { - var a = nodes[j]; - var b = nodes[j + 1]; - var id = [a.id, b.id].sort().join('-'); - - if (midpoints[id]) { - midpoints[id].parents.push(entity); - } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) { - var point = geoVecInterp(a.loc, b.loc, 0.5); - var loc = null; - - if (extent.intersects(point)) { - loc = point; - } else { - for (var k = 0; k < 4; k++) { - point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]); - if (point && - geoVecLength(projection(a.loc), projection(point)) > 20 && - geoVecLength(projection(b.loc), projection(point)) > 20) - { - loc = point; - break; - } - } - } - - if (loc) { - midpoints[id] = { - type: 'midpoint', - id: id, - loc: loc, - edge: [a.id, b.id], - parents: [entity] - }; - } - } - } - } - - - function midpointFilter(d) { - if (midpoints[d.id]) - return true; - - for (var i = 0; i < d.parents.length; i++) { - if (filter(d.parents[i])) { - return true; - } - } - - return false; - } - - - var groups = drawLayer.selectAll('.midpoint') - .filter(midpointFilter) - .data(Object.values(midpoints), function(d) { return d.id; }); - - groups.exit() - .remove(); - - var enter = groups.enter() - .insert('g', ':first-child') - .attr('class', 'midpoint'); - - enter - .append('polygon') - .attr('points', '-6,8 10,0 -6,-8') - .attr('class', 'shadow'); - - enter - .append('polygon') - .attr('points', '-3,4 5,0 -3,-4') - .attr('class', 'fill'); - - groups = groups - .merge(enter) - .attr('transform', function(d) { - var translate = svgPointTransform(projection); - var a = graph.entity(d.edge[0]); - var b = graph.entity(d.edge[1]); - var angle = geoAngle(a, b, projection) * (180 / Math.PI); - return translate(d) + ' rotate(' + angle + ')'; - }) - .call(svgTagClasses().tags( - function(d) { return d.parents[0].tags; } - )); - - // Propagate data bindings. - groups.select('polygon.shadow'); - groups.select('polygon.fill'); - - - // Draw touch targets.. - touchLayer - .call(drawTargets, graph, Object.values(midpoints), midpointFilter); - } - - return drawMidpoints; - } - - function svgPoints(projection, context) { - - function markerPath(selection, klass) { - selection - .attr('class', klass) - .attr('transform', 'translate(-8, -23)') - .attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z'); - } - - function sortY(a, b) { - return b.loc[1] - a.loc[1]; - } - - - // Avoid exit/enter if we're just moving stuff around. - // The node will get a new version but we only need to run the update selection. - function fastEntityKey(d) { - var mode = context.mode(); - var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); - return isMoving ? d.id : osmEntity.key(d); - } - - - function drawTargets(selection, graph, entities, filter) { - var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - var getTransform = svgPointTransform(projection).geojson; - var activeID = context.activeID(); - var data = []; - - entities.forEach(function(node) { - if (activeID === node.id) return; // draw no target on the activeID - - data.push({ - type: 'Feature', - id: node.id, - properties: { - target: true, - entity: node - }, - geometry: node.asGeoJSON() - }); - }); - - var targets = selection.selectAll('.point.target') - .filter(function(d) { return filter(d.properties.entity); }) - .data(data, function key(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('rect') - .attr('x', -10) - .attr('y', -26) - .attr('width', 20) - .attr('height', 30) - .merge(targets) - .attr('class', function(d) { return 'node point target ' + fillClass + d.id; }) - .attr('transform', getTransform); - } - - - function drawPoints(selection, graph, entities, filter) { - var wireframe = context.surface().classed('fill-wireframe'); - var zoom = geoScaleToZoom(projection.scale()); - var base = context.history().base(); - - // Points with a direction will render as vertices at higher zooms.. - function renderAsPoint(entity) { - return entity.geometry(graph) === 'point' && - !(zoom >= 18 && entity.directions(graph, projection).length); - } - - // All points will render as vertices in wireframe mode too.. - var points = wireframe ? [] : entities.filter(renderAsPoint); - points.sort(sortY); - - - var drawLayer = selection.selectAll('.layer-osm.points .points-group.points'); - var touchLayer = selection.selectAll('.layer-touch.points'); - - // Draw points.. - var groups = drawLayer.selectAll('g.point') - .filter(filter) - .data(points, fastEntityKey); - - groups.exit() - .remove(); - - var enter = groups.enter() - .append('g') - .attr('class', function(d) { return 'node point ' + d.id; }) - .order(); - - enter - .append('path') - .call(markerPath, 'shadow'); - - enter - .append('ellipse') - .attr('cx', 0.5) - .attr('cy', 1) - .attr('rx', 6.5) - .attr('ry', 3) - .attr('class', 'stroke'); - - enter - .append('path') - .call(markerPath, 'stroke'); - - enter - .append('use') - .attr('transform', 'translate(-5, -19)') - .attr('class', 'icon') - .attr('width', '11px') - .attr('height', '11px'); - - groups = groups - .merge(enter) - .attr('transform', svgPointTransform(projection)) - .classed('added', function(d) { - return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new - }) - .classed('moved', function(d) { - return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc); - }) - .classed('retagged', function(d) { - return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); - }) - .call(svgTagClasses()); - - groups.select('.shadow'); // propagate bound data - groups.select('.stroke'); // propagate bound data - groups.select('.icon') // propagate bound data - .attr('xlink:href', function(entity) { - var preset = _mainPresetIndex.match(entity, graph); - var picon = preset && preset.icon; - - if (!picon) { - return ''; - } else { - var isMaki = /^maki-/.test(picon); - return '#' + picon + (isMaki ? '-11' : ''); - } - }); - - - // Draw touch targets.. - touchLayer - .call(drawTargets, graph, points, filter); - } - - - return drawPoints; - } - - function svgTurns(projection, context) { - - function icon(turn) { - var u = turn.u ? '-u' : ''; - if (turn.no) return '#iD-turn-no' + u; - if (turn.only) return '#iD-turn-only' + u; - return '#iD-turn-yes' + u; - } - - function drawTurns(selection, graph, turns) { - - function turnTransform(d) { - var pxRadius = 50; - var toWay = graph.entity(d.to.way); - var toPoints = graph.childNodes(toWay) - .map(function (n) { return n.loc; }) - .map(projection); - var toLength = geoPathLength(toPoints); - var mid = toLength / 2; // midpoint of destination way - - var toNode = graph.entity(d.to.node); - var toVertex = graph.entity(d.to.vertex); - var a = geoAngle(toVertex, toNode, projection); - var o = projection(toVertex.loc); - var r = d.u ? 0 // u-turn: no radius - : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius - : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways - - return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + - 'rotate(' + a * 180 / Math.PI + ')'; - } - - - var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns'); - var touchLayer = selection.selectAll('.layer-touch.turns'); - - // Draw turns.. - var groups = drawLayer.selectAll('g.turn') - .data(turns, function(d) { return d.key; }); - - // exit - groups.exit() - .remove(); - - // enter - var groupsEnter = groups.enter() - .append('g') - .attr('class', function(d) { return 'turn ' + d.key; }); - - var turnsEnter = groupsEnter - .filter(function(d) { return !d.u; }); - - turnsEnter.append('rect') - .attr('transform', 'translate(-22, -12)') - .attr('width', '44') - .attr('height', '24'); - - turnsEnter.append('use') - .attr('transform', 'translate(-22, -12)') - .attr('width', '44') - .attr('height', '24'); - - var uEnter = groupsEnter - .filter(function(d) { return d.u; }); - - uEnter.append('circle') - .attr('r', '16'); - - uEnter.append('use') - .attr('transform', 'translate(-16, -16)') - .attr('width', '32') - .attr('height', '32'); - - // update - groups = groups - .merge(groupsEnter) - .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; }) - .attr('transform', turnTransform); - - groups.select('use') - .attr('xlink:href', icon); - - groups.select('rect'); // propagate bound data - groups.select('circle'); // propagate bound data - - - // Draw touch targets.. - var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - groups = touchLayer.selectAll('g.turn') - .data(turns, function(d) { return d.key; }); - - // exit - groups.exit() - .remove(); - - // enter - groupsEnter = groups.enter() - .append('g') - .attr('class', function(d) { return 'turn ' + d.key; }); - - turnsEnter = groupsEnter - .filter(function(d) { return !d.u; }); - - turnsEnter.append('rect') - .attr('class', 'target ' + fillClass) - .attr('transform', 'translate(-22, -12)') - .attr('width', '44') - .attr('height', '24'); - - uEnter = groupsEnter - .filter(function(d) { return d.u; }); - - uEnter.append('circle') - .attr('class', 'target ' + fillClass) - .attr('r', '16'); - - // update - groups = groups - .merge(groupsEnter) - .attr('transform', turnTransform); - - groups.select('rect'); // propagate bound data - groups.select('circle'); // propagate bound data - - - return this; - } - - return drawTurns; - } - - function svgVertices(projection, context) { - var radiuses = { - // z16-, z17, z18+, w/icon - shadow: [6, 7.5, 7.5, 12], - stroke: [2.5, 3.5, 3.5, 8], - fill: [1, 1.5, 1.5, 1.5] - }; - - var _currHoverTarget; - var _currPersistent = {}; - var _currHover = {}; - var _prevHover = {}; - var _currSelected = {}; - var _prevSelected = {}; - var _radii = {}; - - - function sortY(a, b) { - return b.loc[1] - a.loc[1]; - } - - // Avoid exit/enter if we're just moving stuff around. - // The node will get a new version but we only need to run the update selection. - function fastEntityKey(d) { - var mode = context.mode(); - var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); - return isMoving ? d.id : osmEntity.key(d); - } - - - function draw(selection, graph, vertices, sets, filter) { - sets = sets || { selected: {}, important: {}, hovered: {} }; - - var icons = {}; - var directions = {}; - var wireframe = context.surface().classed('fill-wireframe'); - var zoom = geoScaleToZoom(projection.scale()); - var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2); - var activeID = context.activeID(); - var base = context.history().base(); - - - function getIcon(d) { - // always check latest entity, as fastEntityKey avoids enter/exit now - var entity = graph.entity(d.id); - if (entity.id in icons) return icons[entity.id]; - - icons[entity.id] = - entity.hasInterestingTags() && - _mainPresetIndex.match(entity, graph).icon; - - return icons[entity.id]; - } - - - // memoize directions results, return false for empty arrays (for use in filter) - function getDirections(entity) { - if (entity.id in directions) return directions[entity.id]; - - var angles = entity.directions(graph, projection); - directions[entity.id] = angles.length ? angles : false; - return angles; - } - - - function updateAttributes(selection) { - ['shadow', 'stroke', 'fill'].forEach(function(klass) { - var rads = radiuses[klass]; - selection.selectAll('.' + klass) - .each(function(entity) { - var i = z && getIcon(entity); - var r = rads[i ? 3 : z]; - - // slightly increase the size of unconnected endpoints #3775 - if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) { - r += 1.5; - } - - if (klass === 'shadow') { // remember this value, so we don't need to - _radii[entity.id] = r; // recompute it when we draw the touch targets - } - - select(this) - .attr('r', r) - .attr('visibility', (i && klass === 'fill') ? 'hidden' : null); - }); - }); - } - - vertices.sort(sortY); - - var groups = selection.selectAll('g.vertex') - .filter(filter) - .data(vertices, fastEntityKey); - - // exit - groups.exit() - .remove(); - - // enter - var enter = groups.enter() - .append('g') - .attr('class', function(d) { return 'node vertex ' + d.id; }) - .order(); - - enter - .append('circle') - .attr('class', 'shadow'); - - enter - .append('circle') - .attr('class', 'stroke'); - - // Vertices with tags get a fill. - enter.filter(function(d) { return d.hasInterestingTags(); }) - .append('circle') - .attr('class', 'fill'); - - // update - groups = groups - .merge(enter) - .attr('transform', svgPointTransform(projection)) - .classed('sibling', function(d) { return d.id in sets.selected; }) - .classed('shared', function(d) { return graph.isShared(d); }) - .classed('endpoint', function(d) { return d.isEndpoint(graph); }) - .classed('added', function(d) { - return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new - }) - .classed('moved', function(d) { - return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc); - }) - .classed('retagged', function(d) { - return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags); - }) - .call(updateAttributes); - - // Vertices with icons get a `use`. - var iconUse = groups - .selectAll('.icon') - .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey); - - // exit - iconUse.exit() - .remove(); - - // enter - iconUse.enter() - .append('use') - .attr('class', 'icon') - .attr('width', '11px') - .attr('height', '11px') - .attr('transform', 'translate(-5.5, -5.5)') - .attr('xlink:href', function(d) { - var picon = getIcon(d); - var isMaki = /^maki-/.test(picon); - return '#' + picon + (isMaki ? '-11' : ''); - }); - - - // Vertices with directions get viewfields - var dgroups = groups - .selectAll('.viewfieldgroup') - .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey); - - // exit - dgroups.exit() - .remove(); - - // enter/update - dgroups = dgroups.enter() - .insert('g', '.shadow') - .attr('class', 'viewfieldgroup') - .merge(dgroups); - - var viewfields = dgroups.selectAll('.viewfield') - .data(getDirections, function key(d) { return osmEntity.key(d); }); - - // exit - viewfields.exit() - .remove(); - - // enter/update - viewfields.enter() - .append('path') - .attr('class', 'viewfield') - .attr('d', 'M0,0H0') - .merge(viewfields) - .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')') - .attr('transform', function(d) { return 'rotate(' + d + ')'; }); - } - - - function drawTargets(selection, graph, entities, filter) { - var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor '; - var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor '; - var getTransform = svgPointTransform(projection).geojson; - var activeID = context.activeID(); - var data = { targets: [], nopes: [] }; - - entities.forEach(function(node) { - if (activeID === node.id) return; // draw no target on the activeID - - var vertexType = svgPassiveVertex(node, graph, activeID); - if (vertexType !== 0) { // passive or adjacent - allow to connect - data.targets.push({ - type: 'Feature', - id: node.id, - properties: { - target: true, - entity: node - }, - geometry: node.asGeoJSON() - }); - } else { - data.nopes.push({ - type: 'Feature', - id: node.id + '-nope', - properties: { - nope: true, - target: true, - entity: node - }, - geometry: node.asGeoJSON() - }); - } - }); - - // Targets allow hover and vertex snapping - var targets = selection.selectAll('.vertex.target-allowed') - .filter(function(d) { return filter(d.properties.entity); }) - .data(data.targets, function key(d) { return d.id; }); - - // exit - targets.exit() - .remove(); - - // enter/update - targets.enter() - .append('circle') - .attr('r', function(d) { - return _radii[d.id] - || radiuses.shadow[3]; - }) - .merge(targets) - .attr('class', function(d) { - return 'node vertex target target-allowed ' - + targetClass + d.id; - }) - .attr('transform', getTransform); - - - // NOPE - var nopes = selection.selectAll('.vertex.target-nope') - .filter(function(d) { return filter(d.properties.entity); }) - .data(data.nopes, function key(d) { return d.id; }); - - // exit - nopes.exit() - .remove(); - - // enter/update - nopes.enter() - .append('circle') - .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); }) - .merge(nopes) - .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; }) - .attr('transform', getTransform); - } - - - // Points can also render as vertices: - // 1. in wireframe mode or - // 2. at higher zooms if they have a direction - function renderAsVertex(entity, graph, wireframe, zoom) { - var geometry = entity.geometry(graph); - return geometry === 'vertex' || (geometry === 'point' && ( - wireframe || (zoom >= 18 && entity.directions(graph, projection).length) - )); - } - - - function isEditedNode(node, base, head) { - var baseNode = base.entities[node.id]; - var headNode = head.entities[node.id]; - return !headNode || - !baseNode || - !fastDeepEqual(headNode.tags, baseNode.tags) || - !fastDeepEqual(headNode.loc, baseNode.loc); - } - - - function getSiblingAndChildVertices(ids, graph, wireframe, zoom) { - var results = {}; - - var seenIds = {}; - - function addChildVertices(entity) { - - // avoid redunant work and infinite recursion of circular relations - if (seenIds[entity.id]) return; - seenIds[entity.id] = true; - - var geometry = entity.geometry(graph); - if (!context.features().isHiddenFeature(entity, graph, geometry)) { - var i; - if (entity.type === 'way') { - for (i = 0; i < entity.nodes.length; i++) { - var child = graph.hasEntity(entity.nodes[i]); - if (child) { - addChildVertices(child); - } - } - } else if (entity.type === 'relation') { - for (i = 0; i < entity.members.length; i++) { - var member = graph.hasEntity(entity.members[i].id); - if (member) { - addChildVertices(member); - } - } - } else if (renderAsVertex(entity, graph, wireframe, zoom)) { - results[entity.id] = entity; - } - } - } - - ids.forEach(function(id) { - var entity = graph.hasEntity(id); - if (!entity) return; - - if (entity.type === 'node') { - if (renderAsVertex(entity, graph, wireframe, zoom)) { - results[entity.id] = entity; - graph.parentWays(entity).forEach(function(entity) { - addChildVertices(entity); - }); - } - } else { // way, relation - addChildVertices(entity); - } - }); - - return results; - } - - - function drawVertices(selection, graph, entities, filter, extent, fullRedraw) { - var wireframe = context.surface().classed('fill-wireframe'); - var visualDiff = context.surface().classed('highlight-edited'); - var zoom = geoScaleToZoom(projection.scale()); - var mode = context.mode(); - var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id); - var base = context.history().base(); - - var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices'); - var touchLayer = selection.selectAll('.layer-touch.points'); - - if (fullRedraw) { - _currPersistent = {}; - _radii = {}; - } - - // Collect important vertices from the `entities` list.. - // (during a paritial redraw, it will not contain everything) - for (var i = 0; i < entities.length; i++) { - var entity = entities[i]; - var geometry = entity.geometry(graph); - var keep = false; - - // a point that looks like a vertex.. - if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) { - _currPersistent[entity.id] = entity; - keep = true; - - // a vertex of some importance.. - } else if (geometry === 'vertex' && - (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) - || (visualDiff && isEditedNode(entity, base, graph)))) { - _currPersistent[entity.id] = entity; - keep = true; - } - - // whatever this is, it's not a persistent vertex.. - if (!keep && !fullRedraw) { - delete _currPersistent[entity.id]; - } - } - - // 3 sets of vertices to consider: - var sets = { - persistent: _currPersistent, // persistent = important vertices (render always) - selected: _currSelected, // selected + siblings of selected (render always) - hovered: _currHover // hovered + siblings of hovered (render only in draw modes) - }; - - var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent); - - // Draw the vertices.. - // The filter function controls the scope of what objects d3 will touch (exit/enter/update) - // Adjust the filter function to expand the scope beyond whatever entities were passed in. - var filterRendered = function(d) { - return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d); - }; - drawLayer - .call(draw, graph, currentVisible(all), sets, filterRendered); - - // Draw touch targets.. - // When drawing, render all targets (not just those affected by a partial redraw) - var filterTouch = function(d) { - return isMoving ? true : filterRendered(d); - }; - touchLayer - .call(drawTargets, graph, currentVisible(all), filterTouch); - - - function currentVisible(which) { - return Object.keys(which) - .map(graph.hasEntity, graph) // the current version of this entity - .filter(function (entity) { return entity && entity.intersects(extent, graph); }); - } - } - - - // partial redraw - only update the selected items.. - drawVertices.drawSelected = function(selection, graph, extent) { - var wireframe = context.surface().classed('fill-wireframe'); - var zoom = geoScaleToZoom(projection.scale()); - - _prevSelected = _currSelected || {}; - if (context.map().isInWideSelection()) { - _currSelected = {}; - context.selectedIDs().forEach(function(id) { - var entity = graph.hasEntity(id); - if (!entity) return; - - if (entity.type === 'node') { - if (renderAsVertex(entity, graph, wireframe, zoom)) { - _currSelected[entity.id] = entity; - } - } - }); - - } else { - _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom); - } - - // note that drawVertices will add `_currSelected` automatically if needed.. - var filter = function(d) { return d.id in _prevSelected; }; - drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false); - }; - - - // partial redraw - only update the hovered items.. - drawVertices.drawHover = function(selection, graph, target, extent) { - if (target === _currHoverTarget) return; // continue only if something changed - - var wireframe = context.surface().classed('fill-wireframe'); - var zoom = geoScaleToZoom(projection.scale()); - - _prevHover = _currHover || {}; - _currHoverTarget = target; - var entity = target && target.properties && target.properties.entity; - - if (entity) { - _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom); - } else { - _currHover = {}; - } - - // note that drawVertices will add `_currHover` automatically if needed.. - var filter = function(d) { return d.id in _prevHover; }; - drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false); - }; - - return drawVertices; - } - - function utilBindOnce(target, type, listener, capture) { - var typeOnce = type + '.once'; - function one() { - target.on(typeOnce, null); - listener.apply(this, arguments); - } - target.on(typeOnce, one, capture); - return this; - } - - // Adapted from d3-zoom to handle pointer events. - - // Ignore right-click, since that should open the context menu. - function defaultFilter$2() { - return !event.ctrlKey && !event.button; - } - - function defaultExtent$1() { - var e = this; - if (e instanceof SVGElement) { - e = e.ownerSVGElement || e; - if (e.hasAttribute('viewBox')) { - e = e.viewBox.baseVal; - return [[e.x, e.y], [e.x + e.width, e.y + e.height]]; - } - return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]; - } - return [[0, 0], [e.clientWidth, e.clientHeight]]; - } - - function defaultWheelDelta$1() { - return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002); - } - - function defaultConstrain$1(transform, extent, translateExtent) { - var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], - dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], - dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], - dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; - return transform.translate( - dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), - dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1) - ); - } - - function utilZoomPan() { - var filter = defaultFilter$2, - extent = defaultExtent$1, - constrain = defaultConstrain$1, - wheelDelta = defaultWheelDelta$1, - scaleExtent = [0, Infinity], - translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], - interpolate = interpolateZoom, - listeners = dispatch('start', 'zoom', 'end'), - _wheelDelay = 150, - _transform = identity$2, - _activeGesture; - - function zoom(selection) { - selection - .on('pointerdown.zoom', pointerdown) - .on('wheel.zoom', wheeled) - .style('touch-action', 'none') - .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); - - select(window) - .on('pointermove.zoompan', pointermove) - .on('pointerup.zoompan pointercancel.zoompan', pointerup); - } - - zoom.transform = function(collection, transform, point) { - var selection = collection.selection ? collection.selection() : collection; - if (collection !== selection) { - schedule(collection, transform, point); - } else { - selection.interrupt().each(function() { - gesture(this, arguments) - .start() - .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform) - .end(); - }); - } - }; - - zoom.scaleBy = function(selection, k, p) { - zoom.scaleTo(selection, function() { - var k0 = _transform.k, - k1 = typeof k === 'function' ? k.apply(this, arguments) : k; - return k0 * k1; - }, p); - }; - - zoom.scaleTo = function(selection, k, p) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t0 = _transform, - p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p, - p1 = t0.invert(p0), - k1 = typeof k === 'function' ? k.apply(this, arguments) : k; - return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); - }, p); - }; - - zoom.translateBy = function(selection, x, y) { - zoom.transform(selection, function() { - return constrain(_transform.translate( - typeof x === 'function' ? x.apply(this, arguments) : x, - typeof y === 'function' ? y.apply(this, arguments) : y - ), extent.apply(this, arguments), translateExtent); - }); - }; - - zoom.translateTo = function(selection, x, y, p) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t = _transform, - p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p; - return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate( - typeof x === 'function' ? -x.apply(this, arguments) : -x, - typeof y === 'function' ? -y.apply(this, arguments) : -y - ), e, translateExtent); - }, p); - }; - - function scale(transform, k) { - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); - return k === transform.k ? transform : new Transform(k, transform.x, transform.y); - } - - function translate(transform, p0, p1) { - var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k; - return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); - } - - function centroid(extent) { - return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; - } - - function schedule(transition, transform, point) { - transition - .on('start.zoom', function() { gesture(this, arguments).start(); }) - .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); }) - .tween('zoom', function() { - var that = this, - args = arguments, - g = gesture(that, args), - e = extent.apply(that, args), - p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point, - w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), - a = _transform, - b = typeof transform === 'function' ? transform.apply(that, args) : transform, - i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); - return function(t) { - if (t === 1) t = b; // Avoid rounding error on end. - else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } - g.zoom(null, t); - }; - }); - } - - function gesture(that, args, clean) { - return (!clean && _activeGesture) || new Gesture(that, args); - } - - function Gesture(that, args) { - this.that = that; - this.args = args; - this.active = 0; - this.extent = extent.apply(that, args); - } - - Gesture.prototype = { - start: function() { - if (++this.active === 1) { - _activeGesture = this; - this.emit('start'); - } - return this; - }, - zoom: function(key, transform) { - if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]); - if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]); - if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]); - _transform = transform; - this.emit('zoom'); - return this; - }, - end: function() { - if (--this.active === 0) { - _activeGesture = null; - this.emit('end'); - } - return this; - }, - emit: function(type) { - customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]); - } - }; - - function wheeled() { - if (!filter.apply(this, arguments)) return; - var g = gesture(this, arguments), - t = _transform, - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), - p = utilFastMouse(this)(event); - - // If the mouse is in the same location as before, reuse it. - // If there were recent wheel events, reset the wheel idle timeout. - if (g.wheel) { - if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { - g.mouse[1] = t.invert(g.mouse[0] = p); - } - clearTimeout(g.wheel); - - // Otherwise, capture the mouse point and location at the start. - } else { - g.mouse = [p, t.invert(p)]; - interrupt(this); - g.start(); - } - - event.preventDefault(); - event.stopImmediatePropagation(); - g.wheel = setTimeout(wheelidled, _wheelDelay); - g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); - - function wheelidled() { - g.wheel = null; - g.end(); - } - } - - var _downPointerIDs = new Set(); - var _pointerLocGetter; - - function pointerdown() { - _downPointerIDs.add(event.pointerId); - - if (!filter.apply(this, arguments)) return; - - var g = gesture(this, arguments, _downPointerIDs.size === 1); - var started; - - event.stopImmediatePropagation(); - _pointerLocGetter = utilFastMouse(this); - var loc = _pointerLocGetter(event); - var p = [loc, _transform.invert(loc), event.pointerId]; - if (!g.pointer0) { - g.pointer0 = p; - started = true; - - } else if (!g.pointer1 && g.pointer0[2] !== p[2]) { - g.pointer1 = p; - } - - if (started) { - interrupt(this); - g.start(); - } - } - - function pointermove() { - if (!_downPointerIDs.has(event.pointerId)) return; - - if (!_activeGesture || !_pointerLocGetter) return; - - var g = gesture(this, arguments); - - var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId; - var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId; - - if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) { - // The pointer went up without ending the gesture somehow, e.g. - // a down mouse was moved off the map and released. End it here. - if (g.pointer0) _downPointerIDs.delete(g.pointer0[2]); - if (g.pointer1) _downPointerIDs.delete(g.pointer1[2]); - g.end(); - return; - } - - event.preventDefault(); - event.stopImmediatePropagation(); - - var loc = _pointerLocGetter(event); - var t, p, l; - - if (isPointer0) g.pointer0[0] = loc; - else if (isPointer1) g.pointer1[0] = loc; - - t = _transform; - if (g.pointer1) { - var p0 = g.pointer0[0], l0 = g.pointer0[1], - p1 = g.pointer1[0], l1 = g.pointer1[1], - dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, - dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; - t = scale(t, Math.sqrt(dp / dl)); - p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - } else if (g.pointer0) { - p = g.pointer0[0]; - l = g.pointer0[1]; - } - else return; - g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent)); - } - - function pointerup() { - if (!_downPointerIDs.has(event.pointerId)) return; - - _downPointerIDs.delete(event.pointerId); - - if (!_activeGesture) return; - - var g = gesture(this, arguments); - - event.stopImmediatePropagation(); - - if (g.pointer0 && g.pointer0[2] === event.pointerId) delete g.pointer0; - else if (g.pointer1 && g.pointer1[2] === event.pointerId) delete g.pointer1; - - if (g.pointer1 && !g.pointer0) { - g.pointer0 = g.pointer1; - delete g.pointer1; - } - if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]); - else { - g.end(); - } - } - - zoom.wheelDelta = function(_) { - return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta; - }; - - zoom.filter = function(_) { - return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter; - }; - - zoom.extent = function(_) { - return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; - }; - - zoom.scaleExtent = function(_) { - return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; - }; - - zoom.translateExtent = function(_) { - return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; - }; - - zoom.constrain = function(_) { - return arguments.length ? (constrain = _, zoom) : constrain; - }; - - zoom.interpolate = function(_) { - return arguments.length ? (interpolate = _, zoom) : interpolate; - }; - - zoom._transform = function(_) { - return arguments.length ? (_transform = _, zoom) : _transform; - }; - - zoom.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? zoom : value; - }; - - return zoom; - } - - // A custom double-click / double-tap event detector that works on touch devices - // if pointer events are supported. Falls back to default `dblclick` event. - function utilDoubleUp() { - - var dispatch$1 = dispatch('doubleUp'); - - var _maxTimespan = 500; // milliseconds - var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices - var _pointer; // object representing the pointer that could trigger double up - - function pointerIsValidFor(loc) { - // second pointerup must occur within a small timeframe after the first pointerdown - return new Date().getTime() - _pointer.startTime <= _maxTimespan && - // all pointer events must occur within a small distance of the first pointerdown - geoVecLength(_pointer.startLoc, loc) <= _maxDistance; - } - - function pointerdown() { - - // ignore right-click - if (event.ctrlKey || event.button === 2) return; - - var loc = [event.clientX, event.clientY]; - - // Don't rely on pointerId here since it can change between pointerdown - // events on touch devices - if (_pointer && !pointerIsValidFor(loc)) { - // if this pointer is no longer valid, clear it so another can be started - _pointer = undefined; - } - - if (!_pointer) { - _pointer = { - startLoc: loc, - startTime: new Date().getTime(), - upCount: 0, - pointerId: event.pointerId - }; - } else { // double down - _pointer.pointerId = event.pointerId; - } - } - - function pointerup() { - - // ignore right-click - if (event.ctrlKey || event.button === 2) return; - - if (!_pointer || _pointer.pointerId !== event.pointerId) return; - - _pointer.upCount += 1; - - if (_pointer.upCount === 2) { // double up! - var loc = [event.clientX, event.clientY]; - if (pointerIsValidFor(loc)) { - var locInThis = utilFastMouse(this)(event); - dispatch$1.call('doubleUp', this, locInThis); - } - // clear the pointer info in any case - _pointer = undefined; - } - } - - function doubleUp(selection) { - if ('PointerEvent' in window) { - // dblclick isn't well supported on touch devices so manually use - // pointer events if they're available - selection - .on('pointerdown.doubleUp', pointerdown) - .on('pointerup.doubleUp', pointerup); - } else { - // fallback to dblclick - selection - .on('dblclick.doubleUp', function() { - dispatch$1.call('doubleUp', this, utilFastMouse(this)(event)); - }); - } - } - - doubleUp.off = function(selection) { - selection - .on('pointerdown.doubleUp', null) - .on('pointerup.doubleUp', null) - .on('dblclick.doubleUp', null); - }; - - return utilRebind(doubleUp, dispatch$1, 'on'); - } - - // constants - var TILESIZE = 256; - var minZoom = 2; - var maxZoom = 24; - var kMin = geoZoomToScale(minZoom, TILESIZE); - var kMax = geoZoomToScale(maxZoom, TILESIZE); - - function clamp(num, min, max) { - return Math.max(min, Math.min(num, max)); - } - - - function rendererMap(context) { - var dispatch$1 = dispatch( - 'move', 'drawn', - 'crossEditableZoom', 'hitMinZoom', - 'changeHighlighting', 'changeAreaFill' - ); - var projection = context.projection; - var curtainProjection = context.curtainProjection; - var drawLayers; - var drawPoints; - var drawVertices; - var drawLines; - var drawAreas; - var drawMidpoints; - var drawLabels; - - var _selection = select(null); - var supersurface = select(null); - var wrapper = select(null); - var surface = select(null); - - var _dimensions = [1, 1]; - var _dblClickZoomEnabled = true; - var _redrawEnabled = true; - var _gestureTransformStart; - var _transformStart = projection.transform(); - var _transformLast; - var _isTransformed = false; - var _minzoom = 0; - var _getMouseCoords; - var _lastPointerEvent; - var _lastWithinEditableZoom; - - // whether a pointerdown event started the zoom - var _pointerDown = false; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom - var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom; - - var _zoomerPanner = _zoomerPannerFunction() - .scaleExtent([kMin, kMax]) - .interpolate(interpolate) - .filter(zoomEventFilter) - .on('zoom.map', zoomPan) - .on('start.map', function() { - _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown'; - }) - .on('end.map', function() { - _pointerDown = false; - }); - var _doubleUpHandler = utilDoubleUp(); - - var scheduleRedraw = throttle(redraw, 750); - // var isRedrawScheduled = false; - // var pendingRedrawCall; - // function scheduleRedraw() { - // // Only schedule the redraw if one has not already been set. - // if (isRedrawScheduled) return; - // isRedrawScheduled = true; - // var that = this; - // var args = arguments; - // pendingRedrawCall = window.requestIdleCallback(function () { - // // Reset the boolean so future redraws can be set. - // isRedrawScheduled = false; - // redraw.apply(that, args); - // }, { timeout: 1400 }); - // } - - function cancelPendingRedraw() { - scheduleRedraw.cancel(); - // isRedrawScheduled = false; - // window.cancelIdleCallback(pendingRedrawCall); - } - - - function map(selection) { - _selection = selection; - - context - .on('change.map', immediateRedraw); - - var osm = context.connection(); - if (osm) { - osm.on('change.map', immediateRedraw); - } - - function didUndoOrRedo(targetTransform) { - var mode = context.mode().id; - if (mode !== 'browse' && mode !== 'select') return; - if (targetTransform) { - map.transformEase(targetTransform); - } - } - - context.history() - .on('merge.map', function() { scheduleRedraw(); }) - .on('change.map', immediateRedraw) - .on('undone.map', function(stack, fromStack) { - didUndoOrRedo(fromStack.transform); - }) - .on('redone.map', function(stack) { - didUndoOrRedo(stack.transform); - }); - - context.background() - .on('change.map', immediateRedraw); - - context.features() - .on('redraw.map', immediateRedraw); - - drawLayers - .on('change.map', function() { - context.background().updateImagery(); - immediateRedraw(); - }); - - selection - .on('wheel.map mousewheel.map', function() { - // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552 - event.preventDefault(); - }) - .call(_zoomerPanner) - .call(_zoomerPanner.transform, projection.transform()) - .on('dblclick.zoom', null); // override d3-zoom dblclick handling - - map.supersurface = supersurface = selection.append('div') - .attr('class', 'supersurface') - .call(utilSetTransform, 0, 0); - - // Need a wrapper div because Opera can't cope with an absolutely positioned - // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16 - wrapper = supersurface - .append('div') - .attr('class', 'layer layer-data'); - - map.surface = surface = wrapper - .call(drawLayers) - .selectAll('.surface'); - - surface - .call(drawLabels.observe) - .call(_doubleUpHandler) - .on(_pointerPrefix + 'down.zoom', function() { - _lastPointerEvent = event; - if (event.button === 2) { - event.stopPropagation(); - } - }, true) - .on(_pointerPrefix + 'up.zoom', function() { - _lastPointerEvent = event; - if (resetTransform()) { - immediateRedraw(); - } - }) - .on(_pointerPrefix + 'move.map', function() { - _lastPointerEvent = event; - }) - .on(_pointerPrefix + 'over.vertices', function() { - if (map.editableDataEnabled() && !_isTransformed) { - var hover = event.target.__data__; - surface.call(drawVertices.drawHover, context.graph(), hover, map.extent()); - dispatch$1.call('drawn', this, { full: false }); - } - }) - .on(_pointerPrefix + 'out.vertices', function() { - if (map.editableDataEnabled() && !_isTransformed) { - var hover = event.relatedTarget && event.relatedTarget.__data__; - surface.call(drawVertices.drawHover, context.graph(), hover, map.extent()); - dispatch$1.call('drawn', this, { full: false }); - } - }); - - var detected = utilDetect(); - - // only WebKit supports gesture events - if ('GestureEvent' in window && - // Listening for gesture events on iOS 13.4+ breaks double-tapping, - // but we only need to do this on desktop Safari anyway. – #7694 - !detected.isMobileWebKit) { - - // Desktop Safari sends gesture events for multitouch trackpad pinches. - // We can listen for these and translate them into map zooms. - surface - .on('gesturestart.surface', function() { - event.preventDefault(); - _gestureTransformStart = projection.transform(); - }) - .on('gesturechange.surface', gestureChange); - } - - // must call after surface init - updateAreaFill(); - - _doubleUpHandler.on('doubleUp.map', function(p0) { - if (!_dblClickZoomEnabled) return; - - // don't zoom if targeting something other than the map itself - if (typeof event.target.__data__ === 'object' && - // or area fills - !select(event.target).classed('fill')) return; - - var zoomOut = event.shiftKey; - - var t = projection.transform(); - - var p1 = t.invert(p0); - - t = t.scale(zoomOut ? 0.5 : 2); - - t.x = p0[0] - p1[0] * t.k; - t.y = p0[1] - p1[1] * t.k; - - map.transformEase(t); - }); - - context.on('enter.map', function() { - if (!map.editableDataEnabled(true /* skip zoom check */)) return; - - // redraw immediately any objects affected by a change in selectedIDs. - var graph = context.graph(); - var selectedAndParents = {}; - context.selectedIDs().forEach(function(id) { - var entity = graph.hasEntity(id); - if (entity) { - selectedAndParents[entity.id] = entity; - if (entity.type === 'node') { - graph.parentWays(entity).forEach(function(parent) { - selectedAndParents[parent.id] = parent; - }); - } - } - }); - var data = Object.values(selectedAndParents); - var filter = function(d) { return d.id in selectedAndParents; }; - - data = context.features().filter(data, graph); - - surface - .call(drawVertices.drawSelected, graph, map.extent()) - .call(drawLines, graph, data, filter) - .call(drawAreas, graph, data, filter) - .call(drawMidpoints, graph, data, filter, map.trimmedExtent()); - - dispatch$1.call('drawn', this, { full: false }); - - // redraw everything else later - scheduleRedraw(); - }); - - map.dimensions(utilGetDimensions(selection)); - } - - - function zoomEventFilter() { - // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18) - // Intercept `mousedown` and check if there is an orphaned zoom gesture. - // This can happen if a previous `mousedown` occurred without a `mouseup`. - // If we detect this, dispatch `mouseup` to complete the orphaned gesture, - // so that d3-zoom won't stop propagation of new `mousedown` events. - if (event.type === 'mousedown') { - var hasOrphan = false; - var listeners = window.__on; - for (var i = 0; i < listeners.length; i++) { - var listener = listeners[i]; - if (listener.name === 'zoom' && listener.type === 'mouseup') { - hasOrphan = true; - break; - } - } - if (hasOrphan) { - var event$1 = window.CustomEvent; - if (event$1) { - event$1 = new event$1('mouseup'); - } else { - event$1 = window.document.createEvent('Event'); - event$1.initEvent('mouseup', false, false); - } - // Event needs to be dispatched with an event.view property. - event$1.view = window; - window.dispatchEvent(event$1); - } - } - - return event.button !== 2; // ignore right clicks - } - - - function pxCenter() { - return [_dimensions[0] / 2, _dimensions[1] / 2]; - } - - - function drawEditable(difference, extent) { - var mode = context.mode(); - var graph = context.graph(); - var features = context.features(); - var all = context.history().intersects(map.extent()); - var fullRedraw = false; - var data; - var set; - var filter; - var applyFeatureLayerFilters = true; - - if (map.isInWideSelection()) { - data = []; - utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) { - var entity = context.hasEntity(id); - if (entity) data.push(entity); - }); - fullRedraw = true; - filter = utilFunctor(true); - // selected features should always be visible, so we can skip filtering - applyFeatureLayerFilters = false; - - } else if (difference) { - var complete = difference.complete(map.extent()); - data = Object.values(complete).filter(Boolean); - set = new Set(Object.keys(complete)); - filter = function(d) { return set.has(d.id); }; - features.clear(data); - - } else { - // force a full redraw if gatherStats detects that a feature - // should be auto-hidden (e.g. points or buildings).. - if (features.gatherStats(all, graph, _dimensions)) { - extent = undefined; - } - - if (extent) { - data = context.history().intersects(map.extent().intersection(extent)); - set = new Set(data.map(function(entity) { return entity.id; })); - filter = function(d) { return set.has(d.id); }; - - } else { - data = all; - fullRedraw = true; - filter = utilFunctor(true); - } - } - - if (applyFeatureLayerFilters) { - data = features.filter(data, graph); - } else { - context.features().resetStats(); - } - - if (mode && mode.id === 'select') { - // update selected vertices - the user might have just double-clicked a way, - // creating a new vertex, triggering a partial redraw without a mode change - surface.call(drawVertices.drawSelected, graph, map.extent()); - } - - surface - .call(drawVertices, graph, data, filter, map.extent(), fullRedraw) - .call(drawLines, graph, data, filter) - .call(drawAreas, graph, data, filter) - .call(drawMidpoints, graph, data, filter, map.trimmedExtent()) - .call(drawLabels, graph, data, filter, _dimensions, fullRedraw) - .call(drawPoints, graph, data, filter); - - dispatch$1.call('drawn', this, {full: true}); - } - - map.init = function() { - drawLayers = svgLayers(projection, context); - drawPoints = svgPoints(projection, context); - drawVertices = svgVertices(projection, context); - drawLines = svgLines(projection, context); - drawAreas = svgAreas(projection, context); - drawMidpoints = svgMidpoints(projection, context); - drawLabels = svgLabels(projection, context); - }; - - function editOff() { - context.features().resetStats(); - surface.selectAll('.layer-osm *').remove(); - surface.selectAll('.layer-touch:not(.markers) *').remove(); - - var allowed = { - 'browse': true, - 'save': true, - 'select-note': true, - 'select-data': true, - 'select-error': true - }; - - var mode = context.mode(); - if (mode && !allowed[mode.id]) { - context.enter(modeBrowse(context)); - } - - dispatch$1.call('drawn', this, {full: true}); - } - - - - - - function gestureChange() { - // Remap Safari gesture events to wheel events - #5492 - // We want these disabled most places, but enabled for zoom/unzoom on map surface - // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent - var e = event; - e.preventDefault(); - - var props = { - deltaMode: 0, // dummy values to ignore in zoomPan - deltaY: 1, // dummy values to ignore in zoomPan - clientX: e.clientX, - clientY: e.clientY, - screenX: e.screenX, - screenY: e.screenY, - x: e.x, - y: e.y - }; - - var e2 = new WheelEvent('wheel', props); - e2._scale = e.scale; // preserve the original scale - e2._rotation = e.rotation; // preserve the original rotation - - _selection.node().dispatchEvent(e2); - } - - - function zoomPan(manualEvent) { - var event$1 = (manualEvent || event); - var source = event$1.sourceEvent; - var eventTransform = event$1.transform; - var x = eventTransform.x; - var y = eventTransform.y; - var k = eventTransform.k; - - // Special handling of 'wheel' events: - // They might be triggered by the user scrolling the mouse wheel, - // or 2-finger pinch/zoom gestures, the transform may need adjustment. - if (source && source.type === 'wheel') { - - // assume that the gesture is already handled by pointer events - if (_pointerDown) return; - - var detected = utilDetect(); - var dX = source.deltaX; - var dY = source.deltaY; - var x2 = x; - var y2 = y; - var k2 = k; - var t0, p0, p1; - - // Normalize mousewheel scroll speed (Firefox) - #3029 - // If wheel delta is provided in LINE units, recalculate it in PIXEL units - // We are essentially redoing the calculations that occur here: - // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203 - // See this for more info: - // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js - if (source.deltaMode === 1 /* LINE */) { - // Convert from lines to pixels, more if the user is scrolling fast. - // (I made up the exp function to roughly match Firefox to what Chrome does) - // These numbers should be floats, because integers are treated as pan gesture below. - var lines = Math.abs(source.deltaY); - var sign = (source.deltaY > 0) ? 1 : -1; - dY = sign * clamp( - Math.exp((lines - 1) * 0.75) * 4.000244140625, - 4.000244140625, // min - 350.000244140625 // max - ); - - // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3) - // There doesn't seem to be any scroll accelleration. - // This multiplier increases the speed a little bit - #5512 - if (detected.os !== 'mac') { - dY *= 5; - } - - // recalculate x2,y2,k2 - t0 = _isTransformed ? _transformLast : _transformStart; - p0 = _getMouseCoords(source); - p1 = t0.invert(p0); - k2 = t0.k * Math.pow(2, -dY / 500); - k2 = clamp(k2, kMin, kMax); - x2 = p0[0] - p1[0] * k2; - y2 = p0[1] - p1[1] * k2; - - // 2 finger map pinch zooming (Safari) - #5492 - // These are fake `wheel` events we made from Safari `gesturechange` events.. - } else if (source._scale) { - // recalculate x2,y2,k2 - t0 = _gestureTransformStart; - p0 = _getMouseCoords(source); - p1 = t0.invert(p0); - k2 = t0.k * source._scale; - k2 = clamp(k2, kMin, kMax); - x2 = p0[0] - p1[0] * k2; - y2 = p0[1] - p1[1] * k2; - - // 2 finger map pinch zooming (all browsers except Safari) - #5492 - // Pinch zooming via the `wheel` event will always have: - // - `ctrlKey = true` - // - `deltaY` is not round integer pixels (ignore `deltaX`) - } else if (source.ctrlKey && !isInteger(dY)) { - dY *= 6; // slightly scale up whatever the browser gave us - - // recalculate x2,y2,k2 - t0 = _isTransformed ? _transformLast : _transformStart; - p0 = _getMouseCoords(source); - p1 = t0.invert(p0); - k2 = t0.k * Math.pow(2, -dY / 500); - k2 = clamp(k2, kMin, kMax); - x2 = p0[0] - p1[0] * k2; - y2 = p0[1] - p1[1] * k2; - - // Trackpad scroll zooming with shift or alt/option key down - } else if ((source.altKey || source.shiftKey) && isInteger(dY)) { - // recalculate x2,y2,k2 - t0 = _isTransformed ? _transformLast : _transformStart; - p0 = _getMouseCoords(source); - p1 = t0.invert(p0); - k2 = t0.k * Math.pow(2, -dY / 500); - k2 = clamp(k2, kMin, kMax); - x2 = p0[0] - p1[0] * k2; - y2 = p0[1] - p1[1] * k2; - - // 2 finger map panning (Mac only, all browsers) - #5492, #5512 - // Panning via the `wheel` event will always have: - // - `ctrlKey = false` - // - `deltaX`,`deltaY` are round integer pixels - } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) { - p1 = projection.translate(); - x2 = p1[0] - dX; - y2 = p1[1] - dY; - k2 = projection.scale(); - k2 = clamp(k2, kMin, kMax); - } - - // something changed - replace the event transform - if (x2 !== x || y2 !== y || k2 !== k) { - x = x2; - y = y2; - k = k2; - eventTransform = identity$2.translate(x2, y2).scale(k2); - if (_zoomerPanner._transform) { - // utilZoomPan interface - _zoomerPanner._transform(eventTransform); - } else { - // d3_zoom interface - _selection.node().__zoom = eventTransform; - } - } - - } - - if (_transformStart.x === x && - _transformStart.y === y && - _transformStart.k === k) { - return; // no change - } - - var withinEditableZoom = map.withinEditableZoom(); - if (_lastWithinEditableZoom !== withinEditableZoom) { - if (_lastWithinEditableZoom !== undefined) { - // notify that the map zoomed in or out over the editable zoom threshold - dispatch$1.call('crossEditableZoom', this, withinEditableZoom); - } - _lastWithinEditableZoom = withinEditableZoom; - } - - if (geoScaleToZoom(k, TILESIZE) < _minzoom) { - surface.interrupt(); - dispatch$1.call('hitMinZoom', this, map); - setCenterZoom(map.center(), context.minEditableZoom(), 0, true); - scheduleRedraw(); - dispatch$1.call('move', this, map); - return; - } - - projection.transform(eventTransform); - - var scale = k / _transformStart.k; - var tX = (x / scale - _transformStart.x) * scale; - var tY = (y / scale - _transformStart.y) * scale; - - if (context.inIntro()) { - curtainProjection.transform({ - x: x - tX, - y: y - tY, - k: k - }); - } - - if (source) { - _lastPointerEvent = event$1; - } - _isTransformed = true; - _transformLast = eventTransform; - utilSetTransform(supersurface, tX, tY, scale); - scheduleRedraw(); - - dispatch$1.call('move', this, map); - - - function isInteger(val) { - return typeof val === 'number' && isFinite(val) && Math.floor(val) === val; - } - } - - - function resetTransform() { - if (!_isTransformed) return false; - - utilSetTransform(supersurface, 0, 0); - _isTransformed = false; - if (context.inIntro()) { - curtainProjection.transform(projection.transform()); - } - return true; - } - - - function redraw(difference, extent) { - if (surface.empty() || !_redrawEnabled) return; - - // If we are in the middle of a zoom/pan, we can't do differenced redraws. - // It would result in artifacts where differenced entities are redrawn with - // one transform and unchanged entities with another. - if (resetTransform()) { - difference = extent = undefined; - } - - var zoom = map.zoom(); - var z = String(~~zoom); - - if (surface.attr('data-zoom') !== z) { - surface.attr('data-zoom', z); - } - - // class surface as `lowzoom` around z17-z18.5 (based on latitude) - var lat = map.center()[1]; - var lowzoom = linear$2() - .domain([-60, 0, 60]) - .range([17, 18.5, 17]) - .clamp(true); - - surface - .classed('low-zoom', zoom <= lowzoom(lat)); - - - if (!difference) { - supersurface.call(context.background()); - wrapper.call(drawLayers); - } - - // OSM - if (map.editableDataEnabled() || map.isInWideSelection()) { - context.loadTiles(projection); - drawEditable(difference, extent); - } else { - editOff(); - } - - _transformStart = projection.transform(); - - return map; - } - - - - var immediateRedraw = function(difference, extent) { - if (!difference && !extent) cancelPendingRedraw(); - redraw(difference, extent); - }; - - - map.lastPointerEvent = function() { - return _lastPointerEvent; - }; - - - map.mouse = function() { - var event$1 = _lastPointerEvent || event; - if (event$1) { - var s; - while ((s = event$1.sourceEvent)) { event$1 = s; } - return _getMouseCoords(event$1); - } - return null; - }; - - - // returns Lng/Lat - map.mouseCoordinates = function() { - var coord = map.mouse() || pxCenter(); - return projection.invert(coord); - }; - - - map.dblclickZoomEnable = function(val) { - if (!arguments.length) return _dblClickZoomEnabled; - _dblClickZoomEnabled = val; - return map; - }; - - - map.redrawEnable = function(val) { - if (!arguments.length) return _redrawEnabled; - _redrawEnabled = val; - return map; - }; - - - map.isTransformed = function() { - return _isTransformed; - }; - - - function setTransform(t2, duration, force) { - var t = projection.transform(); - if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false; - - if (duration) { - _selection - .transition() - .duration(duration) - .on('start', function() { map.startEase(); }) - .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k)); - } else { - projection.transform(t2); - _transformStart = t2; - _selection.call(_zoomerPanner.transform, _transformStart); - } - - return true; - } - - - function setCenterZoom(loc2, z2, duration, force) { - var c = map.center(); - var z = map.zoom(); - if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false; - - var proj = geoRawMercator().transform(projection.transform()); // copy projection - - var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax); - proj.scale(k2); - - var t = proj.translate(); - var point = proj(loc2); - - var center = pxCenter(); - t[0] += center[0] - point[0]; - t[1] += center[1] - point[1]; - - return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force); - } - - - map.pan = function(delta, duration) { - var t = projection.translate(); - var k = projection.scale(); - - t[0] += delta[0]; - t[1] += delta[1]; - - if (duration) { - _selection - .transition() - .duration(duration) - .on('start', function() { map.startEase(); }) - .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k)); - } else { - projection.translate(t); - _transformStart = projection.transform(); - _selection.call(_zoomerPanner.transform, _transformStart); - dispatch$1.call('move', this, map); - immediateRedraw(); - } - - return map; - }; - - - map.dimensions = function(val) { - if (!arguments.length) return _dimensions; - - _dimensions = val; - drawLayers.dimensions(_dimensions); - context.background().dimensions(_dimensions); - projection.clipExtent([[0, 0], _dimensions]); - _getMouseCoords = utilFastMouse(supersurface.node()); - - scheduleRedraw(); - return map; - }; - - - function zoomIn(delta) { - setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true); - } - - function zoomOut(delta) { - setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true); - } - - map.zoomIn = function() { zoomIn(1); }; - map.zoomInFurther = function() { zoomIn(4); }; - map.canZoomIn = function() { return map.zoom() < maxZoom; }; - - map.zoomOut = function() { zoomOut(1); }; - map.zoomOutFurther = function() { zoomOut(4); }; - map.canZoomOut = function() { return map.zoom() > minZoom; }; - - map.center = function(loc2) { - if (!arguments.length) { - return projection.invert(pxCenter()); - } - - if (setCenterZoom(loc2, map.zoom())) { - dispatch$1.call('move', this, map); - } - - scheduleRedraw(); - return map; - }; - - map.unobscuredCenterZoomEase = function(loc, zoom) { - var offset = map.unobscuredOffsetPx(); - - var proj = geoRawMercator().transform(projection.transform()); // copy projection - // use the target zoom to calculate the offset center - proj.scale(geoZoomToScale(zoom, TILESIZE)); - - var locPx = proj(loc); - var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]]; - var offsetLoc = proj.invert(offsetLocPx); - - map.centerZoomEase(offsetLoc, zoom); - }; - - map.unobscuredOffsetPx = function() { - var openPane = context.container().select('.map-panes .map-pane.shown'); - if (!openPane.empty()) { - return [openPane.node().offsetWidth/2, 0]; - } - return [0, 0]; - }; - - map.zoom = function(z2) { - if (!arguments.length) { - return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0); - } - - if (z2 < _minzoom) { - surface.interrupt(); - dispatch$1.call('hitMinZoom', this, map); - z2 = context.minEditableZoom(); - } - - if (setCenterZoom(map.center(), z2)) { - dispatch$1.call('move', this, map); - } - - scheduleRedraw(); - return map; - }; - - - map.centerZoom = function(loc2, z2) { - if (setCenterZoom(loc2, z2)) { - dispatch$1.call('move', this, map); - } - - scheduleRedraw(); - return map; - }; - - - map.zoomTo = function(entity) { - var extent = entity.extent(context.graph()); - if (!isFinite(extent.area())) return map; - - var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20); - return map.centerZoom(extent.center(), z2); - }; - - - map.centerEase = function(loc2, duration) { - duration = duration || 250; - setCenterZoom(loc2, map.zoom(), duration); - return map; - }; - - - map.zoomEase = function(z2, duration) { - duration = duration || 250; - setCenterZoom(map.center(), z2, duration, false); - return map; - }; - - - map.centerZoomEase = function(loc2, z2, duration) { - duration = duration || 250; - setCenterZoom(loc2, z2, duration, false); - return map; - }; - - - map.transformEase = function(t2, duration) { - duration = duration || 250; - setTransform(t2, duration, false /* don't force */); - return map; - }; - - - map.zoomToEase = function(obj, duration) { - var extent; - if (Array.isArray(obj)) { - obj.forEach(function(entity) { - var entityExtent = entity.extent(context.graph()); - if (!extent) { - extent = entityExtent; - } else { - extent = extent.extend(entityExtent); - } - }); - } else { - extent = obj.extent(context.graph()); - } - if (!isFinite(extent.area())) return map; - - var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20); - return map.centerZoomEase(extent.center(), z2, duration); - }; - - - map.startEase = function() { - utilBindOnce(surface, _pointerPrefix + 'down.ease', function() { - map.cancelEase(); - }); - return map; - }; - - - map.cancelEase = function() { - _selection.interrupt(); - return map; - }; - - - map.extent = function(val) { - if (!arguments.length) { - return new geoExtent( - projection.invert([0, _dimensions[1]]), - projection.invert([_dimensions[0], 0]) - ); - } else { - var extent = geoExtent(val); - map.centerZoom(extent.center(), map.extentZoom(extent)); - } - }; - - - map.trimmedExtent = function(val) { - if (!arguments.length) { - var headerY = 71; - var footerY = 30; - var pad = 10; - return new geoExtent( - projection.invert([pad, _dimensions[1] - footerY - pad]), - projection.invert([_dimensions[0] - pad, headerY + pad]) - ); - } else { - var extent = geoExtent(val); - map.centerZoom(extent.center(), map.trimmedExtentZoom(extent)); - } - }; - - - function calcExtentZoom(extent, dim) { - var tl = projection([extent[0][0], extent[1][1]]); - var br = projection([extent[1][0], extent[0][1]]); - - // Calculate maximum zoom that fits extent - var hFactor = (br[0] - tl[0]) / dim[0]; - var vFactor = (br[1] - tl[1]) / dim[1]; - var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2; - var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2; - var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff); - - return newZoom; - } - - - map.extentZoom = function(val) { - return calcExtentZoom(geoExtent(val), _dimensions); - }; - - - map.trimmedExtentZoom = function(val) { - var trimY = 120; - var trimX = 40; - var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY]; - return calcExtentZoom(geoExtent(val), trimmed); - }; - - - map.withinEditableZoom = function() { - return map.zoom() >= context.minEditableZoom(); - }; - - - map.isInWideSelection = function() { - return !map.withinEditableZoom() && context.selectedIDs().length; - }; - - - map.editableDataEnabled = function(skipZoomCheck) { - - var layer = context.layers().layer('osm'); - if (!layer || !layer.enabled()) return false; - - return skipZoomCheck || map.withinEditableZoom(); - }; - - - map.notesEditable = function() { - var layer = context.layers().layer('notes'); - if (!layer || !layer.enabled()) return false; - - return map.withinEditableZoom(); - }; - - - map.minzoom = function(val) { - if (!arguments.length) return _minzoom; - _minzoom = val; - return map; - }; - - - map.toggleHighlightEdited = function() { - surface.classed('highlight-edited', !surface.classed('highlight-edited')); - map.pan([0,0]); // trigger a redraw - dispatch$1.call('changeHighlighting', this); - }; - - - map.areaFillOptions = ['wireframe', 'partial', 'full']; - - map.activeAreaFill = function(val) { - if (!arguments.length) return corePreferences('area-fill') || 'partial'; - - corePreferences('area-fill', val); - if (val !== 'wireframe') { - corePreferences('area-fill-toggle', val); - } - updateAreaFill(); - map.pan([0,0]); // trigger a redraw - dispatch$1.call('changeAreaFill', this); - return map; - }; - - map.toggleWireframe = function() { - - var activeFill = map.activeAreaFill(); - - if (activeFill === 'wireframe') { - activeFill = corePreferences('area-fill-toggle') || 'partial'; - } else { - activeFill = 'wireframe'; - } - - map.activeAreaFill(activeFill); - }; - - function updateAreaFill() { - var activeFill = map.activeAreaFill(); - map.areaFillOptions.forEach(function(opt) { - surface.classed('fill-' + opt, Boolean(opt === activeFill)); - }); - } - - - map.layers = () => drawLayers; - - - map.doubleUpHandler = function() { - return _doubleUpHandler; - }; - - - return utilRebind(map, dispatch$1, 'on'); - } - - function rendererPhotos(context) { - var dispatch$1 = dispatch('change'); - var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam']; - var _allPhotoTypes = ['flat', 'panoramic']; - var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy - - function photos() {} - - function updateStorage() { - if (window.mocha) return; - - var hash = utilStringQs(window.location.hash); - var enabled = context.layers().all().filter(function(d) { - return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled(); - }).map(function(d) { - return d.id; - }); - if (enabled.length) { - hash.photo_overlay = enabled.join(','); - } else { - delete hash.photo_overlay; - } - window.location.replace('#' + utilQsString(hash, true)); - } - - photos.overlayLayerIDs = function() { - return _layerIDs; - }; - - photos.allPhotoTypes = function() { - return _allPhotoTypes; - }; - - function showsLayer(id) { - var layer = context.layers().layer(id); - return layer && layer.supported() && layer.enabled(); - } - - photos.shouldFilterByPhotoType = function() { - return showsLayer('mapillary') || - (showsLayer('streetside') && showsLayer('openstreetcam')); - }; - - photos.showsPhotoType = function(val) { - if (!photos.shouldFilterByPhotoType()) return true; - - return _shownPhotoTypes.indexOf(val) !== -1; - }; - - photos.showsFlat = function() { - return photos.showsPhotoType('flat'); - }; - - photos.showsPanoramic = function() { - return photos.showsPhotoType('panoramic'); - }; - - photos.togglePhotoType = function(val) { - var index = _shownPhotoTypes.indexOf(val); - if (index !== -1) { - _shownPhotoTypes.splice(index, 1); - } else { - _shownPhotoTypes.push(val); - } - dispatch$1.call('change', this); - return photos; - }; - - photos.init = function() { - var hash = utilStringQs(window.location.hash); - if (hash.photo_overlay) { - var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(','); - hashOverlayIDs.forEach(function(id) { - var layer = context.layers().layer(id); - if (layer) layer.enabled(true); - }); - } - - context.layers().on('change.rendererPhotos', updateStorage); - }; - - return utilRebind(photos, dispatch$1, 'on'); - } - - function uiAccount(context) { - var osm = context.connection(); - - - function update(selection) { - if (!osm) return; - - if (!osm.authenticated()) { - selection.selectAll('.userLink, .logoutLink') - .classed('hide', true); - return; - } - - osm.userDetails(function(err, details) { - var userLink = selection.select('.userLink'), - logoutLink = selection.select('.logoutLink'); - - userLink.html(''); - logoutLink.html(''); - - if (err || !details) return; - - selection.selectAll('.userLink, .logoutLink') - .classed('hide', false); - - // Link - userLink.append('a') - .attr('href', osm.userURL(details.display_name)) - .attr('target', '_blank'); - - // Add thumbnail or dont - if (details.image_url) { - userLink.append('img') - .attr('class', 'icon pre-text user-icon') - .attr('src', details.image_url); - } else { - userLink - .call(svgIcon('#iD-icon-avatar', 'pre-text light')); - } - - // Add user name - userLink.append('span') - .attr('class', 'label') - .text(details.display_name); - - logoutLink.append('a') - .attr('class', 'logout') - .attr('href', '#') - .text(_t('logout')) - .on('click.logout', function() { - event.preventDefault(); - osm.logout(); - }); - }); - } - - - return function(selection) { - selection.append('li') - .attr('class', 'logoutLink') - .classed('hide', true); - - selection.append('li') - .attr('class', 'userLink') - .classed('hide', true); - - if (osm) { - osm.on('change.account', function() { update(selection); }); - update(selection); - } - }; - } - - function uiAttribution(context) { - let _selection = select(null); - - - function render(selection, data, klass) { - let div = selection.selectAll(`.${klass}`) - .data([0]); - - div = div.enter() - .append('div') - .attr('class', klass) - .merge(div); - - - let attributions = div.selectAll('.attribution') - .data(data, d => d.id); - - attributions.exit() - .remove(); - - attributions = attributions.enter() - .append('span') - .attr('class', 'attribution') - .each((d, i, nodes) => { - let attribution = select(nodes[i]); - - if (d.terms_html) { - attribution.html(d.terms_html); - return; - } - - if (d.terms_url) { - attribution = attribution - .append('a') - .attr('href', d.terms_url) - .attr('target', '_blank'); - } - - const sourceID = d.id.replace(/\./g, ''); - const terms_text = _t(`imagery.${sourceID}.attribution.text`, - { default: d.terms_text || d.id || d.name() } - ); - - if (d.icon && !d.overlay) { - attribution - .append('img') - .attr('class', 'source-image') - .attr('src', d.icon); - } - - attribution - .append('span') - .attr('class', 'attribution-text') - .text(terms_text); - }) - .merge(attributions); - - - let copyright = attributions.selectAll('.copyright-notice') - .data(d => { - let notice = d.copyrightNotices(context.map().zoom(), context.map().extent()); - return notice ? [notice] : []; - }); - - copyright.exit() - .remove(); - - copyright = copyright.enter() - .append('span') - .attr('class', 'copyright-notice') - .merge(copyright); - - copyright - .text(String); - } - - - function update() { - let baselayer = context.background().baseLayerSource(); - _selection - .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution'); - - const z = context.map().zoom(); - let overlays = context.background().overlayLayerSources() || []; - _selection - .call(render, overlays.filter(s => s.validZoom(z)), 'overlay-layer-attribution'); - } - - - return function(selection) { - _selection = selection; - - context.background() - .on('change.attribution', update); - - context.map() - .on('move.attribution', throttle(update, 400, { leading: false })); - - update(); - }; - } - - function uiContributors(context) { - var osm = context.connection(), - debouncedUpdate = debounce(function() { update(); }, 1000), - limit = 4, - hidden = false, - wrap = select(null); - - - function update() { - if (!osm) return; - - var users = {}, - entities = context.history().intersects(context.map().extent()); - - entities.forEach(function(entity) { - if (entity && entity.user) users[entity.user] = true; - }); - - var u = Object.keys(users), - subset = u.slice(0, u.length > limit ? limit - 1 : limit); - - wrap.html('') - .call(svgIcon('#iD-icon-nearby', 'pre-text light')); - - var userList = select(document.createElement('span')); - - userList.selectAll() - .data(subset) - .enter() - .append('a') - .attr('class', 'user-link') - .attr('href', function(d) { return osm.userURL(d); }) - .attr('target', '_blank') - .text(String); - - if (u.length > limit) { - var count = select(document.createElement('span')); - - count.append('a') - .attr('target', '_blank') - .attr('href', function() { - return osm.changesetsURL(context.map().center(), context.map().zoom()); - }) - .text(u.length - limit + 1); - - wrap.append('span') - .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() })); - - } else { - wrap.append('span') - .html(_t('contributors.list', { users: userList.html() })); - } - - if (!u.length) { - hidden = true; - wrap - .transition() - .style('opacity', 0); - - } else if (hidden) { - wrap - .transition() - .style('opacity', 1); - } - } - - - return function(selection) { - if (!osm) return; - wrap = selection; - update(); - - osm.on('loaded.contributors', debouncedUpdate); - context.map().on('move.contributors', debouncedUpdate); - }; - } - - var _popoverID = 0; - - function uiPopover(klass) { - var _id = _popoverID++; - var _anchorSelection = select(null); - var popover = function(selection) { - _anchorSelection = selection; - selection.each(setup); - }; - var _animation = utilFunctor(false); - var _placement = utilFunctor('top'); // top, bottom, left, right - var _alignment = utilFunctor('center'); // leading, center, trailing - var _scrollContainer = utilFunctor(select(null)); - var _content; - var _displayType = utilFunctor(''); - var _hasArrow = utilFunctor(true); - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - popover.displayType = function(val) { - if (arguments.length) { - _displayType = utilFunctor(val); - return popover; - } else { - return _displayType; - } - }; - - popover.hasArrow = function(val) { - if (arguments.length) { - _hasArrow = utilFunctor(val); - return popover; - } else { - return _hasArrow; - } - }; - - popover.placement = function(val) { - if (arguments.length) { - _placement = utilFunctor(val); - return popover; - } else { - return _placement; - } - }; - - popover.alignment = function(val) { - if (arguments.length) { - _alignment = utilFunctor(val); - return popover; - } else { - return _alignment; - } - }; - - popover.scrollContainer = function(val) { - if (arguments.length) { - _scrollContainer = utilFunctor(val); - return popover; - } else { - return _scrollContainer; - } - }; - - popover.content = function(val) { - if (arguments.length) { - _content = val; - return popover; - } else { - return _content; - } - }; - - popover.isShown = function() { - var popoverSelection = _anchorSelection.select('.popover-' + _id); - return !popoverSelection.empty() && popoverSelection.classed('in'); - }; - - popover.show = function() { - _anchorSelection.each(show); - }; - - popover.updateContent = function() { - _anchorSelection.each(updateContent); - }; - - popover.hide = function() { - _anchorSelection.each(hide); - }; - - popover.toggle = function() { - _anchorSelection.each(toggle); - }; - - popover.destroy = function(selection, selector) { - // by default, just destroy the current popover - selector = selector || '.popover-' + _id; - - selection - .on(_pointerPrefix + 'enter.popover', null) - .on(_pointerPrefix + 'leave.popover', null) - .on(_pointerPrefix + 'up.popover', null) - .on(_pointerPrefix + 'down.popover', null) - .on('click.popover', null) - .attr('title', function() { - return this.getAttribute('data-original-title') || this.getAttribute('title'); - }) - .attr('data-original-title', null) - .selectAll(selector) - .remove(); - }; - - - popover.destroyAny = function(selection) { - selection.call(popover.destroy, '.popover'); - }; - - function setup() { - var anchor = select(this); - var animate = _animation.apply(this, arguments); - var popoverSelection = anchor.selectAll('.popover-' + _id) - .data([0]); - - - var enter = popoverSelection.enter() - .append('div') - .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')) - .classed('arrowed', _hasArrow.apply(this, arguments)); - - enter - .append('div') - .attr('class', 'popover-arrow'); - - enter - .append('div') - .attr('class', 'popover-inner'); - - popoverSelection = enter - .merge(popoverSelection); - - if (animate) { - popoverSelection.classed('fade', true); - } - - var display = _displayType.apply(this, arguments); - - if (display === 'hover') { - var _lastNonMouseEnterTime; - anchor.on(_pointerPrefix + 'enter.popover', function() { - - if (event.pointerType) { - if (event.pointerType !== 'mouse') { - _lastNonMouseEnterTime = event.timeStamp; - // only allow hover behavior for mouse input - return; - } else if (_lastNonMouseEnterTime && - event.timeStamp - _lastNonMouseEnterTime < 1500) { - // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter - // event for non-mouse interactions right after sending - // the correct type pointerenter event. Workaround by discarding - // any mouse event that occurs immediately after a non-mouse event. - return; - } - } - - // don't show if buttons are pressed, e.g. during click and drag of map - if (event.buttons !== 0) return; - - show.apply(this, arguments); - }); - anchor.on(_pointerPrefix + 'leave.popover', function() { - hide.apply(this, arguments); - }); - - } else if (display === 'clickFocus') { - anchor - .on(_pointerPrefix + 'down.popover', function() { - event.preventDefault(); - event.stopPropagation(); - }) - .on(_pointerPrefix + 'up.popover', function() { - event.preventDefault(); - event.stopPropagation(); - }) - .on('click.popover', toggle); - - popoverSelection - .attr('tabindex', 0) - .on('blur.popover', function() { - anchor.each(function() { - hide.apply(this, arguments); - }); - }); - } - } - - - function show() { - var anchor = select(this); - var popoverSelection = anchor.selectAll('.popover-' + _id); - - if (popoverSelection.empty()) { - // popover was removed somehow, put it back - anchor.call(popover.destroy); - anchor.each(setup); - popoverSelection = anchor.selectAll('.popover-' + _id); - } - - popoverSelection.classed('in', true); - - var displayType = _displayType.apply(this, arguments); - if (displayType === 'clickFocus') { - anchor.classed('active', true); - popoverSelection.node().focus(); - } - - anchor.each(updateContent); - } - - function updateContent() { - var anchor = select(this); - - if (_content) { - anchor.selectAll('.popover-' + _id + ' > .popover-inner') - .call(_content.apply(this, arguments)); - } - - updatePosition.apply(this, arguments); - // hack: update multiple times to fix instances where the absolute offset is - // set before the dynamic popover size is calculated by the browser - updatePosition.apply(this, arguments); - updatePosition.apply(this, arguments); - } - - - function updatePosition() { - - var anchor = select(this); - var popoverSelection = anchor.selectAll('.popover-' + _id); - - var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments); - var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node(); - var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0; - var scrollTop = scrollNode ? scrollNode.scrollTop : 0; - - var placement = _placement.apply(this, arguments); - popoverSelection - .classed('left', false) - .classed('right', false) - .classed('top', false) - .classed('bottom', false) - .classed(placement, true); - - var alignment = _alignment.apply(this, arguments); - var alignFactor = 0.5; - if (alignment === 'leading') { - alignFactor = 0; - } else if (alignment === 'trailing') { - alignFactor = 1; - } - var anchorFrame = getFrame(anchor.node()); - var popoverFrame = getFrame(popoverSelection.node()); - var position; - - switch (placement) { - case 'top': - position = { - x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor, - y: anchorFrame.y - popoverFrame.h - }; - break; - case 'bottom': - position = { - x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor, - y: anchorFrame.y + anchorFrame.h - }; - break; - case 'left': - position = { - x: anchorFrame.x - popoverFrame.w, - y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor - }; - break; - case 'right': - position = { - x: anchorFrame.x + anchorFrame.w, - y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor - }; - break; - } - - if (position) { - - if (scrollNode && (placement === 'top' || placement === 'bottom')) { - - var initialPosX = position.x; - - if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) { - position.x = scrollNode.offsetWidth - 10 - popoverFrame.w; - } else if (position.x < 10) { - position.x = 10; - } - - var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); - // keep the arrow centered on the button, or as close as possible - var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10); - arrow.style('left', ~~arrowPosX + 'px'); - } - - popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px'); - } else { - popoverSelection.style('left', null).style('top', null); - } - - function getFrame(node) { - var positionStyle = select(node).style('position'); - if (positionStyle === 'absolute' || positionStyle === 'static') { - return { - x: node.offsetLeft - scrollLeft, - y: node.offsetTop - scrollTop, - w: node.offsetWidth, - h: node.offsetHeight - }; - } else { - return { - x: 0, - y: 0, - w: node.offsetWidth, - h: node.offsetHeight - }; - } - } - } - - - function hide() { - var anchor = select(this); - if (_displayType.apply(this, arguments) === 'clickFocus') { - anchor.classed('active', false); - } - anchor.selectAll('.popover-' + _id).classed('in', false); - } - - - function toggle() { - if (select(this).select('.popover-' + _id).classed('in')) { - hide.apply(this, arguments); - } else { - show.apply(this, arguments); - } - } - - - return popover; - } - - function uiTooltip(klass) { - - var tooltip = uiPopover((klass || '') + ' tooltip') - .displayType('hover'); - - var _title = function() { - var title = this.getAttribute('data-original-title'); - if (title) { - return title; - } else { - title = this.getAttribute('title'); - this.removeAttribute('title'); - this.setAttribute('data-original-title', title); - } - return title; - }; - - var _heading = utilFunctor(null); - var _keys = utilFunctor(null); - - tooltip.title = function(val) { - if (!arguments.length) return _title; - _title = utilFunctor(val); - return tooltip; - }; - - tooltip.heading = function(val) { - if (!arguments.length) return _heading; - _heading = utilFunctor(val); - return tooltip; - }; - - tooltip.keys = function(val) { - if (!arguments.length) return _keys; - _keys = utilFunctor(val); - return tooltip; - }; - - tooltip.content(function() { - var heading = _heading.apply(this, arguments); - var text = _title.apply(this, arguments); - var keys = _keys.apply(this, arguments); - - return function(selection) { - - var headingSelect = selection - .selectAll('.tooltip-heading') - .data(heading ? [heading] :[]); - - headingSelect.exit() - .remove(); - - headingSelect.enter() - .append('div') - .attr('class', 'tooltip-heading') - .merge(headingSelect) - .html(heading); - - var textSelect = selection - .selectAll('.tooltip-text') - .data(text ? [text] :[]); - - textSelect.exit() - .remove(); - - textSelect.enter() - .append('div') - .attr('class', 'tooltip-text') - .merge(textSelect) - .html(text); - - var keyhintWrap = selection - .selectAll('.keyhint-wrap') - .data(keys && keys.length ? [0] : []); - - keyhintWrap.exit() - .remove(); - - var keyhintWrapEnter = keyhintWrap.enter() - .append('div') - .attr('class', 'keyhint-wrap'); - - keyhintWrapEnter - .append('span') - .html(_t('tooltip_keyhint')); - - keyhintWrap = keyhintWrapEnter.merge(keyhintWrap); - - keyhintWrap.selectAll('kbd.shortcut') - .data(keys && keys.length ? keys : []) - .enter() - .append('kbd') - .attr('class', 'shortcut') - .html(function(d) { - return d; - }); - }; - }); - - return tooltip; - } - - function uiEditMenu(context) { - var dispatch$1 = dispatch('toggled'); - - var _menu = select(null); - var _operations = []; - // the position the menu should be displayed relative to - var _anchorLoc = [0, 0]; - var _anchorLocLonLat = [0, 0]; - // a string indicating how the menu was opened - var _triggerType = ''; - - var _vpTopMargin = 85; // viewport top margin - var _vpBottomMargin = 45; // viewport bottom margin - var _vpSideMargin = 35; // viewport side margin - - var _menuTop = false; - var _menuHeight; - var _menuWidth; - - // hardcode these values to make menu positioning easier - var _verticalPadding = 4; - - // see also `.edit-menu .tooltip` CSS; include margin - var _tooltipWidth = 210; - - // offset the menu slightly from the target location - var _menuSideMargin = 10; - - var _tooltips = []; - - var editMenu = function(selection) { - - var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen'); - - var ops = _operations.filter(function(op) { - return !isTouchMenu || !op.mouseOnly; - }); - - if (!ops.length) return; - - _tooltips = []; - - // Position the menu above the anchor for stylus and finger input - // since the mapper's hand likely obscures the screen below the anchor - _menuTop = isTouchMenu; - - // Show labels for touch input since there aren't hover tooltips - var showLabels = isTouchMenu; - - var buttonHeight = showLabels ? 32 : 34; - if (showLabels) { - // Get a general idea of the width based on the length of the label - _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) { - return op.title.length; - }))); - } else { - _menuWidth = 44; - } - - _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight; - - _menu = selection - .append('div') - .attr('class', 'edit-menu') - .classed('touch-menu', isTouchMenu) - .style('padding', _verticalPadding + 'px 0'); - - var buttons = _menu.selectAll('.edit-menu-item') - .data(ops); - - // enter - var buttonsEnter = buttons.enter() - .append('button') - .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; }) - .style('height', buttonHeight + 'px') - .on('click', click) - // don't listen for `mouseup` because we only care about non-mouse pointer types - .on('pointerup', pointerup) - .on('pointerdown mousedown', function pointerdown() { - // don't let button presses also act as map input - #1869 - event.stopPropagation(); - }); - - buttonsEnter.each(function(d) { - var tooltip = uiTooltip() - .heading(d.title) - .title(d.tooltip()) - .keys([d.keys[0]]); - - _tooltips.push(tooltip); - - select(this) - .call(tooltip) - .append('div') - .attr('class', 'icon-wrap') - .call(svgIcon('#iD-operation-' + d.id, 'operation')); - }); - - if (showLabels) { - buttonsEnter.append('span') - .attr('class', 'label') - .text(function(d) { - return d.title; - }); - } - - // update - buttons = buttonsEnter - .merge(buttons) - .classed('disabled', function(d) { return d.disabled(); }); - - updatePosition(); - - var initialScale = context.projection.scale(); - context.map() - .on('move.edit-menu', function() { - if (initialScale !== context.projection.scale()) { - editMenu.close(); - } - }) - .on('drawn.edit-menu', function(info) { - if (info.full) updatePosition(); - }); - - var lastPointerUpType; - // `pointerup` is always called before `click` - function pointerup() { - lastPointerUpType = event.pointerType; - } - - function click(operation) { - event.stopPropagation(); - if (operation.disabled()) { - if (lastPointerUpType === 'touch' || - lastPointerUpType === 'pen') { - // there are no tooltips for touch interactions so flash feedback instead - context.ui().flash - .duration(4000) - .iconName('#iD-operation-' + operation.id) - .iconClass('operation disabled') - .text(operation.tooltip)(); - } - } else { - if (lastPointerUpType === 'touch' || - lastPointerUpType === 'pen') { - context.ui().flash - .duration(2000) - .iconName('#iD-operation-' + operation.id) - .iconClass('operation') - .text(operation.annotation() || operation.title)(); - } - - operation(); - editMenu.close(); - } - lastPointerUpType = null; - } - - dispatch$1.call('toggled', this, true); - }; - - function updatePosition() { - - if (!_menu || _menu.empty()) return; - - var anchorLoc = context.projection(_anchorLocLonLat); - - var viewport = context.surfaceRect(); - - if (anchorLoc[0] < 0 || - anchorLoc[0] > viewport.width || - anchorLoc[1] < 0 || - anchorLoc[1] > viewport.height) { - // close the menu if it's gone offscreen - - editMenu.close(); - return; - } - - var menuLeft = displayOnLeft(viewport); - - var offset = [0, 0]; - - offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin; - - if (_menuTop) { - if (anchorLoc[1] - _menuHeight < _vpTopMargin) { - // menu is near top viewport edge, shift downward - offset[1] = -anchorLoc[1] + _vpTopMargin; - } else { - offset[1] = -_menuHeight; - } - } else { - if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) { - // menu is near bottom viewport edge, shift upwards - offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin; - } else { - offset[1] = 0; - } - } - - var origin = geoVecAdd(anchorLoc, offset); - - _menu - .style('left', origin[0] + 'px') - .style('top', origin[1] + 'px'); - - var tooltipSide = tooltipPosition(viewport, menuLeft); - _tooltips.forEach(function(tooltip) { - tooltip.placement(tooltipSide); - }); - - function displayOnLeft(viewport) { - if (_mainLocalizer.textDirection() === 'ltr') { - if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) { - // right menu would be too close to the right viewport edge, go left - return true; - } - // prefer right menu - return false; - - } else { // rtl - if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) { - // left menu would be too close to the left viewport edge, go right - return false; - } - // prefer left menu - return true; - } - } - - function tooltipPosition(viewport, menuLeft) { - if (_mainLocalizer.textDirection() === 'ltr') { - if (menuLeft) { - // if there's not room for a right-side menu then there definitely - // isn't room for right-side tooltips - return 'left'; - } - if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) { - // right tooltips would be too close to the right viewport edge, go left - return 'left'; - } - // prefer right tooltips - return 'right'; - - } else { // rtl - if (!menuLeft) { - return 'right'; - } - if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) { - // left tooltips would be too close to the left viewport edge, go right - return 'right'; - } - // prefer left tooltips - return 'left'; - } - } - } - - editMenu.close = function () { - - context.map() - .on('move.edit-menu', null) - .on('drawn.edit-menu', null); - - _menu.remove(); - _tooltips = []; - - dispatch$1.call('toggled', this, false); - }; - - editMenu.anchorLoc = function(val) { - if (!arguments.length) return _anchorLoc; - _anchorLoc = val; - _anchorLocLonLat = context.projection.invert(_anchorLoc); - return editMenu; - }; - - editMenu.triggerType = function(val) { - if (!arguments.length) return _triggerType; - _triggerType = val; - return editMenu; - }; - - editMenu.operations = function(val) { - if (!arguments.length) return _operations; - _operations = val; - return editMenu; - }; - - return utilRebind(editMenu, dispatch$1, 'on'); - } - - function uiFeatureInfo(context) { - function update(selection) { - var features = context.features(); - var stats = features.stats(); - var count = 0; - var hiddenList = features.hidden().map(function(k) { - if (stats[k]) { - count += stats[k]; - return String(stats[k]) + ' ' + _t('feature.' + k + '.description'); - } - }).filter(Boolean); - - selection.html(''); - - if (hiddenList.length) { - var tooltipBehavior = uiTooltip() - .placement('top') - .title(function() { - return hiddenList.join('
    '); - }); - - selection.append('a') - .attr('class', 'chip') - .attr('href', '#') - .attr('tabindex', -1) - .html(_t('feature_info.hidden_warning', { count: count })) - .call(tooltipBehavior) - .on('click', function() { - tooltipBehavior.hide(); - event.preventDefault(); - // open the Map Data pane - context.ui().togglePanes(context.container().select('.map-panes .map-data-pane')); - }); - } - - selection - .classed('hide', !hiddenList.length); - } - - - return function(selection) { - update(selection); - - context.features().on('change.feature_info', function() { - update(selection); - }); - }; - } - - function uiFlash(context) { - var _flashTimer; - - var _duration = 2000; - var _iconName = '#iD-icon-no'; - var _iconClass = 'disabled'; - var _text = ''; - var _textClass; - - function flash() { - if (_flashTimer) { - _flashTimer.stop(); - } - - context.container().select('.main-footer-wrap') - .classed('footer-hide', true) - .classed('footer-show', false); - context.container().select('.flash-wrap') - .classed('footer-hide', false) - .classed('footer-show', true); - - var content = context.container().select('.flash-wrap').selectAll('.flash-content') - .data([0]); - - // Enter - var contentEnter = content.enter() - .append('div') - .attr('class', 'flash-content'); - - var iconEnter = contentEnter - .append('svg') - .attr('class', 'flash-icon icon') - .append('g') - .attr('transform', 'translate(10,10)'); - - iconEnter - .append('circle') - .attr('r', 9); - - iconEnter - .append('use') - .attr('transform', 'translate(-7,-7)') - .attr('width', '14') - .attr('height', '14'); - - contentEnter - .append('div') - .attr('class', 'flash-text'); - - - // Update - content = content - .merge(contentEnter); - - content - .selectAll('.flash-icon') - .attr('class', 'icon flash-icon ' + (_iconClass || '')); - - content - .selectAll('.flash-icon use') - .attr('xlink:href', _iconName); - - content - .selectAll('.flash-text') - .attr('class', 'flash-text ' + (_textClass || '')) - .text(_text); - - - _flashTimer = d3_timeout(function() { - _flashTimer = null; - context.container().select('.main-footer-wrap') - .classed('footer-hide', false) - .classed('footer-show', true); - context.container().select('.flash-wrap') - .classed('footer-hide', true) - .classed('footer-show', false); - }, _duration); - - return content; - } - - - flash.duration = function(_) { - if (!arguments.length) return _duration; - _duration = _; - return flash; - }; - - flash.text = function(_) { - if (!arguments.length) return _text; - _text = _; - return flash; - }; - - flash.textClass = function(_) { - if (!arguments.length) return _textClass; - _textClass = _; - return flash; - }; - - flash.iconName = function(_) { - if (!arguments.length) return _iconName; - _iconName = _; - return flash; - }; - - flash.iconClass = function(_) { - if (!arguments.length) return _iconClass; - _iconClass = _; - return flash; - }; - - return flash; - } - - function uiFullScreen(context) { - var element = context.container().node(); - // var button = d3_select(null); - - - function getFullScreenFn() { - if (element.requestFullscreen) { - return element.requestFullscreen; - } else if (element.msRequestFullscreen) { - return element.msRequestFullscreen; - } else if (element.mozRequestFullScreen) { - return element.mozRequestFullScreen; - } else if (element.webkitRequestFullscreen) { - return element.webkitRequestFullscreen; - } - } - - - function getExitFullScreenFn() { - if (document.exitFullscreen) { - return document.exitFullscreen; - } else if (document.msExitFullscreen) { - return document.msExitFullscreen; - } else if (document.mozCancelFullScreen) { - return document.mozCancelFullScreen; - } else if (document.webkitExitFullscreen) { - return document.webkitExitFullscreen; - } - } - - - function isFullScreen() { - return document.fullscreenElement || - document.mozFullScreenElement || - document.webkitFullscreenElement || - document.msFullscreenElement; - } - - - function isSupported() { - return !!getFullScreenFn(); - } - - - function fullScreen() { - event.preventDefault(); - if (!isFullScreen()) { - // button.classed('active', true); - getFullScreenFn().apply(element); - } else { - // button.classed('active', false); - getExitFullScreenFn().apply(document); - } - } - - - return function() { // selection) { - if (!isSupported()) return; - - // button = selection.append('button') - // .attr('title', t('full_screen')) - // .attr('tabindex', -1) - // .on('click', fullScreen) - // .call(tooltip); - - // button.append('span') - // .attr('class', 'icon full-screen'); - - var detected = utilDetect(); - var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']); - context.keybinding().on(keys, fullScreen); - }; - } - - function uiGeolocate(context) { - var _geolocationOptions = { - // prioritize speed and power usage over precision - enableHighAccuracy: false, - // don't hang indefinitely getting the location - timeout: 6000 // 6sec - }; - var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true); - var _layer = context.layers().layer('geolocate'); - var _position; - var _extent; - var _timeoutID; - var _button = select(null); - - function click() { - if (context.inIntro()) return; - if (!_layer.enabled() && !_locating.isShown()) { - - // This timeout ensures that we still call finish() even if - // the user declines to share their location in Firefox - _timeoutID = setTimeout(error, 10000 /* 10sec */ ); - - context.container().call(_locating); - // get the latest position even if we already have one - navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions); - } else { - _locating.close(); - _layer.enabled(null, false); - updateButtonState(); - } - } - - function zoomTo() { - context.enter(modeBrowse(context)); - - var map = context.map(); - _layer.enabled(_position, true); - updateButtonState(); - map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent))); - } - - function success(geolocation) { - _position = geolocation; - var coords = _position.coords; - _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy); - zoomTo(); - finish(); - } - - function error() { - if (_position) { - // use the position from a previous call if we have one - zoomTo(); - } else { - context.ui().flash - .text(_t('geolocate.location_unavailable')) - .iconName('#iD-icon-geolocate')(); - } - - finish(); - } - - function finish() { - _locating.close(); // unblock ui - if (_timeoutID) { clearTimeout(_timeoutID); } - _timeoutID = undefined; - } - - function updateButtonState() { - _button.classed('active', _layer.enabled()); - } - - return function(selection) { - if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return; - - _button = selection - .append('button') - .on('click', click) - .call(svgIcon('#iD-icon-geolocate', 'light')) - .call(uiTooltip() - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - .title(_t('geolocate.title')) - .keys([_t('geolocate.key')]) - ); - - context.keybinding().on(_t('geolocate.key'), click); - }; - } - - function uiPanelBackground(context) { - var background = context.background(); - var currSourceName = null; - var metadata = {}; - var metadataKeys = [ - 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy' - ]; - - var debouncedRedraw = debounce(redraw, 250); - - function redraw(selection) { - var source = background.baseLayerSource(); - if (!source) return; - - var isDG = (source.id.match(/^DigitalGlobe/i) !== null); - - if (currSourceName !== source.name()) { - currSourceName = source.name(); - metadata = {}; - } - - selection.html(''); - - var list = selection - .append('ul') - .attr('class', 'background-info'); - - list - .append('li') - .text(currSourceName); - - metadataKeys.forEach(function(k) { - // DigitalGlobe vintage is available in raster layers for now. - if (isDG && k === 'vintage') return; - - list - .append('li') - .attr('class', 'background-info-list-' + k) - .classed('hide', !metadata[k]) - .text(_t('info_panels.background.' + k) + ':') - .append('span') - .attr('class', 'background-info-span-' + k) - .text(metadata[k]); - }); - - debouncedGetMetadata(selection); - - var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles'; - - selection - .append('a') - .text(_t('info_panels.background.' + toggleTiles)) - .attr('href', '#') - .attr('class', 'button button-toggle-tiles') - .on('click', function() { - event.preventDefault(); - context.setDebug('tile', !context.getDebug('tile')); - selection.call(redraw); - }); - - if (isDG) { - var key = source.id + '-vintage'; - var sourceVintage = context.background().findSource(key); - var showsVintage = context.background().showsLayer(sourceVintage); - var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage'; - selection - .append('a') - .text(_t('info_panels.background.' + toggleVintage)) - .attr('href', '#') - .attr('class', 'button button-toggle-vintage') - .on('click', function() { - event.preventDefault(); - context.background().toggleOverlayLayer(sourceVintage); - selection.call(redraw); - }); - } - - // disable if necessary - ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) { - if (source.id !== layerId) { - var key = layerId + '-vintage'; - var sourceVintage = context.background().findSource(key); - if (context.background().showsLayer(sourceVintage)) { - context.background().toggleOverlayLayer(sourceVintage); - } - } - }); - } - - - var debouncedGetMetadata = debounce(getMetadata, 250); - - function getMetadata(selection) { - var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center - if (tile.empty()) return; - - var sourceName = currSourceName; - var d = tile.datum(); - var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom()); - var center = context.map().center(); - - // update zoom - metadata.zoom = String(zoom); - selection.selectAll('.background-info-list-zoom') - .classed('hide', false) - .selectAll('.background-info-span-zoom') - .text(metadata.zoom); - - if (!d || !d.length >= 3) return; - - background.baseLayerSource().getMetadata(center, d, function(err, result) { - if (err || currSourceName !== sourceName) return; - - // update vintage - var vintage = result.vintage; - metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown'); - selection.selectAll('.background-info-list-vintage') - .classed('hide', false) - .selectAll('.background-info-span-vintage') - .text(metadata.vintage); - - // update other metdata - metadataKeys.forEach(function(k) { - if (k === 'zoom' || k === 'vintage') return; // done already - var val = result[k]; - metadata[k] = val; - selection.selectAll('.background-info-list-' + k) - .classed('hide', !val) - .selectAll('.background-info-span-' + k) - .text(val); - }); - }); - } - - - var panel = function(selection) { - selection.call(redraw); - - context.map() - .on('drawn.info-background', function() { - selection.call(debouncedRedraw); - }) - .on('move.info-background', function() { - selection.call(debouncedGetMetadata); - }); - - }; - - panel.off = function() { - context.map() - .on('drawn.info-background', null) - .on('move.info-background', null); - }; - - panel.id = 'background'; - panel.title = _t('info_panels.background.title'); - panel.key = _t('info_panels.background.key'); - - - return panel; - } - - function uiPanelHistory(context) { - var osm; - - function displayTimestamp(timestamp) { - if (!timestamp) return _t('info_panels.history.unknown'); - var options = { - day: 'numeric', month: 'short', year: 'numeric', - hour: 'numeric', minute: 'numeric', second: 'numeric' - }; - var d = new Date(timestamp); - if (isNaN(d.getTime())) return _t('info_panels.history.unknown'); - return d.toLocaleString(_mainLocalizer.localeCode(), options); - } - - - function displayUser(selection, userName) { - if (!userName) { - selection - .append('span') - .text(_t('info_panels.history.unknown')); - return; - } - - selection - .append('span') - .attr('class', 'user-name') - .text(userName); - - var links = selection - .append('div') - .attr('class', 'links'); - - if (osm) { - links - .append('a') - .attr('class', 'user-osm-link') - .attr('href', osm.userURL(userName)) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('OSM'); - } - - links - .append('a') - .attr('class', 'user-hdyc-link') - .attr('href', 'https://hdyc.neis-one.org/?' + userName) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('HDYC'); - } - - - function displayChangeset(selection, changeset) { - if (!changeset) { - selection - .append('span') - .text(_t('info_panels.history.unknown')); - return; - } - - selection - .append('span') - .attr('class', 'changeset-id') - .text(changeset); - - var links = selection - .append('div') - .attr('class', 'links'); - - if (osm) { - links - .append('a') - .attr('class', 'changeset-osm-link') - .attr('href', osm.changesetURL(changeset)) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('OSM'); - } - - links - .append('a') - .attr('class', 'changeset-osmcha-link') - .attr('href', 'https://osmcha.org/changesets/' + changeset) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('OSMCha'); - - links - .append('a') - .attr('class', 'changeset-achavi-link') - .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('Achavi'); - } - - - function redraw(selection) { - var selectedNoteID = context.selectedNoteID(); - osm = context.connection(); - - var selected, note, entity; - if (selectedNoteID && osm) { // selected 1 note - selected = [ _t('note.note') + ' ' + selectedNoteID ]; - note = osm.getNote(selectedNoteID); - } else { // selected 1..n entities - selected = context.selectedIDs() - .filter(function(e) { return context.hasEntity(e); }); - if (selected.length) { - entity = context.entity(selected[0]); - } - } - - var singular = selected.length === 1 ? selected[0] : null; - - selection.html(''); - - selection - .append('h4') - .attr('class', 'history-heading') - .text(singular || _t('info_panels.history.selected', { n: selected.length })); - - if (!singular) return; - - if (entity) { - selection.call(redrawEntity, entity); - } else if (note) { - selection.call(redrawNote, note); - } - } - - - function redrawNote(selection, note) { - if (!note || note.isNew()) { - selection - .append('div') - .text(_t('info_panels.history.note_no_history')); - return; - } - - var list = selection - .append('ul'); - - list - .append('li') - .text(_t('info_panels.history.note_comments') + ':') - .append('span') - .text(note.comments.length); - - if (note.comments.length) { - list - .append('li') - .text(_t('info_panels.history.note_created_date') + ':') - .append('span') - .text(displayTimestamp(note.comments[0].date)); - - list - .append('li') - .text(_t('info_panels.history.note_created_user') + ':') - .call(displayUser, note.comments[0].user); - } - - if (osm) { - selection - .append('a') - .attr('class', 'view-history-on-osm') - .attr('target', '_blank') - .attr('tabindex', -1) - .attr('href', osm.noteURL(note)) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(_t('info_panels.history.note_link_text')); - } - } - - - function redrawEntity(selection, entity) { - if (!entity || entity.isNew()) { - selection - .append('div') - .text(_t('info_panels.history.no_history')); - return; - } - - var links = selection - .append('div') - .attr('class', 'links'); - - if (osm) { - links - .append('a') - .attr('class', 'view-history-on-osm') - .attr('href', osm.historyURL(entity)) - .attr('target', '_blank') - .attr('tabindex', -1) - .attr('title', _t('info_panels.history.link_text')) - .text('OSM'); - } - links - .append('a') - .attr('class', 'pewu-history-viewer-link') - .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId()) - .attr('target', '_blank') - .attr('tabindex', -1) - .text('PeWu'); - - var list = selection - .append('ul'); - - list - .append('li') - .text(_t('info_panels.history.version') + ':') - .append('span') - .text(entity.version); - - list - .append('li') - .text(_t('info_panels.history.last_edit') + ':') - .append('span') - .text(displayTimestamp(entity.timestamp)); - - list - .append('li') - .text(_t('info_panels.history.edited_by') + ':') - .call(displayUser, entity.user); - - list - .append('li') - .text(_t('info_panels.history.changeset') + ':') - .call(displayChangeset, entity.changeset); - } - - - var panel = function(selection) { - selection.call(redraw); - - context.map() - .on('drawn.info-history', function() { - selection.call(redraw); - }); - - context - .on('enter.info-history', function() { - selection.call(redraw); - }); - }; - - panel.off = function() { - context.map().on('drawn.info-history', null); - context.on('enter.info-history', null); - }; - - panel.id = 'history'; - panel.title = _t('info_panels.history.title'); - panel.key = _t('info_panels.history.key'); - - - return panel; - } - - var OSM_PRECISION = 7; - - /** - * Returns a localized representation of the given length measurement. - * - * @param {Number} m area in meters - * @param {Boolean} isImperial true for U.S. customary units; false for metric - */ - function displayLength(m, isImperial) { - var d = m * (isImperial ? 3.28084 : 1); - var unit; - - if (isImperial) { - if (d >= 5280) { - d /= 5280; - unit = 'miles'; - } else { - unit = 'feet'; - } - } else { - if (d >= 1000) { - d /= 1000; - unit = 'kilometers'; - } else { - unit = 'meters'; - } - } - - return _t('units.' + unit, { - quantity: d.toLocaleString(_mainLocalizer.localeCode(), { - maximumSignificantDigits: 4 - }) - }); - } - - /** - * Returns a localized representation of the given area measurement. - * - * @param {Number} m2 area in square meters - * @param {Boolean} isImperial true for U.S. customary units; false for metric - */ - function displayArea(m2, isImperial) { - var locale = _mainLocalizer.localeCode(); - var d = m2 * (isImperial ? 10.7639111056 : 1); - var d1, d2, area; - var unit1 = ''; - var unit2 = ''; - - if (isImperial) { - if (d >= 6969600) { // > 0.25mi² show mi² - d1 = d / 27878400; - unit1 = 'square_miles'; - } else { - d1 = d; - unit1 = 'square_feet'; - } - - if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres - d2 = d / 43560; - unit2 = 'acres'; - } - - } else { - if (d >= 250000) { // > 0.25km² show km² - d1 = d / 1000000; - unit1 = 'square_kilometers'; - } else { - d1 = d; - unit1 = 'square_meters'; - } - - if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares - d2 = d / 10000; - unit2 = 'hectares'; - } - } - - area = _t('units.' + unit1, { - quantity: d1.toLocaleString(locale, { - maximumSignificantDigits: 4 - }) - }); - - if (d2) { - return _t('units.area_pair', { - area1: area, - area2: _t('units.' + unit2, { - quantity: d2.toLocaleString(locale, { - maximumSignificantDigits: 2 - }) - }) - }); - } else { - return area; - } - } - - function wrap(x, min, max) { - var d = max - min; - return ((x - min) % d + d) % d + min; - } - - function clamp$1(x, min, max) { - return Math.max(min, Math.min(x, max)); - } - - function displayCoordinate(deg, pos, neg) { - var locale = _mainLocalizer.localeCode(); - var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60; - var sec = (min - Math.floor(min)) * 60; - var displayDegrees = _t('units.arcdegrees', { - quantity: Math.floor(Math.abs(deg)).toLocaleString(locale) - }); - var displayCoordinate; - - if (Math.floor(sec) > 0) { - displayCoordinate = displayDegrees + - _t('units.arcminutes', { - quantity: Math.floor(min).toLocaleString(locale) - }) + - _t('units.arcseconds', { - quantity: Math.round(sec).toLocaleString(locale) - }); - } else if (Math.floor(min) > 0) { - displayCoordinate = displayDegrees + - _t('units.arcminutes', { - quantity: Math.round(min).toLocaleString(locale) - }); - } else { - displayCoordinate = _t('units.arcdegrees', { - quantity: Math.round(Math.abs(deg)).toLocaleString(locale) - }); - } - - if (deg === 0) { - return displayCoordinate; - } else { - return _t('units.coordinate', { - coordinate: displayCoordinate, - direction: _t('units.' + (deg > 0 ? pos : neg)) - }); - } - } - - /** - * Returns given coordinate pair in degree-minute-second format. - * - * @param {Array} coord longitude and latitude - */ - function dmsCoordinatePair(coord) { - return _t('units.coordinate_pair', { - latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'), - longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west') - }); - } - - /** - * Returns the given coordinate pair in decimal format. - * note: unlocalized to avoid comma ambiguity - see #4765 - * - * @param {Array} coord longitude and latitude - */ - function decimalCoordinatePair(coord) { - return _t('units.coordinate_pair', { - latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION), - longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION) - }); - } - - function uiPanelLocation(context) { - var currLocation = ''; - - - function redraw(selection) { - selection.html(''); - - var list = selection - .append('ul'); - - // Mouse coordinates - var coord = context.map().mouseCoordinates(); - if (coord.some(isNaN)) { - coord = context.map().center(); - } - - list - .append('li') - .text(dmsCoordinatePair(coord)) - .append('li') - .text(decimalCoordinatePair(coord)); - - // Location Info - selection - .append('div') - .attr('class', 'location-info') - .text(currLocation || ' '); - - debouncedGetLocation(selection, coord); - } - - - var debouncedGetLocation = debounce(getLocation, 250); - function getLocation(selection, coord) { - if (!services.geocoder) { - currLocation = _t('info_panels.location.unknown_location'); - selection.selectAll('.location-info') - .text(currLocation); - } else { - services.geocoder.reverse(coord, function(err, result) { - currLocation = result ? result.display_name : _t('info_panels.location.unknown_location'); - selection.selectAll('.location-info') - .text(currLocation); - }); - } - } - - - var panel = function(selection) { - selection.call(redraw); - - context.surface() - .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() { - selection.call(redraw); - }); - }; - - panel.off = function() { - context.surface() - .on('.info-location', null); - }; - - panel.id = 'location'; - panel.title = _t('info_panels.location.title'); - panel.key = _t('info_panels.location.key'); - - - return panel; - } - - function uiPanelMeasurement(context) { - var locale = _mainLocalizer.localeCode(); - var isImperial = !_mainLocalizer.usesMetric(); - - - function radiansToMeters(r) { - // using WGS84 authalic radius (6371007.1809 m) - return r * 6371007.1809; - } - - function steradiansToSqmeters(r) { - // http://gis.stackexchange.com/a/124857/40446 - return r / (4 * Math.PI) * 510065621724000; - } - - - function toLineString(feature) { - if (feature.type === 'LineString') return feature; - - var result = { type: 'LineString', coordinates: [] }; - if (feature.type === 'Polygon') { - result.coordinates = feature.coordinates[0]; - } else if (feature.type === 'MultiPolygon') { - result.coordinates = feature.coordinates[0][0]; - } - - return result; - } - - - function redraw(selection) { - var graph = context.graph(); - var selectedNoteID = context.selectedNoteID(); - var osm = services.osm; - - var heading; - var center, location, centroid; - var closed, geometry; - var totalNodeCount, length = 0, area = 0; - - if (selectedNoteID && osm) { // selected 1 note - - var note = osm.getNote(selectedNoteID); - heading = _t('note.note') + ' ' + selectedNoteID; - location = note.loc; - geometry = 'note'; - - } else { // selected 1..n entities - var selectedIDs = context.selectedIDs().filter(function(id) { - return context.hasEntity(id); - }); - var selected = selectedIDs.map(function(id) { - return context.entity(id); - }); - - heading = selected.length === 1 ? selected[0].id : - _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) }); - - if (selected.length) { - var extent = geoExtent(); - for (var i in selected) { - var entity = selected[i]; - extent._extend(entity.extent(graph)); - - geometry = entity.geometry(graph); - if (geometry === 'line' || geometry === 'area') { - closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate()); - var feature = entity.asGeoJSON(graph); - length += radiansToMeters(d3_geoLength(toLineString(feature))); - centroid = d3_geoCentroid(feature); - if (closed) { - area += steradiansToSqmeters(entity.area(graph)); - } - } - } - - if (selected.length > 1) { - geometry = null; - closed = null; - centroid = null; - } - - if (selected.length === 1 && selected[0].type === 'node') { - location = selected[0].loc; - } else { - totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length; - } - - if (!location && !centroid) { - center = extent.center(); - } - } - } - - selection.html(''); - - if (heading) { - selection - .append('h4') - .attr('class', 'measurement-heading') - .text(heading); - } - - var list = selection - .append('ul'); - var coordItem; - - if (geometry) { - list - .append('li') - .text(_t('info_panels.measurement.geometry') + ':') - .append('span') - .text( - closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry) - ); - } - - if (totalNodeCount) { - list - .append('li') - .text(_t('info_panels.measurement.node_count') + ':') - .append('span') - .text(totalNodeCount.toLocaleString(locale)); - } - - if (area) { - list - .append('li') - .text(_t('info_panels.measurement.area') + ':') - .append('span') - .text(displayArea(area, isImperial)); - } - - if (length) { - var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length')); - list - .append('li') - .text(lengthLabel + ':') - .append('span') - .text(displayLength(length, isImperial)); - } - - if (location) { - coordItem = list - .append('li') - .text(_t('info_panels.measurement.location') + ':'); - coordItem.append('span') - .text(dmsCoordinatePair(location)); - coordItem.append('span') - .text(decimalCoordinatePair(location)); - } - - if (centroid) { - coordItem = list - .append('li') - .text(_t('info_panels.measurement.centroid') + ':'); - coordItem.append('span') - .text(dmsCoordinatePair(centroid)); - coordItem.append('span') - .text(decimalCoordinatePair(centroid)); - } - - if (center) { - coordItem = list - .append('li') - .text(_t('info_panels.measurement.center') + ':'); - coordItem.append('span') - .text(dmsCoordinatePair(center)); - coordItem.append('span') - .text(decimalCoordinatePair(center)); - } - - if (length || area) { - var toggle = isImperial ? 'imperial' : 'metric'; - selection - .append('a') - .text(_t('info_panels.measurement.' + toggle)) - .attr('href', '#') - .attr('class', 'button button-toggle-units') - .on('click', function() { - event.preventDefault(); - isImperial = !isImperial; - selection.call(redraw); - }); - } - } - - - var panel = function(selection) { - selection.call(redraw); - - context.map() - .on('drawn.info-measurement', function() { - selection.call(redraw); - }); - - context - .on('enter.info-measurement', function() { - selection.call(redraw); - }); - }; - - panel.off = function() { - context.map().on('drawn.info-measurement', null); - context.on('enter.info-measurement', null); - }; - - panel.id = 'measurement'; - panel.title = _t('info_panels.measurement.title'); - panel.key = _t('info_panels.measurement.key'); - - - return panel; - } - - var uiInfoPanels = { - background: uiPanelBackground, - history: uiPanelHistory, - location: uiPanelLocation, - measurement: uiPanelMeasurement, - }; - - function uiInfo(context) { - var ids = Object.keys(uiInfoPanels); - var wasActive = ['measurement']; - var panels = {}; - var active = {}; - - // create panels - ids.forEach(function(k) { - if (!panels[k]) { - panels[k] = uiInfoPanels[k](context); - active[k] = false; - } - }); - - - function info(selection) { - - function redraw() { - var activeids = ids.filter(function(k) { return active[k]; }).sort(); - - var containers = infoPanels.selectAll('.panel-container') - .data(activeids, function(k) { return k; }); - - containers.exit() - .style('opacity', 1) - .transition() - .duration(200) - .style('opacity', 0) - .on('end', function(d) { - select(this) - .call(panels[d].off) - .remove(); - }); - - var enter = containers.enter() - .append('div') - .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; }); - - enter - .style('opacity', 0) - .transition() - .duration(200) - .style('opacity', 1); - - var title = enter - .append('div') - .attr('class', 'panel-title fillD2'); - - title - .append('h3') - .text(function(d) { return panels[d].title; }); - - title - .append('button') - .attr('class', 'close') - .on('click', function (d) { info.toggle(d); }) - .call(svgIcon('#iD-icon-close')); - - enter - .append('div') - .attr('class', function(d) { return 'panel-content panel-content-' + d; }); - - - // redraw the panels - infoPanels.selectAll('.panel-content') - .each(function(d) { - select(this).call(panels[d]); - }); - } - - - info.toggle = function(which) { - if (event) { - event.stopImmediatePropagation(); - event.preventDefault(); - } - - var activeids = ids.filter(function(k) { return active[k]; }); - - if (which) { // toggle one - active[which] = !active[which]; - if (activeids.length === 1 && activeids[0] === which) { // none active anymore - wasActive = [which]; - } - - context.container().select('.' + which + '-panel-toggle-item') - .classed('active', active[which]) - .select('input') - .property('checked', active[which]); - - } else { // toggle all - if (activeids.length) { - wasActive = activeids; - activeids.forEach(function(k) { active[k] = false; }); - } else { - wasActive.forEach(function(k) { active[k] = true; }); - } - } - - redraw(); - }; - - - var infoPanels = selection.selectAll('.info-panels') - .data([0]); - - infoPanels = infoPanels.enter() - .append('div') - .attr('class', 'info-panels') - .merge(infoPanels); - - redraw(); - - context.keybinding() - .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle); - - ids.forEach(function(k) { - var key = _t('info_panels.' + k + '.key', { default: null }); - if (!key) return; - context.keybinding() - .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); }); - }); - } - - return info; - } - - function pointBox(loc, context) { - var rect = context.surfaceRect(); - var point = context.curtainProjection(loc); - return { - left: point[0] + rect.left - 40, - top: point[1] + rect.top - 60, - width: 80, - height: 90 - }; - } - - - function pad(locOrBox, padding, context) { - var box; - if (locOrBox instanceof Array) { - var rect = context.surfaceRect(); - var point = context.curtainProjection(locOrBox); - box = { - left: point[0] + rect.left, - top: point[1] + rect.top - }; - } else { - box = locOrBox; - } - - return { - left: box.left - padding, - top: box.top - padding, - width: (box.width || 0) + 2 * padding, - height: (box.width || 0) + 2 * padding - }; - } - - - function icon(name, svgklass, useklass) { - return '' + - ''; - } - - var helpStringReplacements; - - // Returns the localized string for `id` with a standardized set of icon, key, and - // label replacements suitable for tutorials and documentation. Optionally supplemented - // with custom `replacements` - function helpString(id, replacements) { - // only load these the first time - if (!helpStringReplacements) helpStringReplacements = { - // insert icons corresponding to various UI elements - point_icon: icon('#iD-icon-point', 'pre-text'), - line_icon: icon('#iD-icon-line', 'pre-text'), - area_icon: icon('#iD-icon-area', 'pre-text'), - note_icon: icon('#iD-icon-note', 'pre-text add-note'), - plus: icon('#iD-icon-plus', 'pre-text'), - minus: icon('#iD-icon-minus', 'pre-text'), - move_icon: icon('#iD-operation-move', 'pre-text operation'), - merge_icon: icon('#iD-operation-merge', 'pre-text operation'), - delete_icon: icon('#iD-operation-delete', 'pre-text operation'), - circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'), - split_icon: icon('#iD-operation-split', 'pre-text operation'), - orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'), - disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'), - layers_icon: icon('#iD-icon-layers', 'pre-text'), - data_icon: icon('#iD-icon-data', 'pre-text'), - inspect: icon('#iD-icon-inspect', 'pre-text'), - help_icon: icon('#iD-icon-help', 'pre-text'), - undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'), - redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'), - save_icon: icon('#iD-icon-save', 'pre-text'), - leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'), - rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'), - mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'), - tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'), - doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'), - longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'), - touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'), - pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'), - - // insert keys; may be localized and platform-dependent - shift: uiCmd.display('⇧'), - alt: uiCmd.display('⌥'), - return: uiCmd.display('↵'), - esc: _t('shortcuts.key.esc'), - space: _t('shortcuts.key.space'), - add_note_key: _t('modes.add_note.key'), - help_key: _t('help.key'), - shortcuts_key: _t('shortcuts.toggle.key'), - - // reference localized UI labels directly so that they'll always match - save: _t('save.title'), - undo: _t('undo.title'), - redo: _t('redo.title'), - upload: _t('commit.save'), - point: _t('modes.add_point.title'), - line: _t('modes.add_line.title'), - area: _t('modes.add_area.title'), - note: _t('modes.add_note.title'), - delete: _t('operations.delete.title'), - move: _t('operations.move.title'), - orthogonalize: _t('operations.orthogonalize.title'), - circularize: _t('operations.circularize.title'), - merge: _t('operations.merge.title'), - disconnect: _t('operations.disconnect.title'), - split: _t('operations.split.title'), - map_data: _t('map_data.title'), - osm_notes: _t('map_data.layers.notes.title'), - fields: _t('inspector.fields'), - tags: _t('inspector.tags'), - relations: _t('inspector.relations'), - new_relation: _t('inspector.new_relation'), - turn_restrictions: _t('presets.fields.restrictions.label'), - background_settings: _t('background.description'), - imagery_offset: _t('background.fix_misalignment'), - start_the_walkthrough: _t('splash.walkthrough'), - help: _t('help.title'), - ok: _t('intro.ok') - }; - - var reps; - if (replacements) { - reps = Object.assign(replacements, helpStringReplacements); - } else { - reps = helpStringReplacements; - } - - return _t(id, reps) - // use keyboard key styling for shortcuts - .replace(/\`(.*?)\`/g, '$1'); - } - - - function slugify(text) { - return text.toString().toLowerCase() - .replace(/\s+/g, '-') // Replace spaces with - - .replace(/[^\w\-]+/g, '') // Remove all non-word chars - .replace(/\-\-+/g, '-') // Replace multiple - with single - - .replace(/^-+/, '') // Trim - from start of text - .replace(/-+$/, ''); // Trim - from end of text - } - - - // console warning for missing walkthrough names - var missingStrings = {}; - function checkKey(key, text) { - if (_t(key, { default: undefined}) === undefined) { - if (missingStrings.hasOwnProperty(key)) return; // warn once - missingStrings[key] = text; - var missing = key + ': ' + text; - if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line - } - } - - - function localize(obj) { - var key; - - // Assign name if entity has one.. - var name = obj.tags && obj.tags.name; - if (name) { - key = 'intro.graph.name.' + slugify(name); - obj.tags.name = _t(key, { default: name }); - checkKey(key, name); - } - - // Assign street name if entity has one.. - var street = obj.tags && obj.tags['addr:street']; - if (street) { - key = 'intro.graph.name.' + slugify(street); - obj.tags['addr:street'] = _t(key, { default: street }); - checkKey(key, street); - - // Add address details common across walkthrough.. - var addrTags = [ - 'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', - 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb' - ]; - addrTags.forEach(function(k) { - var key = 'intro.graph.' + k; - var tag = 'addr:' + k; - var val = obj.tags && obj.tags[tag]; - var str = _t(key, { default: val }); - - if (str) { - if (str.match(/^<.*>$/) !== null) { - delete obj.tags[tag]; - } else { - obj.tags[tag] = str; - } - } - }); - } - - return obj; - } - - - // Used to detect squareness.. some duplicataion of code from actionOrthogonalize. - function isMostlySquare(points) { - // note: uses 15 here instead of the 12 from actionOrthogonalize because - // actionOrthogonalize can actually straighten some larger angles as it iterates - var threshold = 15; // degrees within right or straight - var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right - var upperBound = Math.cos(threshold * Math.PI / 180); // near straight - - for (var i = 0; i < points.length; i++) { - var a = points[(i - 1 + points.length) % points.length]; - var origin = points[i]; - var b = points[(i + 1) % points.length]; - - var dotp = geoVecNormalizedDot(a, b, origin); - var mag = Math.abs(dotp); - if (mag > lowerBound && mag < upperBound) { - return false; - } - } - - return true; - } - - - function selectMenuItem(context, operation) { - return context.container().select('.edit-menu .edit-menu-item-' + operation); - } - - - function transitionTime(point1, point2) { - var distance = geoSphericalDistance(point1, point2); - if (distance === 0) - return 0; - else if (distance < 80) - return 500; - else - return 1000; - } - - // Tooltips and svg mask used to highlight certain features - function uiCurtain(containerNode) { - - var surface = select(null), - tooltip = select(null), - darkness = select(null); - - function curtain(selection) { - surface = selection - .append('svg') - .attr('class', 'curtain') - .style('top', 0) - .style('left', 0); - - darkness = surface.append('path') - .attr('x', 0) - .attr('y', 0) - .attr('class', 'curtain-darkness'); - - select(window).on('resize.curtain', resize); - - tooltip = selection.append('div') - .attr('class', 'tooltip'); - - tooltip - .append('div') - .attr('class', 'popover-arrow'); - - tooltip - .append('div') - .attr('class', 'popover-inner'); - - resize(); - - - function resize() { - surface - .attr('width', containerNode.clientWidth) - .attr('height', containerNode.clientHeight); - curtain.cut(darkness.datum()); - } - } - - - /** - * Reveal cuts the curtain to highlight the given box, - * and shows a tooltip with instructions next to the box. - * - * @param {String|ClientRect} [box] box used to cut the curtain - * @param {String} [text] text for a tooltip - * @param {Object} [options] - * @param {string} [options.tooltipClass] optional class to add to the tooltip - * @param {integer} [options.duration] transition time in milliseconds - * @param {string} [options.buttonText] if set, create a button with this text label - * @param {function} [options.buttonCallback] if set, the callback for the button - * @param {function} [options.padding] extra margin in px to put around bbox - * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain - */ - curtain.reveal = function(box, text, options) { - options = options || {}; - - if (typeof box === 'string') { - box = select(box).node(); - } - if (box && box.getBoundingClientRect) { - box = copyBox(box.getBoundingClientRect()); - var containerRect = containerNode.getBoundingClientRect(); - box.top -= containerRect.top; - box.left -= containerRect.left; - } - if (box && options.padding) { - box.top -= options.padding; - box.left -= options.padding; - box.bottom += options.padding; - box.right += options.padding; - box.height += options.padding * 2; - box.width += options.padding * 2; - } - - var tooltipBox; - if (options.tooltipBox) { - tooltipBox = options.tooltipBox; - if (typeof tooltipBox === 'string') { - tooltipBox = select(tooltipBox).node(); - } - if (tooltipBox && tooltipBox.getBoundingClientRect) { - tooltipBox = copyBox(tooltipBox.getBoundingClientRect()); - } - } else { - tooltipBox = box; - } - - if (tooltipBox && text) { - // pseudo markdown bold text for the instruction section.. - var parts = text.split('**'); - var html = parts[0] ? '' + parts[0] + '' : ''; - if (parts[1]) { - html += '' + parts[1] + ''; - } - - html = html.replace(/\*(.*?)\*/g, '$1'); // emphasis - html = html.replace(/\{br\}/g, '

    '); // linebreak - - if (options.buttonText && options.buttonCallback) { - html += '
    ' + - '
    '; - } - - var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || ''); - tooltip - .classed(classes, true) - .selectAll('.popover-inner') - .html(html); - - if (options.buttonText && options.buttonCallback) { - var button = tooltip.selectAll('.button-section .button.action'); - button - .on('click', function() { - event.preventDefault(); - options.buttonCallback(); - }); - } - - var tip = copyBox(tooltip.node().getBoundingClientRect()), - w = containerNode.clientWidth, - h = containerNode.clientHeight, - tooltipWidth = 200, - tooltipArrow = 5, - side, pos; - - - // hack: this will have bottom placement, - // so need to reserve extra space for the tooltip illustration. - if (options.tooltipClass === 'intro-mouse') { - tip.height += 80; - } - - // trim box dimensions to just the portion that fits in the container.. - if (tooltipBox.top + tooltipBox.height > h) { - tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h); - } - if (tooltipBox.left + tooltipBox.width > w) { - tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w); - } - - // determine tooltip placement.. - - if (tooltipBox.top + tooltipBox.height < 100) { - // tooltip below box.. - side = 'bottom'; - pos = [ - tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, - tooltipBox.top + tooltipBox.height - ]; - - } else if (tooltipBox.top > h - 140) { - // tooltip above box.. - side = 'top'; - pos = [ - tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, - tooltipBox.top - tip.height - ]; - - } else { - // tooltip to the side of the tooltipBox.. - var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2; - - if (_mainLocalizer.textDirection() === 'rtl') { - if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) { - side = 'right'; - pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY]; - - } else { - side = 'left'; - pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY]; - } - - } else { - if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) { - side = 'left'; - pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY]; - } - else { - side = 'right'; - pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY]; - } - } - } - - if (options.duration !== 0 || !tooltip.classed(side)) { - tooltip.call(uiToggle(true)); - } - - tooltip - .style('top', pos[1] + 'px') - .style('left', pos[0] + 'px') - .attr('class', classes + ' ' + side); - - - // shift popover-inner if it is very close to the top or bottom edge - // (doesn't affect the placement of the popover-arrow) - var shiftY = 0; - if (side === 'left' || side === 'right') { - if (pos[1] < 60) { - shiftY = 60 - pos[1]; - } - else if (pos[1] + tip.height > h - 100) { - shiftY = h - pos[1] - tip.height - 100; - } - } - tooltip.selectAll('.popover-inner') - .style('top', shiftY + 'px'); - - } else { - tooltip - .classed('in', false) - .call(uiToggle(false)); - } - - curtain.cut(box, options.duration); - - return tooltip; - }; - - - curtain.cut = function(datum, duration) { - darkness.datum(datum) - .interrupt(); - - var selection; - if (duration === 0) { - selection = darkness; - } else { - selection = darkness - .transition() - .duration(duration || 600) - .ease(linear$1); - } - - selection - .attr('d', function(d) { - var containerWidth = containerNode.clientWidth; - var containerHeight = containerNode.clientHeight; - var string = 'M 0,0 L 0,' + containerHeight + ' L ' + - containerWidth + ',' + containerHeight + 'L' + - containerWidth + ',0 Z'; - - if (!d) return string; - return string + 'M' + - d.left + ',' + d.top + 'L' + - d.left + ',' + (d.top + d.height) + 'L' + - (d.left + d.width) + ',' + (d.top + d.height) + 'L' + - (d.left + d.width) + ',' + (d.top) + 'Z'; - - }); - }; - - - curtain.remove = function() { - surface.remove(); - tooltip.remove(); - select(window).on('resize.curtain', null); - }; - - - // ClientRects are immutable, so copy them to an object, - // in case we need to trim the height/width. - function copyBox(src) { - return { - top: src.top, - right: src.right, - bottom: src.bottom, - left: src.left, - width: src.width, - height: src.height - }; - } - - - return curtain; - } - - function uiIntroWelcome(context, reveal) { - var dispatch$1 = dispatch('done'); - - var chapter = { - title: 'intro.welcome.title' - }; - - - function welcome() { - context.map().centerZoom([-85.63591, 41.94285], 19); - reveal('.intro-nav-wrap .chapter-welcome', - helpString('intro.welcome.welcome'), - { buttonText: _t('intro.ok'), buttonCallback: practice } - ); - } - - function practice() { - reveal('.intro-nav-wrap .chapter-welcome', - helpString('intro.welcome.practice'), - { buttonText: _t('intro.ok'), buttonCallback: words } - ); - } - - function words() { - reveal('.intro-nav-wrap .chapter-welcome', - helpString('intro.welcome.words'), - { buttonText: _t('intro.ok'), buttonCallback: chapters } - ); - } - - - function chapters() { - dispatch$1.call('done'); - reveal('.intro-nav-wrap .chapter-navigation', - helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') }) - ); - } - - - chapter.enter = function() { - welcome(); - }; - - - chapter.exit = function() { - context.container().select('.curtain-tooltip.intro-mouse') - .selectAll('.counter') - .remove(); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroNavigation(context, reveal) { - var dispatch$1 = dispatch('done'); - var timeouts = []; - var hallId = 'n2061'; - var townHall = [-85.63591, 41.94285]; - var springStreetId = 'w397'; - var springStreetEndId = 'n1834'; - var springStreet = [-85.63582, 41.94255]; - var onewayField = _mainPresetIndex.field('oneway'); - var maxspeedField = _mainPresetIndex.field('maxspeed'); - - - var chapter = { - title: 'intro.navigation.title' - }; - - - function timeout(f, t) { - timeouts.push(window.setTimeout(f, t)); - } - - - function eventCancel() { - event.stopPropagation(); - event.preventDefault(); - } - - - function isTownHallSelected() { - var ids = context.selectedIDs(); - return ids.length === 1 && ids[0] === hallId; - } - - - function dragMap() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - - var msec = transitionTime(townHall, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(townHall, 19, msec); - - timeout(function() { - var centerStart = context.map().center(); - - var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch'; - var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId); - reveal('.surface', dragString); - context.map().on('drawn.intro', function() { - reveal('.surface', dragString, { duration: 0 }); - }); - - context.map().on('move.intro', function() { - var centerNow = context.map().center(); - if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) { - context.map().on('move.intro', null); - timeout(function() { continueTo(zoomMap); }, 3000); - } - }); - - }, msec + 100); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function zoomMap() { - var zoomStart = context.map().zoom(); - - var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch'; - var zoomString = helpString('intro.navigation.' + textId); - - reveal('.surface', zoomString); - - context.map().on('drawn.intro', function() { - reveal('.surface', zoomString, { duration: 0 }); - }); - - context.map().on('move.intro', function() { - if (context.map().zoom() !== zoomStart) { - context.map().on('move.intro', null); - timeout(function() { continueTo(features); }, 3000); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function features() { - var onClick = function() { continueTo(pointsLinesAreas); }; - - reveal('.surface', helpString('intro.navigation.features'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.map().on('drawn.intro', function() { - reveal('.surface', helpString('intro.navigation.features'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - - function continueTo(nextStep) { - context.map().on('drawn.intro', null); - nextStep(); - } - } - - function pointsLinesAreas() { - var onClick = function() { continueTo(nodesWays); }; - - reveal('.surface', helpString('intro.navigation.points_lines_areas'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.map().on('drawn.intro', function() { - reveal('.surface', helpString('intro.navigation.points_lines_areas'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - - function continueTo(nextStep) { - context.map().on('drawn.intro', null); - nextStep(); - } - } - - function nodesWays() { - var onClick = function() { continueTo(clickTownHall); }; - - reveal('.surface', helpString('intro.navigation.nodes_ways'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.map().on('drawn.intro', function() { - reveal('.surface', helpString('intro.navigation.nodes_ways'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - - function continueTo(nextStep) { - context.map().on('drawn.intro', null); - nextStep(); - } - } - - function clickTownHall() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - - var entity = context.hasEntity(hallId); - if (!entity) return; - reveal(null, null, { duration: 0 }); - context.map().centerZoomEase(entity.loc, 19, 500); - - timeout(function() { - var entity = context.hasEntity(hallId); - if (!entity) return; - var box = pointBox(entity.loc, context); - var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall'; - reveal(box, helpString('intro.navigation.' + textId)); - - context.map().on('move.intro drawn.intro', function() { - var entity = context.hasEntity(hallId); - if (!entity) return; - var box = pointBox(entity.loc, context); - reveal(box, helpString('intro.navigation.' + textId), { duration: 0 }); - }); - - context.on('enter.intro', function() { - if (isTownHallSelected()) continueTo(selectedTownHall); - }); - - }, 550); // after centerZoomEase - - context.history().on('change.intro', function() { - if (!context.hasEntity(hallId)) { - continueTo(clickTownHall); - } - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function selectedTownHall() { - if (!isTownHallSelected()) return clickTownHall(); - - var entity = context.hasEntity(hallId); - if (!entity) return clickTownHall(); - - var box = pointBox(entity.loc, context); - var onClick = function() { continueTo(editorTownHall); }; - - reveal(box, helpString('intro.navigation.selected_townhall'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.map().on('move.intro drawn.intro', function() { - var entity = context.hasEntity(hallId); - if (!entity) return; - var box = pointBox(entity.loc, context); - reveal(box, helpString('intro.navigation.selected_townhall'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - - context.history().on('change.intro', function() { - if (!context.hasEntity(hallId)) { - continueTo(clickTownHall); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function editorTownHall() { - if (!isTownHallSelected()) return clickTownHall(); - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - var onClick = function() { continueTo(presetTownHall); }; - - reveal('.entity-editor-pane', - helpString('intro.navigation.editor_townhall'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.on('exit.intro', function() { - continueTo(clickTownHall); - }); - - context.history().on('change.intro', function() { - if (!context.hasEntity(hallId)) { - continueTo(clickTownHall); - } - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - nextStep(); - } - } - - - function presetTownHall() { - if (!isTownHallSelected()) return clickTownHall(); - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - // preset match, in case the user happened to change it. - var entity = context.entity(context.selectedIDs()[0]); - var preset = _mainPresetIndex.match(entity, context.graph()); - - var onClick = function() { continueTo(fieldsTownHall); }; - - reveal('.entity-editor-pane .section-feature-type', - helpString('intro.navigation.preset_townhall', { preset: preset.name() }), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.on('exit.intro', function() { - continueTo(clickTownHall); - }); - - context.history().on('change.intro', function() { - if (!context.hasEntity(hallId)) { - continueTo(clickTownHall); - } - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - nextStep(); - } - } - - - function fieldsTownHall() { - if (!isTownHallSelected()) return clickTownHall(); - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - var onClick = function() { continueTo(closeTownHall); }; - - reveal('.entity-editor-pane .section-preset-fields', - helpString('intro.navigation.fields_townhall'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.on('exit.intro', function() { - continueTo(clickTownHall); - }); - - context.history().on('change.intro', function() { - if (!context.hasEntity(hallId)) { - continueTo(clickTownHall); - } - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - nextStep(); - } - } - - - function closeTownHall() { - if (!isTownHallSelected()) return clickTownHall(); - - var selector = '.entity-editor-pane button.close svg use'; - var href = select(selector).attr('href') || '#iD-icon-close'; - - reveal('.entity-editor-pane', - helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }) - ); - - context.on('exit.intro', function() { - continueTo(searchStreet); - }); - - context.history().on('change.intro', function() { - // update the close icon in the tooltip if the user edits something. - var selector = '.entity-editor-pane button.close svg use'; - var href = select(selector).attr('href') || '#iD-icon-close'; - - reveal('.entity-editor-pane', - helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }), - { duration: 0 } - ); - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function searchStreet() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); // ensure spring street exists - - var msec = transitionTime(springStreet, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it - - timeout(function() { - reveal('.search-header input', - helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') }) - ); - - context.container().select('.search-header input') - .on('keyup.intro', checkSearchResult); - }, msec + 100); - } - - - function checkSearchResult() { - var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item - var firstName = first.select('.entity-name'); - var name = _t('intro.graph.name.spring-street'); - - if (!firstName.empty() && firstName.text() === name) { - reveal(first.node(), - helpString('intro.navigation.choose_street', { name: name }), - { duration: 300 } - ); - - context.on('exit.intro', function() { - continueTo(selectedStreet); - }); - - context.container().select('.search-header input') - .on('keydown.intro', eventCancel, true) - .on('keyup.intro', null); - } - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.container().select('.search-header input') - .on('keydown.intro', null) - .on('keyup.intro', null); - nextStep(); - } - } - - - function selectedStreet() { - if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) { - return searchStreet(); - } - - var onClick = function() { continueTo(editorStreet); }; - var entity = context.entity(springStreetEndId); - var box = pointBox(entity.loc, context); - box.height = 500; - - reveal(box, - helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }), - { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - timeout(function() { - context.map().on('move.intro drawn.intro', function() { - var entity = context.hasEntity(springStreetEndId); - if (!entity) return; - var box = pointBox(entity.loc, context); - box.height = 500; - reveal(box, - helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - }, 600); // after reveal. - - context.on('enter.intro', function(mode) { - if (!context.hasEntity(springStreetId)) { - return continueTo(searchStreet); - } - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) { - // keep Spring Street selected.. - context.enter(modeSelect(context, [springStreetId])); - } - }); - - context.history().on('change.intro', function() { - if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) { - timeout(function() { - continueTo(searchStreet); - }, 300); // after any transition (e.g. if user deleted intersection) - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function editorStreet() { - var selector = '.entity-editor-pane button.close svg use'; - var href = select(selector).attr('href') || '#iD-icon-close'; - - reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' + - helpString('intro.navigation.editor_street', { - button: icon(href, 'pre-text'), - field1: onewayField.label(), - field2: maxspeedField.label() - })); - - context.on('exit.intro', function() { - continueTo(play); - }); - - context.history().on('change.intro', function() { - // update the close icon in the tooltip if the user edits something. - var selector = '.entity-editor-pane button.close svg use'; - var href = select(selector).attr('href') || '#iD-icon-close'; - - reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' + - helpString('intro.navigation.editor_street', { - button: icon(href, 'pre-text'), - field1: onewayField.label(), - field2: maxspeedField.label() - }), { duration: 0 } - ); - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function play() { - dispatch$1.call('done'); - reveal('.ideditor', - helpString('intro.navigation.play', { next: _t('intro.points.title') }), { - tooltipBox: '.intro-nav-wrap .chapter-point', - buttonText: _t('intro.ok'), - buttonCallback: function() { reveal('.ideditor'); } - } - ); - } - - - chapter.enter = function() { - dragMap(); - }; - - - chapter.exit = function() { - timeouts.forEach(window.clearTimeout); - context.on('enter.intro exit.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.search-header input').on('keydown.intro keyup.intro', null); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroPoint(context, reveal) { - var dispatch$1 = dispatch('done'); - var timeouts = []; - var intersection = [-85.63279, 41.94394]; - var building = [-85.632422, 41.944045]; - var cafePreset = _mainPresetIndex.item('amenity/cafe'); - var _pointID = null; - - - var chapter = { - title: 'intro.points.title' - }; - - - function timeout(f, t) { - timeouts.push(window.setTimeout(f, t)); - } - - - function eventCancel() { - event.stopPropagation(); - event.preventDefault(); - } - - - function addPoint() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - - var msec = transitionTime(intersection, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(intersection, 19, msec); - - timeout(function() { - var tooltip = reveal('button.add-point', - helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point')); - - _pointID = null; - - tooltip.selectAll('.popover-inner') - .insert('svg', 'span') - .attr('class', 'tooltip-illustration') - .append('use') - .attr('xlink:href', '#iD-graphic-points'); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'add-point') return; - continueTo(placePoint); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function placePoint() { - if (context.mode().id !== 'add-point') { - return chapter.restart(); - } - - var pointBox = pad(building, 150, context); - var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch'; - reveal(pointBox, helpString('intro.points.' + textId)); - - context.map().on('move.intro drawn.intro', function() { - pointBox = pad(building, 150, context); - reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return chapter.restart(); - _pointID = context.mode().selectedIDs()[0]; - continueTo(searchPreset); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function searchPreset() { - if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { - return addPoint(); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.points.search_cafe', { preset: cafePreset.name() }) - ); - - context.on('enter.intro', function(mode) { - if (!_pointID || !context.hasEntity(_pointID)) { - return continueTo(addPoint); - } - - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) { - // keep the user's point selected.. - context.enter(modeSelect(context, [_pointID])); - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.points.search_cafe', { preset: cafePreset.name() }) - ); - - context.history().on('change.intro', null); - } - }); - - - function checkPresetSearch() { - var first = context.container().select('.preset-list-item:first-child'); - - if (first.classed('preset-amenity-cafe')) { - context.container().select('.preset-search-input') - .on('keydown.intro', eventCancel, true) - .on('keyup.intro', null); - - reveal(first.select('.preset-list-button').node(), - helpString('intro.points.choose_cafe', { preset: cafePreset.name() }), - { duration: 300 } - ); - - context.history().on('change.intro', function() { - continueTo(aboutFeatureEditor); - }); - } - } - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - nextStep(); - } - } - - - function aboutFeatureEditor() { - if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { - return addPoint(); - } - - timeout(function() { - reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), { - tooltipClass: 'intro-points-describe', - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(addName); } - }); - }, 400); - - context.on('exit.intro', function() { - // if user leaves select mode here, just continue with the tutorial. - continueTo(reselectPoint); - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function addName() { - if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { - return addPoint(); - } - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name'); - - timeout(function() { - // It's possible for the user to add a name in a previous step.. - // If so, don't tell them to add the name in this step. - // Give them an OK button instead. - var entity = context.entity(_pointID); - if (entity.tags.name) { - var tooltip = reveal('.entity-editor-pane', addNameString, { - tooltipClass: 'intro-points-describe', - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(addCloseEditor); } - }); - tooltip.select('.instruction').style('display', 'none'); - - } else { - reveal('.entity-editor-pane', addNameString, - { tooltipClass: 'intro-points-describe' } - ); - } - }, 400); - - context.history().on('change.intro', function() { - continueTo(addCloseEditor); - }); - - context.on('exit.intro', function() { - // if user leaves select mode here, just continue with the tutorial. - continueTo(reselectPoint); - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function addCloseEditor() { - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - var selector = '.entity-editor-pane button.close svg use'; - var href = select(selector).attr('href') || '#iD-icon-close'; - - context.on('exit.intro', function() { - continueTo(reselectPoint); - }); - - reveal('.entity-editor-pane', - helpString('intro.points.add_close', { button: icon(href, 'pre-text') }) - ); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function reselectPoint() { - if (!_pointID) return chapter.restart(); - var entity = context.hasEntity(_pointID); - if (!entity) return chapter.restart(); - - // make sure it's still a cafe, in case user somehow changed it.. - var oldPreset = _mainPresetIndex.match(entity, context.graph()); - context.replace(actionChangePreset(_pointID, oldPreset, cafePreset)); - - context.enter(modeBrowse(context)); - - var msec = transitionTime(entity.loc, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerEase(entity.loc, msec); - - timeout(function() { - var box = pointBox(entity.loc, context); - reveal(box, helpString('intro.points.reselect'), { duration: 600 }); - - timeout(function() { - context.map().on('move.intro drawn.intro', function() { - var entity = context.hasEntity(_pointID); - if (!entity) return chapter.restart(); - var box = pointBox(entity.loc, context); - reveal(box, helpString('intro.points.reselect'), { duration: 0 }); - }); - }, 600); // after reveal.. - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return; - continueTo(updatePoint); - }); - - }, msec + 100); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function updatePoint() { - if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { - return continueTo(reselectPoint); - } - - // reset pane, in case user happened to untag the point.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - context.on('exit.intro', function() { - continueTo(reselectPoint); - }); - - context.history().on('change.intro', function() { - continueTo(updateCloseEditor); - }); - - timeout(function() { - reveal('.entity-editor-pane', helpString('intro.points.update'), - { tooltipClass: 'intro-points-describe' } - ); - }, 400); - - function continueTo(nextStep) { - context.on('exit.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function updateCloseEditor() { - if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) { - return continueTo(reselectPoint); - } - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - context.on('exit.intro', function() { - continueTo(rightClickPoint); - }); - - timeout(function() { - reveal('.entity-editor-pane', - helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') }) - ); - }, 500); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function rightClickPoint() { - if (!_pointID) return chapter.restart(); - var entity = context.hasEntity(_pointID); - if (!entity) return chapter.restart(); - - context.enter(modeBrowse(context)); - - var box = pointBox(entity.loc, context); - var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'; - reveal(box, helpString('intro.points.' + textId), { duration: 600 }); - - timeout(function() { - context.map().on('move.intro', function() { - var entity = context.hasEntity(_pointID); - if (!entity) return chapter.restart(); - var box = pointBox(entity.loc, context); - reveal(box, helpString('intro.points.' + textId), { duration: 0 }); - }); - }, 600); // after reveal - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return; - var ids = context.selectedIDs(); - if (ids.length !== 1 || ids[0] !== _pointID) return; - - timeout(function() { - var node = selectMenuItem(context, 'delete').node(); - if (!node) return; - continueTo(enterDelete); - }, 50); // after menu visible - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro', null); - nextStep(); - } - } - - - function enterDelete() { - if (!_pointID) return chapter.restart(); - var entity = context.hasEntity(_pointID); - if (!entity) return chapter.restart(); - - var node = selectMenuItem(context, 'delete').node(); - if (!node) { return continueTo(rightClickPoint); } - - reveal('.edit-menu', - helpString('intro.points.delete'), - { padding: 50 } - ); - - timeout(function() { - context.map().on('move.intro', function() { - reveal('.edit-menu', - helpString('intro.points.delete'), - { duration: 0, padding: 50 } - ); - }); - }, 300); // after menu visible - - context.on('exit.intro', function() { - if (!_pointID) return chapter.restart(); - var entity = context.hasEntity(_pointID); - if (entity) return continueTo(rightClickPoint); // point still exists - }); - - context.history().on('change.intro', function(changed) { - if (changed.deleted().length) { - continueTo(undo); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro', null); - context.history().on('change.intro', null); - context.on('exit.intro', null); - nextStep(); - } - } - - - function undo() { - context.history().on('change.intro', function() { - continueTo(play); - }); - - reveal('.top-toolbar button.undo-button', - helpString('intro.points.undo') - ); - - function continueTo(nextStep) { - context.history().on('change.intro', null); - nextStep(); - } - } - - - function play() { - dispatch$1.call('done'); - reveal('.ideditor', - helpString('intro.points.play', { next: _t('intro.areas.title') }), { - tooltipBox: '.intro-nav-wrap .chapter-area', - buttonText: _t('intro.ok'), - buttonCallback: function() { reveal('.ideditor'); } - } - ); - } - - - chapter.enter = function() { - addPoint(); - }; - - - chapter.exit = function() { - timeouts.forEach(window.clearTimeout); - context.on('enter.intro exit.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroArea(context, reveal) { - var dispatch$1 = dispatch('done'); - var playground = [-85.63552, 41.94159]; - var playgroundPreset = _mainPresetIndex.item('leisure/playground'); - var nameField = _mainPresetIndex.field('name'); - var descriptionField = _mainPresetIndex.field('description'); - var timeouts = []; - var _areaID; - - - var chapter = { - title: 'intro.areas.title' - }; - - - function timeout(f, t) { - timeouts.push(window.setTimeout(f, t)); - } - - - function eventCancel() { - event.stopPropagation(); - event.preventDefault(); - } - - - function revealPlayground(center, text, options) { - var padding = 180 * Math.pow(2, context.map().zoom() - 19.5); - var box = pad(center, padding, context); - reveal(box, text, options); - } - - - function addArea() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - _areaID = null; - - var msec = transitionTime(playground, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(playground, 19, msec); - - timeout(function() { - var tooltip = reveal('button.add-area', - helpString('intro.areas.add_playground')); - - tooltip.selectAll('.popover-inner') - .insert('svg', 'span') - .attr('class', 'tooltip-illustration') - .append('use') - .attr('xlink:href', '#iD-graphic-areas'); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'add-area') return; - continueTo(startPlayground); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function startPlayground() { - if (context.mode().id !== 'add-area') { - return chapter.restart(); - } - - _areaID = null; - context.map().zoomEase(19.5, 500); - - timeout(function() { - var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap'; - var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId); - revealPlayground(playground, - startDrawString, { duration: 250 } - ); - - timeout(function() { - context.map().on('move.intro drawn.intro', function() { - revealPlayground(playground, - startDrawString, { duration: 0 } - ); - }); - context.on('enter.intro', function(mode) { - if (mode.id !== 'draw-area') return chapter.restart(); - continueTo(continuePlayground); - }); - }, 250); // after reveal - - }, 550); // after easing - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function continuePlayground() { - if (context.mode().id !== 'draw-area') { - return chapter.restart(); - } - - _areaID = null; - revealPlayground(playground, - helpString('intro.areas.continue_playground'), - { duration: 250 } - ); - - timeout(function() { - context.map().on('move.intro drawn.intro', function() { - revealPlayground(playground, - helpString('intro.areas.continue_playground'), - { duration: 0 } - ); - }); - }, 250); // after reveal - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-area') { - var entity = context.hasEntity(context.selectedIDs()[0]); - if (entity && entity.nodes.length >= 6) { - return continueTo(finishPlayground); - } else { - return; - } - } else if (mode.id === 'select') { - _areaID = context.selectedIDs()[0]; - return continueTo(searchPresets); - } else { - return chapter.restart(); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function finishPlayground() { - if (context.mode().id !== 'draw-area') { - return chapter.restart(); - } - - _areaID = null; - - var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + - helpString('intro.areas.finish_playground'); - revealPlayground(playground, - finishString, { duration: 250 } - ); - - timeout(function() { - context.map().on('move.intro drawn.intro', function() { - revealPlayground(playground, - finishString, { duration: 0 } - ); - }); - }, 250); // after reveal - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-area') { - return; - } else if (mode.id === 'select') { - _areaID = context.selectedIDs()[0]; - return continueTo(searchPresets); - } else { - return chapter.restart(); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function searchPresets() { - if (!_areaID || !context.hasEntity(_areaID)) { - return addArea(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { - context.enter(modeSelect(context, [_areaID])); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.areas.search_playground', { preset: playgroundPreset.name() }) - ); - }, 400); // after preset list pane visible.. - - context.on('enter.intro', function(mode) { - if (!_areaID || !context.hasEntity(_areaID)) { - return continueTo(addArea); - } - - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) { - // keep the user's area selected.. - context.enter(modeSelect(context, [_areaID])); - - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.areas.search_playground', { preset: playgroundPreset.name() }) - ); - - context.history().on('change.intro', null); - } - }); - - function checkPresetSearch() { - var first = context.container().select('.preset-list-item:first-child'); - - if (first.classed('preset-leisure-playground')) { - reveal(first.select('.preset-list-button').node(), - helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }), - { duration: 300 } - ); - - context.container().select('.preset-search-input') - .on('keydown.intro', eventCancel, true) - .on('keyup.intro', null); - - context.history().on('change.intro', function() { - continueTo(clickAddField); - }); - } - } - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - nextStep(); - } - } - - - function clickAddField() { - if (!_areaID || !context.hasEntity(_areaID)) { - return addArea(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { - return searchPresets(); - } - - if (!context.container().select('.form-field-description').empty()) { - return continueTo(describePlayground); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - // It's possible for the user to add a description in a previous step.. - // If they did this already, just continue to next step. - var entity = context.entity(_areaID); - if (entity.tags.description) { - return continueTo(play); - } - - // scroll "Add field" into view - var box = context.container().select('.more-fields').node().getBoundingClientRect(); - if (box.top > 300) { - var pane = context.container().select('.entity-editor-pane .inspector-body'); - var start = pane.node().scrollTop; - var end = start + (box.top - 300); - - pane - .transition() - .duration(250) - .tween('scroll.inspector', function() { - var node = this; - var i = d3_interpolateNumber(start, end); - return function(t) { - node.scrollTop = i(t); - }; - }); - } - - timeout(function() { - reveal('.more-fields .combobox-input', - helpString('intro.areas.add_field', { - name: nameField.label(), - description: descriptionField.label() - }), - { duration: 300 } - ); - - context.container().select('.more-fields .combobox-input') - .on('click.intro', function() { - // Watch for the combobox to appear... - var watcher; - watcher = window.setInterval(function() { - if (!context.container().select('div.combobox').empty()) { - window.clearInterval(watcher); - continueTo(chooseDescriptionField); - } - }, 300); - }); - }, 300); // after "Add Field" visible - - }, 400); // after editor pane visible - - context.on('exit.intro', function() { - return continueTo(searchPresets); - }); - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.more-fields .combobox-input').on('click.intro', null); - context.on('exit.intro', null); - nextStep(); - } - } - - - function chooseDescriptionField() { - if (!_areaID || !context.hasEntity(_areaID)) { - return addArea(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { - return searchPresets(); - } - - if (!context.container().select('.form-field-description').empty()) { - return continueTo(describePlayground); - } - - // Make sure combobox is ready.. - if (context.container().select('div.combobox').empty()) { - return continueTo(clickAddField); - } - // Watch for the combobox to go away.. - var watcher; - watcher = window.setInterval(function() { - if (context.container().select('div.combobox').empty()) { - window.clearInterval(watcher); - timeout(function() { - if (context.container().select('.form-field-description').empty()) { - continueTo(retryChooseDescription); - } else { - continueTo(describePlayground); - } - }, 300); // after description field added. - } - }, 300); - - reveal('div.combobox', - helpString('intro.areas.choose_field', { field: descriptionField.label() }), - { duration: 300 } - ); - - context.on('exit.intro', function() { - return continueTo(searchPresets); - }); - - function continueTo(nextStep) { - if (watcher) window.clearInterval(watcher); - context.on('exit.intro', null); - nextStep(); - } - } - - - function describePlayground() { - if (!_areaID || !context.hasEntity(_areaID)) { - return addArea(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { - return searchPresets(); - } - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - if (context.container().select('.form-field-description').empty()) { - return continueTo(retryChooseDescription); - } - - context.on('exit.intro', function() { - continueTo(play); - }); - - reveal('.entity-editor-pane', - helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }), - { duration: 300 } - ); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function retryChooseDescription() { - if (!_areaID || !context.hasEntity(_areaID)) { - return addArea(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) { - return searchPresets(); - } - - // reset pane, in case user happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '0%'); - - reveal('.entity-editor-pane', - helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(clickAddField); } - }); - - context.on('exit.intro', function() { - return continueTo(searchPresets); - }); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function play() { - dispatch$1.call('done'); - reveal('.ideditor', - helpString('intro.areas.play', { next: _t('intro.lines.title') }), { - tooltipBox: '.intro-nav-wrap .chapter-line', - buttonText: _t('intro.ok'), - buttonCallback: function() { reveal('.ideditor'); } - } - ); - } - - - chapter.enter = function() { - addArea(); - }; - - - chapter.exit = function() { - timeouts.forEach(window.clearTimeout); - context.on('enter.intro exit.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - context.container().select('.more-fields .combobox-input').on('click.intro', null); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroLine(context, reveal) { - var dispatch$1 = dispatch('done'); - var timeouts = []; - var _tulipRoadID = null; - var flowerRoadID = 'w646'; - var tulipRoadStart = [-85.6297754121684, 41.95805253325314]; - var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204]; - var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585]; - var roadCategory = _mainPresetIndex.item('category-road_minor'); - var residentialPreset = _mainPresetIndex.item('highway/residential'); - var woodRoadID = 'w525'; - var woodRoadEndID = 'n2862'; - var woodRoadAddNode = [-85.62390110349587, 41.95397111462291]; - var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487]; - var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872]; - var washingtonStreetID = 'w522'; - var twelfthAvenueID = 'w1'; - var eleventhAvenueEndID = 'n3550'; - var twelfthAvenueEndID = 'n5'; - var _washingtonSegmentID = null; - var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc; - var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc; - var deleteLinesLoc = [-85.6219395542764, 41.95228033922477]; - var twelfthAvenue = [-85.62219310052491, 41.952505413152956]; - - - var chapter = { - title: 'intro.lines.title' - }; - - - function timeout(f, t) { - timeouts.push(window.setTimeout(f, t)); - } - - - function eventCancel() { - event.stopPropagation(); - event.preventDefault(); - } - - - function addLine() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - - var msec = transitionTime(tulipRoadStart, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(tulipRoadStart, 18.5, msec); - - timeout(function() { - var tooltip = reveal('button.add-line', - helpString('intro.lines.add_line')); - - tooltip.selectAll('.popover-inner') - .insert('svg', 'span') - .attr('class', 'tooltip-illustration') - .append('use') - .attr('xlink:href', '#iD-graphic-lines'); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'add-line') return; - continueTo(startLine); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function startLine() { - if (context.mode().id !== 'add-line') return chapter.restart(); - - _tulipRoadID = null; - - var padding = 70 * Math.pow(2, context.map().zoom() - 18); - var box = pad(tulipRoadStart, padding, context); - box.height = box.height + 100; - - var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap'; - var startLineString = helpString('intro.lines.missing_road') + '{br}' + - helpString('intro.lines.line_draw_info') + - helpString('intro.lines.' + textId); - reveal(box, startLineString); - - context.map().on('move.intro drawn.intro', function() { - padding = 70 * Math.pow(2, context.map().zoom() - 18); - box = pad(tulipRoadStart, padding, context); - box.height = box.height + 100; - reveal(box, startLineString, { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'draw-line') return chapter.restart(); - continueTo(drawLine); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function drawLine() { - if (context.mode().id !== 'draw-line') return chapter.restart(); - - _tulipRoadID = context.mode().selectedIDs()[0]; - context.map().centerEase(tulipRoadMidpoint, 500); - - timeout(function() { - var padding = 200 * Math.pow(2, context.map().zoom() - 18.5); - var box = pad(tulipRoadMidpoint, padding, context); - box.height = box.height * 2; - reveal(box, - helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }) - ); - - context.map().on('move.intro drawn.intro', function() { - padding = 200 * Math.pow(2, context.map().zoom() - 18.5); - box = pad(tulipRoadMidpoint, padding, context); - box.height = box.height * 2; - reveal(box, - helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }), - { duration: 0 } - ); - }); - }, 550); // after easing.. - - context.history().on('change.intro', function() { - if (isLineConnected()) { - continueTo(continueLine); - } - }); - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-line') { - return; - } else if (mode.id === 'select') { - continueTo(retryIntersect); - return; - } else { - return chapter.restart(); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function isLineConnected() { - var entity = _tulipRoadID && context.hasEntity(_tulipRoadID); - if (!entity) return false; - - var drawNodes = context.graph().childNodes(entity); - return drawNodes.some(function(node) { - return context.graph().parentWays(node).some(function(parent) { - return parent.id === flowerRoadID; - }); - }); - } - - - function retryIntersect() { - select(window).on('pointerdown.intro mousedown.intro', eventCancel, true); - - var box = pad(tulipRoadIntersection, 80, context); - reveal(box, - helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') }) - ); - - timeout(chapter.restart, 3000); - } - - - function continueLine() { - if (context.mode().id !== 'draw-line') return chapter.restart(); - var entity = _tulipRoadID && context.hasEntity(_tulipRoadID); - if (!entity) return chapter.restart(); - - context.map().centerEase(tulipRoadIntersection, 500); - - var continueLineText = helpString('intro.lines.continue_line') + '{br}' + - helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + - helpString('intro.lines.finish_road'); - - reveal('.surface', continueLineText); - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-line') - return; - else if (mode.id === 'select') - return continueTo(chooseCategoryRoad); - else - return chapter.restart(); - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function chooseCategoryRoad() { - if (context.mode().id !== 'select') return chapter.restart(); - - context.on('exit.intro', function() { - return chapter.restart(); - }); - - var button = context.container().select('.preset-category-road_minor .preset-list-button'); - if (button.empty()) return chapter.restart(); - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - - reveal(button.node(), - helpString('intro.lines.choose_category_road', { category: roadCategory.name() }) - ); - - button.on('click.intro', function() { - continueTo(choosePresetResidential); - }); - - }, 400); // after editor pane visible - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-list-button').on('click.intro', null); - context.on('exit.intro', null); - nextStep(); - } - } - - - function choosePresetResidential() { - if (context.mode().id !== 'select') return chapter.restart(); - - context.on('exit.intro', function() { - return chapter.restart(); - }); - - var subgrid = context.container().select('.preset-category-road_minor .subgrid'); - if (subgrid.empty()) return chapter.restart(); - - subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button') - .on('click.intro', function() { - continueTo(retryPresetResidential); - }); - - subgrid.selectAll('.preset-highway-residential .preset-list-button') - .on('click.intro', function() { - continueTo(nameRoad); - }); - - timeout(function() { - reveal(subgrid.node(), - helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }), - { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 } - ); - }, 300); - - function continueTo(nextStep) { - context.container().select('.preset-list-button').on('click.intro', null); - context.on('exit.intro', null); - nextStep(); - } - } - - - // selected wrong road type - function retryPresetResidential() { - if (context.mode().id !== 'select') return chapter.restart(); - - context.on('exit.intro', function() { - return chapter.restart(); - }); - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - var button = context.container().select('.entity-editor-pane .preset-list-button'); - - reveal(button.node(), - helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() }) - ); - - button.on('click.intro', function() { - continueTo(chooseCategoryRoad); - }); - - }, 500); - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-list-button').on('click.intro', null); - context.on('exit.intro', null); - nextStep(); - } - } - - - function nameRoad() { - context.on('exit.intro', function() { - continueTo(didNameRoad); - }); - - timeout(function() { - reveal('.entity-editor-pane', - helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }), - { tooltipClass: 'intro-lines-name_road' } - ); - }, 500); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function didNameRoad() { - context.history().checkpoint('doneAddLine'); - - timeout(function() { - reveal('.surface', helpString('intro.lines.did_name_road'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(updateLine); } - }); - }, 500); - - function continueTo(nextStep) { - nextStep(); - } - } - - - function updateLine() { - context.history().reset('doneAddLine'); - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return chapter.restart(); - } - - var msec = transitionTime(woodRoadDragMidpoint, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec); - - timeout(function() { - var padding = 250 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragMidpoint, padding, context); - var advance = function() { continueTo(addNode); }; - - reveal(box, helpString('intro.lines.update_line'), - { buttonText: _t('intro.ok'), buttonCallback: advance } - ); - - context.map().on('move.intro drawn.intro', function() { - var padding = 250 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragMidpoint, padding, context); - reveal(box, helpString('intro.lines.update_line'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance } - ); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function addNode() { - context.history().reset('doneAddLine'); - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return chapter.restart(); - } - - var padding = 40 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadAddNode, padding, context); - var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch')); - reveal(box, addNodeString); - - context.map().on('move.intro drawn.intro', function() { - var padding = 40 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadAddNode, padding, context); - reveal(box, addNodeString, { duration: 0 }); - }); - - context.history().on('change.intro', function(changed) { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - if (changed.created().length === 1) { - timeout(function() { continueTo(startDragEndpoint); }, 500); - } - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') { - continueTo(updateLine); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function startDragEndpoint() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + - helpString('intro.lines.drag_to_intersection'); - reveal(box, startDragString); - - context.map().on('move.intro drawn.intro', function() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - reveal(box, startDragString, { duration: 0 }); - - var entity = context.entity(woodRoadEndID); - if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) { - continueTo(finishDragEndpoint); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function finishDragEndpoint() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - var finishDragString = helpString('intro.lines.spot_looks_good') + - helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')); - reveal(box, finishDragString); - - context.map().on('move.intro drawn.intro', function() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - reveal(box, finishDragString, { duration: 0 }); - - var entity = context.entity(woodRoadEndID); - if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) { - continueTo(startDragEndpoint); - } - }); - - context.on('enter.intro', function() { - continueTo(startDragMidpoint); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function startDragMidpoint() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - if (context.selectedIDs().indexOf(woodRoadID) === -1) { - context.enter(modeSelect(context, [woodRoadID])); - } - - var padding = 80 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragMidpoint, padding, context); - reveal(box, helpString('intro.lines.start_drag_midpoint')); - - context.map().on('move.intro drawn.intro', function() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - var padding = 80 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragMidpoint, padding, context); - reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 }); - }); - - context.history().on('change.intro', function(changed) { - if (changed.created().length === 1) { - continueTo(continueDragMidpoint); - } - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') { - // keep Wood Road selected so midpoint triangles are drawn.. - context.enter(modeSelect(context, [woodRoadID])); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function continueDragMidpoint() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - box.height += 400; - - var advance = function() { - context.history().checkpoint('doneUpdateLine'); - continueTo(deleteLines); - }; - - reveal(box, helpString('intro.lines.continue_drag_midpoint'), - { buttonText: _t('intro.ok'), buttonCallback: advance } - ); - - context.map().on('move.intro drawn.intro', function() { - if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) { - return continueTo(updateLine); - } - var padding = 100 * Math.pow(2, context.map().zoom() - 19); - var box = pad(woodRoadDragEndpoint, padding, context); - box.height += 400; - reveal(box, helpString('intro.lines.continue_drag_midpoint'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance } - ); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function deleteLines() { - context.history().reset('doneUpdateLine'); - context.enter(modeBrowse(context)); - - if (!context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return chapter.restart(); - } - - var msec = transitionTime(deleteLinesLoc, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(deleteLinesLoc, 18, msec); - - timeout(function() { - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(deleteLinesLoc, padding, context); - box.top -= 200; - box.height += 400; - var advance = function() { continueTo(rightClickIntersection); }; - - reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }), - { buttonText: _t('intro.ok'), buttonCallback: advance } - ); - - context.map().on('move.intro drawn.intro', function() { - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(deleteLinesLoc, padding, context); - box.top -= 200; - box.height += 400; - reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance } - ); - }); - - context.history().on('change.intro', function() { - timeout(function() { - continueTo(deleteLines); - }, 500); // after any transition (e.g. if user deleted intersection) - }); - - }, msec + 100); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function rightClickIntersection() { - context.history().reset('doneUpdateLine'); - context.enter(modeBrowse(context)); - - context.map().centerZoomEase(eleventhAvenueEnd, 18, 500); - - var rightClickString = helpString('intro.lines.split_street', { - street1: _t('intro.graph.name.11th-avenue'), - street2: _t('intro.graph.name.washington-street') - }) + - helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch')); - - timeout(function() { - var padding = 60 * Math.pow(2, context.map().zoom() - 18); - var box = pad(eleventhAvenueEnd, padding, context); - reveal(box, rightClickString); - - context.map().on('move.intro drawn.intro', function() { - var padding = 60 * Math.pow(2, context.map().zoom() - 18); - var box = pad(eleventhAvenueEnd, padding, context); - reveal(box, rightClickString, - { duration: 0 } - ); - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return; - var ids = context.selectedIDs(); - if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return; - - timeout(function() { - var node = selectMenuItem(context, 'split').node(); - if (!node) return; - continueTo(splitIntersection); - }, 50); // after menu visible - }); - - context.history().on('change.intro', function() { - timeout(function() { - continueTo(deleteLines); - }, 300); // after any transition (e.g. if user deleted intersection) - }); - - }, 600); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function splitIntersection() { - if (!context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(deleteLines); - } - - var node = selectMenuItem(context, 'split').node(); - if (!node) { return continueTo(rightClickIntersection); } - - var wasChanged = false; - _washingtonSegmentID = null; - - reveal('.edit-menu', helpString('intro.lines.split_intersection', - { street: _t('intro.graph.name.washington-street') }), - { padding: 50 } - ); - - context.map().on('move.intro drawn.intro', function() { - var node = selectMenuItem(context, 'split').node(); - if (!wasChanged && !node) { return continueTo(rightClickIntersection); } - - reveal('.edit-menu', helpString('intro.lines.split_intersection', - { street: _t('intro.graph.name.washington-street') }), - { duration: 0, padding: 50 } - ); - }); - - context.history().on('change.intro', function(changed) { - wasChanged = true; - timeout(function() { - if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) { - _washingtonSegmentID = changed.created()[0].id; - continueTo(didSplit); - } else { - _washingtonSegmentID = null; - continueTo(retrySplit); - } - }, 300); // after any transition (e.g. if user deleted intersection) - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function retrySplit() { - context.enter(modeBrowse(context)); - context.map().centerZoomEase(eleventhAvenueEnd, 18, 500); - var advance = function() { continueTo(rightClickIntersection); }; - - var padding = 60 * Math.pow(2, context.map().zoom() - 18); - var box = pad(eleventhAvenueEnd, padding, context); - reveal(box, helpString('intro.lines.retry_split'), - { buttonText: _t('intro.ok'), buttonCallback: advance } - ); - - context.map().on('move.intro drawn.intro', function() { - var padding = 60 * Math.pow(2, context.map().zoom() - 18); - var box = pad(eleventhAvenueEnd, padding, context); - reveal(box, helpString('intro.lines.retry_split'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance } - ); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function didSplit() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - - var ids = context.selectedIDs(); - var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single'); - var street = _t('intro.graph.name.washington-street'); - - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(twelfthAvenue, padding, context); - box.width = box.width / 2; - reveal(box, helpString(string, { street1: street, street2: street }), - { duration: 500 } - ); - - timeout(function() { - context.map().centerZoomEase(twelfthAvenue, 18, 500); - - context.map().on('move.intro drawn.intro', function() { - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(twelfthAvenue, padding, context); - box.width = box.width / 2; - reveal(box, helpString(string, { street1: street, street2: street }), - { duration: 0 } - ); - }); - }, 600); // after initial reveal and curtain cut - - context.on('enter.intro', function() { - var ids = context.selectedIDs(); - if (ids.length === 1 && ids[0] === _washingtonSegmentID) { - continueTo(multiSelect); - } - }); - - context.history().on('change.intro', function() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function multiSelect() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - - var ids = context.selectedIDs(); - var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1; - var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1; - - if (hasWashington && hasTwelfth) { - return continueTo(multiRightClick); - } else if (!hasWashington && !hasTwelfth) { - return continueTo(didSplit); - } - - context.map().centerZoomEase(twelfthAvenue, 18, 500); - - timeout(function() { - var selected, other, padding, box; - if (hasWashington) { - selected = _t('intro.graph.name.washington-street'); - other = _t('intro.graph.name.12th-avenue'); - padding = 60 * Math.pow(2, context.map().zoom() - 18); - box = pad(twelfthAvenueEnd, padding, context); - box.width *= 3; - } else { - selected = _t('intro.graph.name.12th-avenue'); - other = _t('intro.graph.name.washington-street'); - padding = 200 * Math.pow(2, context.map().zoom() - 18); - box = pad(twelfthAvenue, padding, context); - box.width /= 2; - } - - reveal(box, - helpString('intro.lines.multi_select', - { selected: selected, other1: other }) + ' ' + - helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), - { selected: selected, other2: other }) - ); - - context.map().on('move.intro drawn.intro', function() { - if (hasWashington) { - selected = _t('intro.graph.name.washington-street'); - other = _t('intro.graph.name.12th-avenue'); - padding = 60 * Math.pow(2, context.map().zoom() - 18); - box = pad(twelfthAvenueEnd, padding, context); - box.width *= 3; - } else { - selected = _t('intro.graph.name.12th-avenue'); - other = _t('intro.graph.name.washington-street'); - padding = 200 * Math.pow(2, context.map().zoom() - 18); - box = pad(twelfthAvenue, padding, context); - box.width /= 2; - } - - reveal(box, - helpString('intro.lines.multi_select', - { selected: selected, other1: other }) + ' ' + - helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), - { selected: selected, other2: other }), - { duration: 0 } - ); - }); - - context.on('enter.intro', function() { - continueTo(multiSelect); - }); - - context.history().on('change.intro', function() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - }); - }, 600); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function multiRightClick() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(twelfthAvenue, padding, context); - - var rightClickString = helpString('intro.lines.multi_select_success') + - helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch')); - reveal(box, rightClickString); - - context.map().on('move.intro drawn.intro', function() { - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(twelfthAvenue, padding, context); - reveal(box, rightClickString, { duration: 0 }); - }); - - context.ui().editMenu().on('toggled.intro', function(open) { - if (!open) return; - - timeout(function() { - var ids = context.selectedIDs(); - if (ids.length === 2 && - ids.indexOf(twelfthAvenueID) !== -1 && - ids.indexOf(_washingtonSegmentID) !== -1) { - var node = selectMenuItem(context, 'delete').node(); - if (!node) return; - continueTo(multiDelete); - } else if (ids.length === 1 && - ids.indexOf(_washingtonSegmentID) !== -1) { - return continueTo(multiSelect); - } else { - return continueTo(didSplit); - } - }, 300); // after edit menu visible - }); - - context.history().on('change.intro', function() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.ui().editMenu().on('toggled.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function multiDelete() { - if (!_washingtonSegmentID || - !context.hasEntity(_washingtonSegmentID) || - !context.hasEntity(washingtonStreetID) || - !context.hasEntity(twelfthAvenueID) || - !context.hasEntity(eleventhAvenueEndID)) { - return continueTo(rightClickIntersection); - } - - var node = selectMenuItem(context, 'delete').node(); - if (!node) return continueTo(multiRightClick); - - reveal('.edit-menu', - helpString('intro.lines.multi_delete'), - { padding: 50 } - ); - - context.map().on('move.intro drawn.intro', function() { - reveal('.edit-menu', - helpString('intro.lines.multi_delete'), - { duration: 0, padding: 50 } - ); - }); - - context.on('exit.intro', function() { - if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) { - return continueTo(multiSelect); // left select mode but roads still exist - } - }); - - context.history().on('change.intro', function() { - if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) { - continueTo(retryDelete); // changed something but roads still exist - } else { - continueTo(play); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('exit.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function retryDelete() { - context.enter(modeBrowse(context)); - - var padding = 200 * Math.pow(2, context.map().zoom() - 18); - var box = pad(twelfthAvenue, padding, context); - reveal(box, helpString('intro.lines.retry_delete'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(multiSelect); } - }); - - function continueTo(nextStep) { - nextStep(); - } - } - - - function play() { - dispatch$1.call('done'); - reveal('.ideditor', - helpString('intro.lines.play', { next: _t('intro.buildings.title') }), { - tooltipBox: '.intro-nav-wrap .chapter-building', - buttonText: _t('intro.ok'), - buttonCallback: function() { reveal('.ideditor'); } - } - ); - } - - - chapter.enter = function() { - addLine(); - }; - - - chapter.exit = function() { - timeouts.forEach(window.clearTimeout); - select(window).on('pointerdown.intro mousedown.intro', null, true); - context.on('enter.intro exit.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-list-button').on('click.intro', null); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroBuilding(context, reveal) { - var dispatch$1 = dispatch('done'); - var house = [-85.62815, 41.95638]; - var tank = [-85.62732, 41.95347]; - var buildingCatetory = _mainPresetIndex.item('category-building'); - var housePreset = _mainPresetIndex.item('building/house'); - var tankPreset = _mainPresetIndex.item('man_made/storage_tank'); - var timeouts = []; - var _houseID = null; - var _tankID = null; - - - var chapter = { - title: 'intro.buildings.title' - }; - - - function timeout(f, t) { - timeouts.push(window.setTimeout(f, t)); - } - - - function eventCancel() { - event.stopPropagation(); - event.preventDefault(); - } - - - function revealHouse(center, text, options) { - var padding = 160 * Math.pow(2, context.map().zoom() - 20); - var box = pad(center, padding, context); - reveal(box, text, options); - } - - - function revealTank(center, text, options) { - var padding = 190 * Math.pow(2, context.map().zoom() - 19.5); - var box = pad(center, padding, context); - reveal(box, text, options); - } - - - function addHouse() { - context.enter(modeBrowse(context)); - context.history().reset('initial'); - _houseID = null; - - var msec = transitionTime(house, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(house, 19, msec); - - timeout(function() { - var tooltip = reveal('button.add-area', - helpString('intro.buildings.add_building')); - - tooltip.selectAll('.popover-inner') - .insert('svg', 'span') - .attr('class', 'tooltip-illustration') - .append('use') - .attr('xlink:href', '#iD-graphic-buildings'); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'add-area') return; - continueTo(startHouse); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function startHouse() { - if (context.mode().id !== 'add-area') { - return continueTo(addHouse); - } - - _houseID = null; - context.map().zoomEase(20, 500); - - timeout(function() { - var startString = helpString('intro.buildings.start_building') + - helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')); - revealHouse(house, startString); - - context.map().on('move.intro drawn.intro', function() { - revealHouse(house, startString, { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'draw-area') return chapter.restart(); - continueTo(continueHouse); - }); - - }, 550); // after easing - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function continueHouse() { - if (context.mode().id !== 'draw-area') { - return continueTo(addHouse); - } - - _houseID = null; - - var continueString = helpString('intro.buildings.continue_building') + '{br}' + - helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + - helpString('intro.buildings.finish_building'); - - revealHouse(house, continueString); - - context.map().on('move.intro drawn.intro', function() { - revealHouse(house, continueString, { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-area') { - return; - } else if (mode.id === 'select') { - var graph = context.graph(); - var way = context.entity(context.selectedIDs()[0]); - var nodes = graph.childNodes(way); - var points = utilArrayUniq(nodes) - .map(function(n) { return context.projection(n.loc); }); - - if (isMostlySquare(points)) { - _houseID = way.id; - return continueTo(chooseCategoryBuilding); - } else { - return continueTo(retryHouse); - } - - } else { - return chapter.restart(); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function retryHouse() { - var onClick = function() { continueTo(addHouse); }; - - revealHouse(house, helpString('intro.buildings.retry_building'), - { buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - - context.map().on('move.intro drawn.intro', function() { - revealHouse(house, helpString('intro.buildings.retry_building'), - { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick } - ); - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - nextStep(); - } - } - - - function chooseCategoryBuilding() { - if (!_houseID || !context.hasEntity(_houseID)) { - return addHouse(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { - context.enter(modeSelect(context, [_houseID])); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - - var button = context.container().select('.preset-category-building .preset-list-button'); - - reveal(button.node(), - helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() }) - ); - - button.on('click.intro', function() { - button.on('click.intro', null); - continueTo(choosePresetHouse); - }); - - }, 400); // after preset list pane visible.. - - - context.on('enter.intro', function(mode) { - if (!_houseID || !context.hasEntity(_houseID)) { - return continueTo(addHouse); - } - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) { - return continueTo(chooseCategoryBuilding); - } - }); - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-list-button').on('click.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function choosePresetHouse() { - if (!_houseID || !context.hasEntity(_houseID)) { - return addHouse(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { - context.enter(modeSelect(context, [_houseID])); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - - var button = context.container().select('.preset-building-house .preset-list-button'); - - reveal(button.node(), - helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }), - { duration: 300 } - ); - - button.on('click.intro', function() { - button.on('click.intro', null); - continueTo(closeEditorHouse); - }); - - }, 400); // after preset list pane visible.. - - context.on('enter.intro', function(mode) { - if (!_houseID || !context.hasEntity(_houseID)) { - return continueTo(addHouse); - } - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) { - return continueTo(chooseCategoryBuilding); - } - }); - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-list-button').on('click.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function closeEditorHouse() { - if (!_houseID || !context.hasEntity(_houseID)) { - return addHouse(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) { - context.enter(modeSelect(context, [_houseID])); - } - - context.history().checkpoint('hasHouse'); - - context.on('exit.intro', function() { - continueTo(rightClickHouse); - }); - - timeout(function() { - reveal('.entity-editor-pane', - helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') }) - ); - }, 500); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function rightClickHouse() { - if (!_houseID) return chapter.restart(); - - context.enter(modeBrowse(context)); - context.history().reset('hasHouse'); - var zoom = context.map().zoom(); - if (zoom < 20) { - zoom = 20; - } - context.map().centerZoomEase(house, zoom, 500); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return; - var ids = context.selectedIDs(); - if (ids.length !== 1 || ids[0] !== _houseID) return; - - timeout(function() { - var node = selectMenuItem(context, 'orthogonalize').node(); - if (!node) return; - continueTo(clickSquare); - }, 50); // after menu visible - }); - - context.map().on('move.intro drawn.intro', function() { - var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch')); - revealHouse(house, rightclickString, { duration: 0 }); - }); - - context.history().on('change.intro', function() { - continueTo(rightClickHouse); - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function clickSquare() { - if (!_houseID) return chapter.restart(); - var entity = context.hasEntity(_houseID); - if (!entity) return continueTo(rightClickHouse); - - var node = selectMenuItem(context, 'orthogonalize').node(); - if (!node) { return continueTo(rightClickHouse); } - - var wasChanged = false; - - reveal('.edit-menu', - helpString('intro.buildings.square_building'), - { padding: 50 } - ); - - context.on('enter.intro', function(mode) { - if (mode.id === 'browse') { - continueTo(rightClickHouse); - } else if (mode.id === 'move' || mode.id === 'rotate') { - continueTo(retryClickSquare); - } - }); - - context.map().on('move.intro', function() { - var node = selectMenuItem(context, 'orthogonalize').node(); - if (!wasChanged && !node) { return continueTo(rightClickHouse); } - - reveal('.edit-menu', - helpString('intro.buildings.square_building'), - { duration: 0, padding: 50 } - ); - }); - - context.history().on('change.intro', function() { - wasChanged = true; - context.history().on('change.intro', null); - - // Something changed. Wait for transition to complete and check undo annotation. - timeout(function() { - if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) { - continueTo(doneSquare); - } else { - continueTo(retryClickSquare); - } - }, 500); // after transitioned actions - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function retryClickSquare() { - context.enter(modeBrowse(context)); - - revealHouse(house, helpString('intro.buildings.retry_square'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(rightClickHouse); } - }); - - function continueTo(nextStep) { - nextStep(); - } - } - - - function doneSquare() { - context.history().checkpoint('doneSquare'); - - revealHouse(house, helpString('intro.buildings.done_square'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(addTank); } - }); - - function continueTo(nextStep) { - nextStep(); - } - } - - - function addTank() { - context.enter(modeBrowse(context)); - context.history().reset('doneSquare'); - _tankID = null; - - var msec = transitionTime(tank, context.map().center()); - if (msec) { reveal(null, null, { duration: 0 }); } - context.map().centerZoomEase(tank, 19.5, msec); - - timeout(function() { - reveal('button.add-area', - helpString('intro.buildings.add_tank') - ); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'add-area') return; - continueTo(startTank); - }); - }, msec + 100); - - function continueTo(nextStep) { - context.on('enter.intro', null); - nextStep(); - } - } - - - function startTank() { - if (context.mode().id !== 'add-area') { - return continueTo(addTank); - } - - _tankID = null; - - timeout(function() { - var startString = helpString('intro.buildings.start_tank') + - helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')); - revealTank(tank, startString); - - context.map().on('move.intro drawn.intro', function() { - revealTank(tank, startString, { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id !== 'draw-area') return chapter.restart(); - continueTo(continueTank); - }); - - }, 550); // after easing - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function continueTank() { - if (context.mode().id !== 'draw-area') { - return continueTo(addTank); - } - - _tankID = null; - - var continueString = helpString('intro.buildings.continue_tank') + '{br}' + - helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + - helpString('intro.buildings.finish_tank'); - - revealTank(tank, continueString); - - context.map().on('move.intro drawn.intro', function() { - revealTank(tank, continueString, { duration: 0 }); - }); - - context.on('enter.intro', function(mode) { - if (mode.id === 'draw-area') { - return; - } else if (mode.id === 'select') { - _tankID = context.selectedIDs()[0]; - return continueTo(searchPresetTank); - } else { - return continueTo(addTank); - } - }); - - function continueTo(nextStep) { - context.map().on('move.intro drawn.intro', null); - context.on('enter.intro', null); - nextStep(); - } - } - - - function searchPresetTank() { - if (!_tankID || !context.hasEntity(_tankID)) { - return addTank(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) { - context.enter(modeSelect(context, [_tankID])); - } - - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - timeout(function() { - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.buildings.search_tank', { preset: tankPreset.name() }) - ); - }, 400); // after preset list pane visible.. - - context.on('enter.intro', function(mode) { - if (!_tankID || !context.hasEntity(_tankID)) { - return continueTo(addTank); - } - - var ids = context.selectedIDs(); - if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) { - // keep the user's area selected.. - context.enter(modeSelect(context, [_tankID])); - - // reset pane, in case user somehow happened to change it.. - context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); - // disallow scrolling - context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); - - context.container().select('.preset-search-input') - .on('keydown.intro', null) - .on('keyup.intro', checkPresetSearch); - - reveal('.preset-search-input', - helpString('intro.buildings.search_tank', { preset: tankPreset.name() }) - ); - - context.history().on('change.intro', null); - } - }); - - function checkPresetSearch() { - var first = context.container().select('.preset-list-item:first-child'); - - if (first.classed('preset-man_made-storage_tank')) { - reveal(first.select('.preset-list-button').node(), - helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }), - { duration: 300 } - ); - - context.container().select('.preset-search-input') - .on('keydown.intro', eventCancel, true) - .on('keyup.intro', null); - - context.history().on('change.intro', function() { - continueTo(closeEditorTank); - }); - } - } - - function continueTo(nextStep) { - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.on('enter.intro', null); - context.history().on('change.intro', null); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - nextStep(); - } - } - - - function closeEditorTank() { - if (!_tankID || !context.hasEntity(_tankID)) { - return addTank(); - } - var ids = context.selectedIDs(); - if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) { - context.enter(modeSelect(context, [_tankID])); - } - - context.history().checkpoint('hasTank'); - - context.on('exit.intro', function() { - continueTo(rightClickTank); - }); - - timeout(function() { - reveal('.entity-editor-pane', - helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') }) - ); - }, 500); - - function continueTo(nextStep) { - context.on('exit.intro', null); - nextStep(); - } - } - - - function rightClickTank() { - if (!_tankID) return continueTo(addTank); - - context.enter(modeBrowse(context)); - context.history().reset('hasTank'); - context.map().centerEase(tank, 500); - - timeout(function() { - context.on('enter.intro', function(mode) { - if (mode.id !== 'select') return; - var ids = context.selectedIDs(); - if (ids.length !== 1 || ids[0] !== _tankID) return; - - timeout(function() { - var node = selectMenuItem(context, 'circularize').node(); - if (!node) return; - continueTo(clickCircle); - }, 50); // after menu visible - }); - - var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch')); - - revealTank(tank, rightclickString); - - context.map().on('move.intro drawn.intro', function() { - revealTank(tank, rightclickString, { duration: 0 }); - }); - - context.history().on('change.intro', function() { - continueTo(rightClickTank); - }); - - }, 600); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function clickCircle() { - if (!_tankID) return chapter.restart(); - var entity = context.hasEntity(_tankID); - if (!entity) return continueTo(rightClickTank); - - var node = selectMenuItem(context, 'circularize').node(); - if (!node) { return continueTo(rightClickTank); } - - var wasChanged = false; - - reveal('.edit-menu', - helpString('intro.buildings.circle_tank'), - { padding: 50 } - ); - - context.on('enter.intro', function(mode) { - if (mode.id === 'browse') { - continueTo(rightClickTank); - } else if (mode.id === 'move' || mode.id === 'rotate') { - continueTo(retryClickCircle); - } - }); - - context.map().on('move.intro', function() { - var node = selectMenuItem(context, 'circularize').node(); - if (!wasChanged && !node) { return continueTo(rightClickTank); } - - reveal('.edit-menu', - helpString('intro.buildings.circle_tank'), - { duration: 0, padding: 50 } - ); - }); - - context.history().on('change.intro', function() { - wasChanged = true; - context.history().on('change.intro', null); - - // Something changed. Wait for transition to complete and check undo annotation. - timeout(function() { - if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) { - continueTo(play); - } else { - continueTo(retryClickCircle); - } - }, 500); // after transitioned actions - }); - - function continueTo(nextStep) { - context.on('enter.intro', null); - context.map().on('move.intro', null); - context.history().on('change.intro', null); - nextStep(); - } - } - - - function retryClickCircle() { - context.enter(modeBrowse(context)); - - revealTank(tank, helpString('intro.buildings.retry_circle'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { continueTo(rightClickTank); } - }); - - function continueTo(nextStep) { - nextStep(); - } - } - - - function play() { - dispatch$1.call('done'); - reveal('.ideditor', - helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), { - tooltipBox: '.intro-nav-wrap .chapter-startEditing', - buttonText: _t('intro.ok'), - buttonCallback: function() { reveal('.ideditor'); } - } - ); - } - - - chapter.enter = function() { - addHouse(); - }; - - - chapter.exit = function() { - timeouts.forEach(window.clearTimeout); - context.on('enter.intro exit.intro', null); - context.map().on('move.intro drawn.intro', null); - context.history().on('change.intro', null); - context.container().select('.inspector-wrap').on('wheel.intro', null); - context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null); - context.container().select('.more-fields .combobox-input').on('click.intro', null); - }; - - - chapter.restart = function() { - chapter.exit(); - chapter.enter(); - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - function uiIntroStartEditing(context, reveal) { - var dispatch$1 = dispatch('done', 'startEditing'); - var modalSelection = select(null); - - - var chapter = { - title: 'intro.startediting.title' - }; - - function showHelp() { - reveal('.map-control.help-control', - helpString('intro.startediting.help'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { shortcuts(); } - } - ); - } - - function shortcuts() { - reveal('.map-control.help-control', - helpString('intro.startediting.shortcuts'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { showSave(); } - } - ); - } - - function showSave() { - context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts - reveal('.top-toolbar button.save', - helpString('intro.startediting.save'), { - buttonText: _t('intro.ok'), - buttonCallback: function() { showStart(); } - } - ); - } - - function showStart() { - context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts - - modalSelection = uiModal(context.container()); - - modalSelection.select('.modal') - .attr('class', 'modal-splash modal'); - - modalSelection.selectAll('.close').remove(); - - var startbutton = modalSelection.select('.content') - .attr('class', 'fillL') - .append('button') - .attr('class', 'modal-section huge-modal-button') - .on('click', function() { - modalSelection.remove(); - }); - - startbutton - .append('svg') - .attr('class', 'illustration') - .append('use') - .attr('xlink:href', '#iD-logo-walkthrough'); - - startbutton - .append('h2') - .text(_t('intro.startediting.start')); - - dispatch$1.call('startEditing'); - } - - - chapter.enter = function() { - showHelp(); - }; - - - chapter.exit = function() { - modalSelection.remove(); - context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts - }; - - - return utilRebind(chapter, dispatch$1, 'on'); - } - - const chapterUi = { - welcome: uiIntroWelcome, - navigation: uiIntroNavigation, - point: uiIntroPoint, - area: uiIntroArea, - line: uiIntroLine, - building: uiIntroBuilding, - startEditing: uiIntroStartEditing - }; - - const chapterFlow = [ - 'welcome', - 'navigation', - 'point', - 'area', - 'line', - 'building', - 'startEditing' - ]; - - - function uiIntro(context) { - const INTRO_IMAGERY = 'EsriWorldImageryClarity'; - let _introGraph = {}; - let _currChapter; - - - function intro(selection) { - _mainFileFetcher.get('intro_graph') - .then(dataIntroGraph => { - // create entities for intro graph and localize names - for (let id in dataIntroGraph) { - if (!_introGraph[id]) { - _introGraph[id] = osmEntity(localize(dataIntroGraph[id])); - } - } - selection.call(startIntro); - }) - .catch(function() { /* ignore */ }); - } - - - function startIntro(selection) { - context.enter(modeBrowse(context)); - - // Save current map state - let osm = context.connection(); - let history = context.history().toJSON(); - let hash = window.location.hash; - let center = context.map().center(); - let zoom = context.map().zoom(); - let background = context.background().baseLayerSource(); - let overlays = context.background().overlayLayerSources(); - let opacity = context.container().selectAll('.main-map .layer-background').style('opacity'); - let caches = osm && osm.caches(); - let baseEntities = context.history().graph().base().entities; - - // Show sidebar and disable the sidebar resizing button - // (this needs to be before `context.inIntro(true)`) - context.ui().sidebar.expand(); - context.container().selectAll('button.sidebar-toggle').classed('disabled', true); - - // Block saving - context.inIntro(true); - - // Load semi-real data used in intro - if (osm) { osm.toggle(false).reset(); } - context.history().reset(); - context.history().merge(Object.values(coreGraph().load(_introGraph).entities)); - context.history().checkpoint('initial'); - - // Setup imagery - let imagery = context.background().findSource(INTRO_IMAGERY); - if (imagery) { - context.background().baseLayerSource(imagery); - } else { - context.background().bing(); - } - overlays.forEach(d => context.background().toggleOverlayLayer(d)); - - // Setup data layers (only OSM) - let layers = context.layers(); - layers.all().forEach(item => { - // if the layer has the function `enabled` - if (typeof item.layer.enabled === 'function') { - item.layer.enabled(item.id === 'osm'); - } - }); - - - context.container().selectAll('.main-map .layer-background').style('opacity', 1); - - let curtain = uiCurtain(context.container().node()); - selection.call(curtain); - - // Store that the user started the walkthrough.. - corePreferences('walkthrough_started', 'yes'); - - // Restore previous walkthrough progress.. - let storedProgress = corePreferences('walkthrough_progress') || ''; - let progress = storedProgress.split(';').filter(Boolean); - - let chapters = chapterFlow.map((chapter, i) => { - let s = chapterUi[chapter](context, curtain.reveal) - .on('done', () => { - - buttons - .filter(d => d.title === s.title) - .classed('finished', true); - - if (i < chapterFlow.length - 1) { - const next = chapterFlow[i + 1]; - context.container().select(`button.chapter-${next}`) - .classed('next', true); - } - - // Store walkthrough progress.. - progress.push(chapter); - corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); - }); - return s; - }); - - chapters[chapters.length - 1].on('startEditing', () => { - // Store walkthrough progress.. - progress.push('startEditing'); - corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); - - // Store if walkthrough is completed.. - let incomplete = utilArrayDifference(chapterFlow, progress); - if (!incomplete.length) { - corePreferences('walkthrough_completed', 'yes'); - } - - curtain.remove(); - navwrap.remove(); - context.container().selectAll('.main-map .layer-background').style('opacity', opacity); - context.container().selectAll('button.sidebar-toggle').classed('disabled', false); - if (osm) { osm.toggle(true).reset().caches(caches); } - context.history().reset().merge(Object.values(baseEntities)); - context.background().baseLayerSource(background); - overlays.forEach(d => context.background().toggleOverlayLayer(d)); - if (history) { context.history().fromJSON(history, false); } - context.map().centerZoom(center, zoom); - window.location.replace(hash); - context.inIntro(false); - }); - - let navwrap = selection - .append('div') - .attr('class', 'intro-nav-wrap fillD'); - - navwrap - .append('svg') - .attr('class', 'intro-nav-wrap-logo') - .append('use') - .attr('xlink:href', '#iD-logo-walkthrough'); - - let buttonwrap = navwrap - .append('div') - .attr('class', 'joined') - .selectAll('button.chapter'); - - let buttons = buttonwrap - .data(chapters) - .enter() - .append('button') - .attr('class', (d, i) => `chapter chapter-${chapterFlow[i]}`) - .on('click', enterChapter); - - buttons - .append('span') - .text(d => _t(d.title)); - - buttons - .append('span') - .attr('class', 'status') - .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline')); - - enterChapter(chapters[0]); - - - function enterChapter(newChapter) { - if (_currChapter) { _currChapter.exit(); } - context.enter(modeBrowse(context)); - - _currChapter = newChapter; - _currChapter.enter(); - - buttons - .classed('next', false) - .classed('active', d => d.title === _currChapter.title); - } - } - - - return intro; - } - - function uiIssuesInfo(context) { - - var warningsItem = { - id: 'warnings', - count: 0, - iconID: 'iD-icon-alert', - descriptionID: 'issues.warnings_and_errors' - }; - - var resolvedItem = { - id: 'resolved', - count: 0, - iconID: 'iD-icon-apply', - descriptionID: 'issues.user_resolved_issues' - }; - - function update(selection) { - - var shownItems = []; - - var liveIssues = context.validator().getIssues({ - what: corePreferences('validate-what') || 'edited', - where: corePreferences('validate-where') || 'all' - }); - if (liveIssues.length) { - warningsItem.count = liveIssues.length; - shownItems.push(warningsItem); - } - - if (corePreferences('validate-what') === 'all') { - var resolvedIssues = context.validator().getResolvedIssues(); - if (resolvedIssues.length) { - resolvedItem.count = resolvedIssues.length; - shownItems.push(resolvedItem); - } - } - - var chips = selection.selectAll('.chip') - .data(shownItems, function(d) { - return d.id; - }); - - chips.exit().remove(); - - var enter = chips.enter() - .append('a') - .attr('class', function(d) { - return 'chip ' + d.id + '-count'; - }) - .attr('href', '#') - .attr('tabindex', -1) - .each(function(d) { - - var chipSelection = select(this); - - var tooltipBehavior = uiTooltip() - .placement('top') - .title(_t(d.descriptionID)); - - chipSelection - .call(tooltipBehavior) - .on('click', function() { - event.preventDefault(); - - tooltipBehavior.hide(select(this)); - // open the Issues pane - context.ui().togglePanes(context.container().select('.map-panes .issues-pane')); - }); - - chipSelection.call(svgIcon('#' + d.iconID)); - - }); - - enter.append('span') - .attr('class', 'count'); - - enter.merge(chips) - .selectAll('span.count') - .text(function(d) { - return d.count.toString(); - }); - } - - - return function(selection) { - update(selection); - - context.validator().on('validated.infobox', function() { - update(selection); - }); - }; - } - - // import { utilGetDimensions } from '../util/dimensions'; - - - function uiMapInMap(context) { - - function mapInMap(selection) { - var backgroundLayer = rendererTileLayer(context); - var overlayLayers = {}; - var projection = geoRawMercator(); - var dataLayer = svgData(projection, context).showLabels(false); - var debugLayer = svgDebug(projection, context); - var zoom = d3_zoom() - .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]) - .on('start', zoomStarted) - .on('zoom', zoomed) - .on('end', zoomEnded); - - var wrap = select(null); - var tiles = select(null); - var viewport = select(null); - - var _isTransformed = false; - var _isHidden = true; - var _skipEvents = false; - var _gesture = null; - var _zDiff = 6; // by default, minimap renders at (main zoom - 6) - var _dMini; // dimensions of minimap - var _cMini; // center pixel of minimap - var _tStart; // transform at start of gesture - var _tCurr; // transform at most recent event - var _timeoutID; - - - function zoomStarted() { - if (_skipEvents) return; - _tStart = _tCurr = projection.transform(); - _gesture = null; - } - - - function zoomed() { - if (_skipEvents) return; - - var x = event.transform.x; - var y = event.transform.y; - var k = event.transform.k; - var isZooming = (k !== _tStart.k); - var isPanning = (x !== _tStart.x || y !== _tStart.y); - - if (!isZooming && !isPanning) { - return; // no change - } - - // lock in either zooming or panning, don't allow both in minimap. - if (!_gesture) { - _gesture = isZooming ? 'zoom' : 'pan'; - } - - var tMini = projection.transform(); - var tX, tY, scale; - - if (_gesture === 'zoom') { - scale = k / tMini.k; - tX = (_cMini[0] / scale - _cMini[0]) * scale; - tY = (_cMini[1] / scale - _cMini[1]) * scale; - } else { - k = tMini.k; - scale = 1; - tX = x - tMini.x; - tY = y - tMini.y; - } - - utilSetTransform(tiles, tX, tY, scale); - utilSetTransform(viewport, 0, 0, scale); - _isTransformed = true; - _tCurr = identity$2.translate(x, y).scale(k); - - var zMain = geoScaleToZoom(context.projection.scale()); - var zMini = geoScaleToZoom(k); - - _zDiff = zMain - zMini; - - queueRedraw(); - } - - - function zoomEnded() { - if (_skipEvents) return; - if (_gesture !== 'pan') return; - - updateProjection(); - _gesture = null; - context.map().center(projection.invert(_cMini)); // recenter main map.. - } - - - function updateProjection() { - var loc = context.map().center(); - var tMain = context.projection.transform(); - var zMain = geoScaleToZoom(tMain.k); - var zMini = Math.max(zMain - _zDiff, 0.5); - var kMini = geoZoomToScale(zMini); - - projection - .translate([tMain.x, tMain.y]) - .scale(kMini); - - var point = projection(loc); - var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0]; - var xMini = _cMini[0] - point[0] + tMain.x + mouse[0]; - var yMini = _cMini[1] - point[1] + tMain.y + mouse[1]; - - projection - .translate([xMini, yMini]) - .clipExtent([[0, 0], _dMini]); - - _tCurr = projection.transform(); - - if (_isTransformed) { - utilSetTransform(tiles, 0, 0); - utilSetTransform(viewport, 0, 0); - _isTransformed = false; - } - - zoom - .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]); - - _skipEvents = true; - wrap.call(zoom.transform, _tCurr); - _skipEvents = false; - } - - - function redraw() { - clearTimeout(_timeoutID); - if (_isHidden) return; - - updateProjection(); - var zMini = geoScaleToZoom(projection.scale()); - - // setup tile container - tiles = wrap - .selectAll('.map-in-map-tiles') - .data([0]); - - tiles = tiles.enter() - .append('div') - .attr('class', 'map-in-map-tiles') - .merge(tiles); - - // redraw background - backgroundLayer - .source(context.background().baseLayerSource()) - .projection(projection) - .dimensions(_dMini); - - var background = tiles - .selectAll('.map-in-map-background') - .data([0]); - - background.enter() - .append('div') - .attr('class', 'map-in-map-background') - .merge(background) - .call(backgroundLayer); - - - // redraw overlay - var overlaySources = context.background().overlayLayerSources(); - var activeOverlayLayers = []; - for (var i = 0; i < overlaySources.length; i++) { - if (overlaySources[i].validZoom(zMini)) { - if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context); - activeOverlayLayers.push(overlayLayers[i] - .source(overlaySources[i]) - .projection(projection) - .dimensions(_dMini)); - } - } - - var overlay = tiles - .selectAll('.map-in-map-overlay') - .data([0]); - - overlay = overlay.enter() - .append('div') - .attr('class', 'map-in-map-overlay') - .merge(overlay); - - - var overlays = overlay - .selectAll('div') - .data(activeOverlayLayers, function(d) { return d.source().name(); }); - - overlays.exit() - .remove(); - - overlays = overlays.enter() - .append('div') - .merge(overlays) - .each(function(layer) { select(this).call(layer); }); - - - var dataLayers = tiles - .selectAll('.map-in-map-data') - .data([0]); - - dataLayers.exit() - .remove(); - - dataLayers = dataLayers.enter() - .append('svg') - .attr('class', 'map-in-map-data') - .merge(dataLayers) - .call(dataLayer) - .call(debugLayer); - - - // redraw viewport bounding box - if (_gesture !== 'pan') { - var getPath = d3_geoPath(projection); - var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] }; - - viewport = wrap.selectAll('.map-in-map-viewport') - .data([0]); - - viewport = viewport.enter() - .append('svg') - .attr('class', 'map-in-map-viewport') - .merge(viewport); - - - var path = viewport.selectAll('.map-in-map-bbox') - .data([bbox]); - - path.enter() - .append('path') - .attr('class', 'map-in-map-bbox') - .merge(path) - .attr('d', getPath) - .classed('thick', function(d) { return getPath.area(d) < 30; }); - } - } - - - function queueRedraw() { - clearTimeout(_timeoutID); - _timeoutID = setTimeout(function() { redraw(); }, 750); - } - - - function toggle() { - if (event) event.preventDefault(); - - _isHidden = !_isHidden; - - context.container().select('.minimap-toggle-item') - .classed('active', !_isHidden) - .select('input') - .property('checked', !_isHidden); - - if (_isHidden) { - wrap - .style('display', 'block') - .style('opacity', '1') - .transition() - .duration(200) - .style('opacity', '0') - .on('end', function() { - selection.selectAll('.map-in-map') - .style('display', 'none'); - }); - } else { - wrap - .style('display', 'block') - .style('opacity', '0') - .transition() - .duration(200) - .style('opacity', '1') - .on('end', function() { - redraw(); - }); - } - } - - - uiMapInMap.toggle = toggle; - - wrap = selection.selectAll('.map-in-map') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'map-in-map') - .style('display', (_isHidden ? 'none' : 'block')) - .call(zoom) - .on('dblclick.zoom', null) - .merge(wrap); - - // reflow warning: Hardcode dimensions - currently can't resize it anyway.. - _dMini = [200,150]; //utilGetDimensions(wrap); - _cMini = geoVecScale(_dMini, 0.5); - - context.map() - .on('drawn.map-in-map', function(drawn) { - if (drawn.full === true) { - redraw(); - } - }); - - redraw(); - - context.keybinding() - .on(_t('background.minimap.key'), toggle); - } - - return mapInMap; - } - - function uiNotice(context) { - - return function(selection) { - var div = selection - .append('div') - .attr('class', 'notice'); - - var button = div - .append('button') - .attr('class', 'zoom-to notice fillD') - .on('click', function() { - context.map().zoomEase(context.minEditableZoom()); - }) - .on('wheel', function() { // let wheel events pass through #4482 - var e2 = new WheelEvent(event.type, event); - context.surface().node().dispatchEvent(e2); - }); - - button - .call(svgIcon('#iD-icon-plus', 'pre-text')) - .append('span') - .attr('class', 'label') - .text(_t('zoom_in_edit')); - - - function disableTooHigh() { - var canEdit = context.map().zoom() >= context.minEditableZoom(); - div.style('display', canEdit ? 'none' : 'block'); - } - - context.map() - .on('move.notice', debounce(disableTooHigh, 500)); - - disableTooHigh(); - }; - } - - function uiPhotoviewer(context) { - - var dispatch$1 = dispatch('resize'); - - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - function photoviewer(selection) { - selection - .append('button') - .attr('class', 'thumb-hide') - .on('click', function () { - if (services.streetside) { services.streetside.hideViewer(context); } - if (services.mapillary) { services.mapillary.hideViewer(context); } - if (services.openstreetcam) { services.openstreetcam.hideViewer(context); } - }) - .append('div') - .call(svgIcon('#iD-icon-close')); - - function preventDefault() { - event.preventDefault(); - } - - selection - .append('button') - .attr('class', 'resize-handle-xy') - .on('touchstart touchdown touchend', preventDefault) - .on( - _pointerPrefix + 'down', - buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true }) - ); - - selection - .append('button') - .attr('class', 'resize-handle-x') - .on('touchstart touchdown touchend', preventDefault) - .on( - _pointerPrefix + 'down', - buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true }) - ); - - selection - .append('button') - .attr('class', 'resize-handle-y') - .on('touchstart touchdown touchend', preventDefault) - .on( - _pointerPrefix + 'down', - buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true }) - ); - - services.streetside.loadViewer(context); - services.mapillary.loadViewer(context); - services.openstreetcam.loadViewer(context); - - function buildResizeListener(target, eventName, dispatch, options) { - - var resizeOnX = !!options.resizeOnX; - var resizeOnY = !!options.resizeOnY; - var minHeight = options.minHeight || 240; - var minWidth = options.minWidth || 320; - var pointerId; - var startX; - var startY; - var startWidth; - var startHeight; - - function startResize() { - if (pointerId !== (event.pointerId || 'mouse')) return; - - event.preventDefault(); - event.stopPropagation(); - - var mapSize = context.map().dimensions(); - - if (resizeOnX) { - var maxWidth = mapSize[0]; - var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth); - target.style('width', newWidth + 'px'); - } - - if (resizeOnY) { - var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map - var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight); - target.style('height', newHeight + 'px'); - } - - dispatch.call(eventName, target, utilGetDimensions(target, true)); - } - - function clamp(num, min, max) { - return Math.max(min, Math.min(num, max)); - } - - function stopResize() { - if (pointerId !== (event.pointerId || 'mouse')) return; - - event.preventDefault(); - event.stopPropagation(); - - // remove all the listeners we added - select(window) - .on('.' + eventName, null); - } - - return function initResize() { - event.preventDefault(); - event.stopPropagation(); - - pointerId = event.pointerId || 'mouse'; - - startX = event.clientX; - startY = event.clientY; - var targetRect = target.node().getBoundingClientRect(); - startWidth = targetRect.width; - startHeight = targetRect.height; - - select(window) - .on(_pointerPrefix + 'move.' + eventName, startResize, false) - .on(_pointerPrefix + 'up.' + eventName, stopResize, false); - - if (_pointerPrefix === 'pointer') { - select(window) - .on('pointercancel.' + eventName, stopResize, false); - } - }; - } - } - - photoviewer.onMapResize = function() { - var photoviewer = context.container().select('.photoviewer'); - var content = context.container().select('.main-content'); - var mapDimensions = utilGetDimensions(content, true); - // shrink photo viewer if it is too big - // (-90 preserves space at top and bottom of map used by menus) - var photoDimensions = utilGetDimensions(photoviewer, true); - if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) { - var setPhotoDimensions = [ - Math.min(photoDimensions[0], mapDimensions[0]), - Math.min(photoDimensions[1], mapDimensions[1] - 90), - ]; - - photoviewer - .style('width', setPhotoDimensions[0] + 'px') - .style('height', setPhotoDimensions[1] + 'px'); - - dispatch$1.call('resize', photoviewer, setPhotoDimensions); - } - }; - - return utilRebind(photoviewer, dispatch$1, 'on'); - } - - function uiRestore(context) { - return function(selection) { - if (!context.history().hasRestorableChanges()) return; - - let modalSelection = uiModal(selection, true); - - modalSelection.select('.modal') - .attr('class', 'modal fillL'); - - let introModal = modalSelection.select('.content'); - - introModal - .append('div') - .attr('class', 'modal-section') - .append('h3') - .text(_t('restore.heading')); - - introModal - .append('div') - .attr('class','modal-section') - .append('p') - .text(_t('restore.description')); - - let buttonWrap = introModal - .append('div') - .attr('class', 'modal-actions'); - - let restore = buttonWrap - .append('button') - .attr('class', 'restore') - .on('click', () => { - context.history().restore(); - modalSelection.remove(); - }); - - restore - .append('svg') - .attr('class', 'logo logo-restore') - .append('use') - .attr('xlink:href', '#iD-logo-restore'); - - restore - .append('div') - .text(_t('restore.restore')); - - let reset = buttonWrap - .append('button') - .attr('class', 'reset') - .on('click', () => { - context.history().clearSaved(); - modalSelection.remove(); - }); - - reset - .append('svg') - .attr('class', 'logo logo-reset') - .append('use') - .attr('xlink:href', '#iD-logo-reset'); - - reset - .append('div') - .text(_t('restore.reset')); - - restore.node().focus(); - }; - } - - function uiScale(context) { - var projection = context.projection, - isImperial = !_mainLocalizer.usesMetric(), - maxLength = 180, - tickHeight = 8; - - - function scaleDefs(loc1, loc2) { - var lat = (loc2[1] + loc1[1]) / 2, - conversion = (isImperial ? 3.28084 : 1), - dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion, - scale = { dist: 0, px: 0, text: '' }, - buckets, i, val, dLon; - - if (isImperial) { - buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1]; - } else { - buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1]; - } - - // determine a user-friendly endpoint for the scale - for (i = 0; i < buckets.length; i++) { - val = buckets[i]; - if (dist >= val) { - scale.dist = Math.floor(dist / val) * val; - break; - } else { - scale.dist = +dist.toFixed(2); - } - } - - dLon = geoMetersToLon(scale.dist / conversion, lat); - scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]); - - scale.text = displayLength(scale.dist / conversion, isImperial); - - return scale; - } - - - function update(selection) { - // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn) - var dims = context.map().dimensions(), - loc1 = projection.invert([0, dims[1]]), - loc2 = projection.invert([maxLength, dims[1]]), - scale = scaleDefs(loc1, loc2); - - selection.select('.scale-path') - .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight); - - selection.select('.scale-textgroup') - .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')'); - - selection.select('.scale-text') - .text(scale.text); - } - - - return function(selection) { - function switchUnits() { - isImperial = !isImperial; - selection.call(update); - } - - var scalegroup = selection.append('svg') - .attr('class', 'scale') - .on('click', switchUnits) - .append('g') - .attr('transform', 'translate(10,11)'); - - scalegroup - .append('path') - .attr('class', 'scale-path'); - - scalegroup - .append('g') - .attr('class', 'scale-textgroup') - .append('text') - .attr('class', 'scale-text'); - - selection.call(update); - - context.map().on('move.scale', function() { - update(selection); - }); - }; - } - - function uiShortcuts(context) { - var detected = utilDetect(); - var _activeTab = 0; - var _modalSelection; - var _selection = select(null); - - - context.keybinding() - .on([_t('shortcuts.toggle.key'), '?'], function () { - if (context.container().selectAll('.modal-shortcuts').size()) { // already showing - if (_modalSelection) { - _modalSelection.close(); - _modalSelection = null; - } - } else { - _modalSelection = uiModal(_selection); - _modalSelection.call(shortcutsModal); - } - }); - - - function shortcutsModal(_modalSelection) { - _modalSelection.select('.modal') - .classed('modal-shortcuts', true); - - var content = _modalSelection.select('.content'); - - content - .append('div') - .attr('class', 'modal-section') - .append('h3') - .text(_t('shortcuts.title')); - - _mainFileFetcher.get('shortcuts') - .then(function(data) { content.call(render, data); }) - .catch(function() { /* ignore */ }); - } - - - function render(selection, dataShortcuts) { - var wrapper = selection - .selectAll('.wrapper') - .data([0]); - - var wrapperEnter = wrapper - .enter() - .append('div') - .attr('class', 'wrapper modal-section'); - - var tabsBar = wrapperEnter - .append('div') - .attr('class', 'tabs-bar'); - - var shortcutsList = wrapperEnter - .append('div') - .attr('class', 'shortcuts-list'); - - wrapper = wrapper.merge(wrapperEnter); - - var tabs = tabsBar - .selectAll('.tab') - .data(dataShortcuts); - - var tabsEnter = tabs - .enter() - .append('div') - .attr('class', 'tab') - .on('click', function (d, i) { - _activeTab = i; - render(selection, dataShortcuts); - }); - - tabsEnter - .append('span') - .text(function (d) { return _t(d.text); }); - - tabs = tabs - .merge(tabsEnter); - - // Update - wrapper.selectAll('.tab') - .classed('active', function (d, i) { - return i === _activeTab; - }); - - - var shortcuts = shortcutsList - .selectAll('.shortcut-tab') - .data(dataShortcuts); - - var shortcutsEnter = shortcuts - .enter() - .append('div') - .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; }); - - var columnsEnter = shortcutsEnter - .selectAll('.shortcut-column') - .data(function (d) { return d.columns; }) - .enter() - .append('table') - .attr('class', 'shortcut-column'); - - var rowsEnter = columnsEnter - .selectAll('.shortcut-row') - .data(function (d) { return d.rows; }) - .enter() - .append('tr') - .attr('class', 'shortcut-row'); - - - var sectionRows = rowsEnter - .filter(function (d) { return !d.shortcuts; }); - - sectionRows - .append('td'); - - sectionRows - .append('td') - .attr('class', 'shortcut-section') - .append('h3') - .text(function (d) { return _t(d.text); }); - - - var shortcutRows = rowsEnter - .filter(function (d) { return d.shortcuts; }); - - var shortcutKeys = shortcutRows - .append('td') - .attr('class', 'shortcut-keys'); - - var modifierKeys = shortcutKeys - .filter(function (d) { return d.modifiers; }); - - modifierKeys - .selectAll('kbd.modifier') - .data(function (d) { - if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') { - return ['⌘']; - } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { - return []; - } else { - return d.modifiers; - } - }) - .enter() - .each(function () { - var selection = select(this); - - selection - .append('kbd') - .attr('class', 'modifier') - .text(function (d) { return uiCmd.display(d); }); - - selection - .append('span') - .text('+'); - }); - - - shortcutKeys - .selectAll('kbd.shortcut') - .data(function (d) { - var arr = d.shortcuts; - if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') { - arr = ['Y']; - } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { - arr = ['F11']; - } - - // replace translations - arr = arr.map(function(s) { - return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s); - }); - - return utilArrayUniq(arr).map(function(s) { - return { - shortcut: s, - separator: d.separator, - suffix: d.suffix - }; - }); - }) - .enter() - .each(function (d, i, nodes) { - var selection = select(this); - var click = d.shortcut.toLowerCase().match(/(.*).click/); - - if (click && click[1]) { // replace "left_click", "right_click" with mouse icon - selection - .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation')); - } else if (d.shortcut.toLowerCase() === 'long-press') { - selection - .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation')); - } else if (d.shortcut.toLowerCase() === 'tap') { - selection - .call(svgIcon('#iD-walkthrough-tap', 'tap operation')); - } else { - selection - .append('kbd') - .attr('class', 'shortcut') - .text(function (d) { return d.shortcut; }); - } - - if (i < nodes.length - 1) { - selection - .append('span') - .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0'); - } else if (i === nodes.length - 1 && d.suffix) { - selection - .append('span') - .text(d.suffix); - } - }); - - - shortcutKeys - .filter(function(d) { return d.gesture; }) - .each(function () { - var selection = select(this); - - selection - .append('span') - .text('+'); - - selection - .append('span') - .attr('class', 'gesture') - .text(function (d) { return _t(d.gesture); }); - }); - - - shortcutRows - .append('td') - .attr('class', 'shortcut-desc') - .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; }); - - - shortcuts = shortcuts - .merge(shortcutsEnter); - - // Update - wrapper.selectAll('.shortcut-tab') - .style('display', function (d, i) { - return i === _activeTab ? 'flex' : 'none'; - }); - } - - - return function(selection, show) { - _selection = selection; - if (show) { - _modalSelection = uiModal(selection); - _modalSelection.call(shortcutsModal); - } - }; - } - - var pair_1 = pair; - - - function search(input, dims) { - if (!dims) dims = 'NSEW'; - if (typeof input !== 'string') return null; - - input = input.toUpperCase(); - var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/; - - var m = input.match(regex); - if (!m) return null; // no match - - var matched = m[0]; - - // extract dimension.. m[1] = leading, m[5] = trailing - var dim; - if (m[1] && m[5]) { // if matched both.. - dim = m[1]; // keep leading - matched = matched.slice(0, -1); // remove trailing dimension from match - } else { - dim = m[1] || m[5]; - } - - // if unrecognized dimension - if (dim && dims.indexOf(dim) === -1) return null; - - // extract DMS - var deg = m[2] ? parseFloat(m[2]) : 0; - var min = m[3] ? parseFloat(m[3]) / 60 : 0; - var sec = m[4] ? parseFloat(m[4]) / 3600 : 0; - var sign = (deg < 0) ? -1 : 1; - if (dim === 'S' || dim === 'W') sign *= -1; - - return { - val: (Math.abs(deg) + min + sec) * sign, - dim: dim, - matched: matched, - remain: input.slice(matched.length) - }; - } - - - function pair(input, dims) { - input = input.trim(); - var one = search(input, dims); - if (!one) return null; - - input = one.remain.trim(); - var two = search(input, dims); - if (!two || two.remain) return null; - - if (one.dim) { - return swapdim(one.val, two.val, one.dim); - } else { - return [one.val, two.val]; - } - } - - - function swapdim(a, b, dim) { - if (dim === 'N' || dim === 'S') return [a, b]; - if (dim === 'W' || dim === 'E') return [b, a]; - } - - function uiFeatureList(context) { - var _geocodeResults; - - - function featureList(selection) { - var header = selection - .append('div') - .attr('class', 'header fillL cf'); - - header - .append('h3') - .text(_t('inspector.feature_list')); - - var searchWrap = selection - .append('div') - .attr('class', 'search-header'); - - var search = searchWrap - .append('input') - .attr('placeholder', _t('inspector.search')) - .attr('type', 'search') - .call(utilNoAuto) - .on('keypress', keypress) - .on('keydown', keydown) - .on('input', inputevent); - - searchWrap - .call(svgIcon('#iD-icon-search', 'pre-text')); - - var listWrap = selection - .append('div') - .attr('class', 'inspector-body'); - - var list = listWrap - .append('div') - .attr('class', 'feature-list cf'); - - context - .on('exit.feature-list', clearSearch); - context.map() - .on('drawn.feature-list', mapDrawn); - - context.keybinding() - .on(uiCmd('⌘F'), focusSearch); - - - function focusSearch() { - var mode = context.mode() && context.mode().id; - if (mode !== 'browse') return; - - event.preventDefault(); - search.node().focus(); - } - - - function keydown() { - if (event.keyCode === 27) { // escape - search.node().blur(); - } - } - - - function keypress() { - var q = search.property('value'), - items = list.selectAll('.feature-list-item'); - if (event.keyCode === 13 && q.length && items.size()) { // return - click(items.datum()); - } - } - - - function inputevent() { - _geocodeResults = undefined; - drawList(); - } - - - function clearSearch() { - search.property('value', ''); - drawList(); - } - - - function mapDrawn(e) { - if (e.full) { - drawList(); - } - } - - - function features() { - var result = []; - var graph = context.graph(); - var visibleCenter = context.map().extent().center(); - var q = search.property('value').toLowerCase(); - - if (!q) return result; - - var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i); - - if (idMatch) { - var elemType = idMatch[1].charAt(0); - var elemId = idMatch[2]; - result.push({ - id: elemType + elemId, - geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation', - type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'), - name: elemId - }); - } - - var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/); - - if (locationMatch) { - var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])]; - result.push({ - id: -1, - geometry: 'point', - type: _t('inspector.location'), - name: dmsCoordinatePair([loc[1], loc[0]]), - location: loc - }); - } - - var allEntities = graph.entities; - var localResults = []; - for (var id in allEntities) { - var entity = allEntities[id]; - if (!entity) continue; - - var name = utilDisplayName(entity) || ''; - if (name.toLowerCase().indexOf(q) < 0) continue; - - var matched = _mainPresetIndex.match(entity, graph); - var type = (matched && matched.name()) || utilDisplayType(entity.id); - var extent = entity.extent(graph); - var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0; - - localResults.push({ - id: entity.id, - entity: entity, - geometry: entity.geometry(graph), - type: type, - name: name, - distance: distance - }); - - if (localResults.length > 100) break; - } - localResults = localResults.sort(function byDistance(a, b) { - return a.distance - b.distance; - }); - result = result.concat(localResults); - - (_geocodeResults || []).forEach(function(d) { - if (d.osm_type && d.osm_id) { // some results may be missing these - #1890 - - // Make a temporary osmEntity so we can preset match - // and better localize the search result - #4725 - var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id); - var tags = {}; - tags[d.class] = d.type; - - var attrs = { id: id, type: d.osm_type, tags: tags }; - if (d.osm_type === 'way') { // for ways, add some fake closed nodes - attrs.nodes = ['a','a']; // so that geometry area is possible - } - - var tempEntity = osmEntity(attrs); - var tempGraph = coreGraph([tempEntity]); - var matched = _mainPresetIndex.match(tempEntity, tempGraph); - var type = (matched && matched.name()) || utilDisplayType(id); - - result.push({ - id: tempEntity.id, - geometry: tempEntity.geometry(tempGraph), - type: type, - name: d.display_name, - extent: new geoExtent( - [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], - [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])]) - }); - } - }); - - if (q.match(/^[0-9]+$/)) { - // if query is just a number, possibly an OSM ID without a prefix - result.push({ - id: 'n' + q, - geometry: 'point', - type: _t('inspector.node'), - name: q - }); - result.push({ - id: 'w' + q, - geometry: 'line', - type: _t('inspector.way'), - name: q - }); - result.push({ - id: 'r' + q, - geometry: 'relation', - type: _t('inspector.relation'), - name: q - }); - } - - return result; - } - - - function drawList() { - var value = search.property('value'); - var results = features(); - - list.classed('filtered', value.length); - - var resultsIndicator = list.selectAll('.no-results-item') - .data([0]) - .enter() - .append('button') - .property('disabled', true) - .attr('class', 'no-results-item') - .call(svgIcon('#iD-icon-alert', 'pre-text')); - - resultsIndicator.append('span') - .attr('class', 'entity-name'); - - list.selectAll('.no-results-item .entity-name') - .text(_t('geocoder.no_results_worldwide')); - - if (services.geocoder) { - list.selectAll('.geocode-item') - .data([0]) - .enter() - .append('button') - .attr('class', 'geocode-item') - .on('click', geocoderSearch) - .append('div') - .attr('class', 'label') - .append('span') - .attr('class', 'entity-name') - .text(_t('geocoder.search')); - } - - list.selectAll('.no-results-item') - .style('display', (value.length && !results.length) ? 'block' : 'none'); - - list.selectAll('.geocode-item') - .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none'); - - list.selectAll('.feature-list-item') - .data([-1]) - .remove(); - - var items = list.selectAll('.feature-list-item') - .data(results, function(d) { return d.id; }); - - var enter = items.enter() - .insert('button', '.geocode-item') - .attr('class', 'feature-list-item') - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .on('click', click); - - var label = enter - .append('div') - .attr('class', 'label'); - - label - .each(function(d) { - select(this) - .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text')); - }); - - label - .append('span') - .attr('class', 'entity-type') - .text(function(d) { return d.type; }); - - label - .append('span') - .attr('class', 'entity-name') - .text(function(d) { return d.name; }); - - enter - .style('opacity', 0) - .transition() - .style('opacity', 1); - - items.order(); - - items.exit() - .remove(); - } - - - function mouseover(d) { - if (d.id === -1) return; - - utilHighlightEntities([d.id], true, context); - } - - - function mouseout(d) { - if (d.id === -1) return; - - utilHighlightEntities([d.id], false, context); - } - - - function click(d) { - event.preventDefault(); - - if (d.location) { - context.map().centerZoomEase([d.location[1], d.location[0]], 19); - - } else if (d.entity) { - utilHighlightEntities([d.id], false, context); - - context.enter(modeSelect(context, [d.entity.id])); - context.map().zoomToEase(d.entity); - - } else { - // download, zoom to, and select the entity with the given ID - context.zoomToEntity(d.id); - } - } - - - function geocoderSearch() { - services.geocoder.search(search.property('value'), function (err, resp) { - _geocodeResults = resp || []; - drawList(); - }); - } - } - - - return featureList; - } - - function uiSectionEntityIssues(context) { - - var _entityIDs = []; - var _issues = []; - var _activeIssueID; - - var section = uiSection('entity-issues', context) - .shouldDisplay(function() { - return _issues.length > 0; - }) - .title(function() { - return _t('issues.list_title', { count: _issues.length }); - }) - .disclosureContent(renderDisclosureContent); - - context.validator() - .on('validated.entity_issues', function() { - // Refresh on validated events - reloadIssues(); - section.reRender(); - }) - .on('focusedIssue.entity_issues', function(issue) { - makeActiveIssue(issue.id); - }); - - function reloadIssues() { - _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true }); - } - - function makeActiveIssue(issueID) { - _activeIssueID = issueID; - section.selection().selectAll('.issue-container') - .classed('active', function(d) { return d.id === _activeIssueID; }); - } - - function renderDisclosureContent(selection) { - - selection.classed('grouped-items-area', true); - - _activeIssueID = _issues.length > 0 ? _issues[0].id : null; - - var containers = selection.selectAll('.issue-container') - .data(_issues, function(d) { return d.id; }); - - // Exit - containers.exit() - .remove(); - - // Enter - var containersEnter = containers.enter() - .append('div') - .attr('class', 'issue-container'); - - - var itemsEnter = containersEnter - .append('div') - .attr('class', function(d) { return 'issue severity-' + d.severity; }) - .on('mouseover.highlight', function(d) { - // don't hover-highlight the selected entity - var ids = d.entityIds - .filter(function(e) { return _entityIDs.indexOf(e) === -1; }); - - utilHighlightEntities(ids, true, context); - }) - .on('mouseout.highlight', function(d) { - var ids = d.entityIds - .filter(function(e) { return _entityIDs.indexOf(e) === -1; }); - - utilHighlightEntities(ids, false, context); - }); - - var labelsEnter = itemsEnter - .append('div') - .attr('class', 'issue-label') - .on('click', function(d) { - - makeActiveIssue(d.id); // expand only the clicked item - - var extent = d.extent(context.graph()); - if (extent) { - var setZoom = Math.max(context.map().zoom(), 19); - context.map().unobscuredCenterZoomEase(extent.center(), setZoom); - } - }); - - var textEnter = labelsEnter - .append('span') - .attr('class', 'issue-text'); - - textEnter - .append('span') - .attr('class', 'issue-icon') - .each(function(d) { - var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error'); - select(this) - .call(svgIcon(iconName)); - }); - - textEnter - .append('span') - .attr('class', 'issue-message'); - - - var infoButton = labelsEnter - .append('button') - .attr('class', 'issue-info-button') - .attr('title', _t('icons.information')) - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-inspect')); - - infoButton - .on('click', function () { - event.stopPropagation(); - event.preventDefault(); - this.blur(); // avoid keeping focus on the button - #4641 - - var container = select(this.parentNode.parentNode.parentNode); - var info = container.selectAll('.issue-info'); - var isExpanded = info.classed('expanded'); - - if (isExpanded) { - info - .transition() - .duration(200) - .style('max-height', '0px') - .style('opacity', '0') - .on('end', function () { - info.classed('expanded', false); - }); - } else { - info - .classed('expanded', true) - .transition() - .duration(200) - .style('max-height', '200px') - .style('opacity', '1') - .on('end', function () { - info.style('max-height', null); - }); - } - }); - - itemsEnter - .append('ul') - .attr('class', 'issue-fix-list'); - - containersEnter - .append('div') - .attr('class', 'issue-info') - .style('max-height', '0') - .style('opacity', '0') - .each(function(d) { - if (typeof d.reference === 'function') { - select(this) - .call(d.reference); - } else { - select(this) - .text(_t('inspector.no_documentation_key')); - } - }); - - - // Update - containers = containers - .merge(containersEnter) - .classed('active', function(d) { return d.id === _activeIssueID; }); - - containers.selectAll('.issue-message') - .text(function(d) { - return d.message(context); - }); - - // fixes - var fixLists = containers.selectAll('.issue-fix-list'); - - var fixes = fixLists.selectAll('.issue-fix-item') - .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; }); - - fixes.exit() - .remove(); - - var fixesEnter = fixes.enter() - .append('li') - .attr('class', 'issue-fix-item') - .on('click', function(d) { - // not all fixes are actionable - if (!select(this).classed('actionable') || !d.onClick) return; - - // Don't run another fix for this issue within a second of running one - // (Necessary for "Select a feature type" fix. Most fixes should only ever run once) - if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return; - d.issue.dateLastRanFix = new Date(); - - // remove hover-highlighting - utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context); - - new Promise(function(resolve, reject) { - d.onClick(context, resolve, reject); - if (d.onClick.length <= 1) { - // if the fix doesn't take any completion parameters then consider it resolved - resolve(); - } - }) - .then(function() { - // revalidate whenever the fix has finished running successfully - context.validator().validate(); - }); - }) - .on('mouseover.highlight', function(d) { - utilHighlightEntities(d.entityIds, true, context); - }) - .on('mouseout.highlight', function(d) { - utilHighlightEntities(d.entityIds, false, context); - }); - - fixesEnter - .append('span') - .attr('class', 'fix-icon') - .each(function(d) { - var iconName = d.icon || 'iD-icon-wrench'; - if (iconName.startsWith('maki')) { - iconName += '-15'; - } - select(this).call(svgIcon('#' + iconName)); - }); - - fixesEnter - .append('span') - .attr('class', 'fix-message') - .text(function(d) { return d.title; }); - - fixesEnter.merge(fixes) - .classed('actionable', function(d) { - return d.onClick; - }) - .attr('title', function(d) { - if (d.disabledReason) { - return d.disabledReason; - } - return null; - }); - } - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) { - _entityIDs = val; - _activeIssueID = null; - reloadIssues(); - } - return section; - }; - - return section; - } - - function uiPresetIcon() { - let _preset; - let _geometry; - let _sizeClass = 'medium'; - - - function isSmall() { - return _sizeClass === 'small'; - } - - - function presetIcon(selection) { - selection.each(render); - } - - - function getIcon(p, geom) { - if (isSmall() && p.isFallback && p.isFallback()) - return 'iD-icon-' + p.id; - else if (p.icon) - return p.icon; - else if (geom === 'line') - return 'iD-other-line'; - else if (geom === 'vertex') - return p.isFallback() ? '' : 'temaki-vertex'; - else if (isSmall() && geom === 'point') - return ''; - else - return 'maki-marker-stroked'; - } - - - function renderPointBorder(enter) { - const w = 40; - const h = 40; - - enter - .append('svg') - .attr('class', 'preset-icon-fill preset-icon-point-border') - .attr('width', w) - .attr('height', h) - .attr('viewBox', `0 0 ${w} ${h}`) - .append('path') - .attr('transform', 'translate(11.5, 8)') - .attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z'); - } - - - function renderCircleFill(fillEnter) { - const w = 60; - const h = 60; - const d = 40; - - fillEnter - .append('svg') - .attr('class', 'preset-icon-fill preset-icon-fill-vertex') - .attr('width', w) - .attr('height', h) - .attr('viewBox', `0 0 ${w} ${h}`) - .append('circle') - .attr('cx', w / 2) - .attr('cy', h / 2) - .attr('r', d / 2); - } - - - function renderSquareFill(fillEnter) { - const d = isSmall() ? 40 : 60; - const w = d; - const h = d; - const l = d * 2/3; - const c1 = (w-l) / 2; - const c2 = c1 + l; - - fillEnter = fillEnter - .append('svg') - .attr('class', 'preset-icon-fill preset-icon-fill-area') - .attr('width', w) - .attr('height', h) - .attr('viewBox', `0 0 ${w} ${h}`); - - ['fill', 'stroke'].forEach(klass => { - fillEnter - .append('path') - .attr('d', `M${c1} ${c1} L${c1} ${c2} L${c2} ${c2} L${c2} ${c1} Z`) - .attr('class', `line area ${klass}`); - }); - - const rVertex = 2.5; - [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(point => { - fillEnter - .append('circle') - .attr('class', 'vertex') - .attr('cx', point[0]) - .attr('cy', point[1]) - .attr('r', rVertex); - }); - - if (!isSmall()) { - const rMidpoint = 1.25; - [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(point => { - fillEnter - .append('circle') - .attr('class', 'midpoint') - .attr('cx', point[0]) - .attr('cy', point[1]) - .attr('r', rMidpoint); - }); - } - } - - - function renderLine(lineEnter) { - const d = isSmall() ? 40 : 60; - // draw the line parametrically - const w = d; - const h = d; - const y = Math.round(d * 0.72); - const l = Math.round(d * 0.6); - const r = 2.5; - const x1 = (w - l) / 2; - const x2 = x1 + l; - - lineEnter = lineEnter - .append('svg') - .attr('class', 'preset-icon-line') - .attr('width', w) - .attr('height', h) - .attr('viewBox', `0 0 ${w} ${h}`); - - ['casing', 'stroke'].forEach(klass => { - lineEnter - .append('path') - .attr('d', `M${x1} ${y} L${x2} ${y}`) - .attr('class', `line ${klass}`); - }); - - [[x1-1, y], [x2+1, y]].forEach(point => { - lineEnter - .append('circle') - .attr('class', 'vertex') - .attr('cx', point[0]) - .attr('cy', point[1]) - .attr('r', r); - }); - } - - - function renderRoute(routeEnter) { - const d = isSmall() ? 40 : 60; - // draw the route parametrically - const w = d; - const h = d; - const y1 = Math.round(d * 0.80); - const y2 = Math.round(d * 0.68); - const l = Math.round(d * 0.6); - const r = 2; - const x1 = (w - l) / 2; - const x2 = x1 + l / 3; - const x3 = x2 + l / 3; - const x4 = x3 + l / 3; - - routeEnter = routeEnter - .append('svg') - .attr('class', 'preset-icon-route') - .attr('width', w) - .attr('height', h) - .attr('viewBox', `0 0 ${w} ${h}`); - - ['casing', 'stroke'].forEach(klass => { - routeEnter - .append('path') - .attr('d', `M${x1} ${y1} L${x2} ${y2}`) - .attr('class', `segment0 line ${klass}`); - routeEnter - .append('path') - .attr('d', `M${x2} ${y2} L${x3} ${y1}`) - .attr('class', `segment1 line ${klass}`); - routeEnter - .append('path') - .attr('d', `M${x3} ${y1} L${x4} ${y2}`) - .attr('class', `segment2 line ${klass}`); - }); - - [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(point => { - routeEnter - .append('circle') - .attr('class', 'vertex') - .attr('cx', point[0]) - .attr('cy', point[1]) - .attr('r', r); - }); - } - - - // Route icons are drawn with a zigzag annotation underneath: - // o o - // / \ / - // o o - // This dataset defines the styles that are used to draw the zigzag segments. - const routeSegments = { - bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'], - bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'], - trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'], - detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'], - ferry: ['route/ferry', 'route/ferry', 'route/ferry'], - foot: ['highway/footway', 'highway/footway', 'highway/footway'], - hiking: ['highway/path', 'highway/path', 'highway/path'], - horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'], - light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'], - monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'], - pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'], - piste: ['piste/downhill', 'piste/hike', 'piste/nordic'], - power: ['power/line', 'power/line', 'power/line'], - road: ['highway/secondary', 'highway/primary', 'highway/trunk'], - subway: ['railway/subway', 'railway/subway', 'railway/subway'], - train: ['railway/rail', 'railway/rail', 'railway/rail'], - tram: ['railway/tram', 'railway/tram', 'railway/tram'], - waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream'] - }; - - - function render() { - let p = _preset.apply(this, arguments); - let geom = _geometry ? _geometry.apply(this, arguments) : null; - if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) { - geom = 'route'; - } - - const showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; - const isFallback = isSmall() && p.isFallback && p.isFallback(); - const imageURL = (showThirdPartyIcons === 'true') && p.imageURL; - const picon = getIcon(p, geom); - const isMaki = picon && /^maki-/.test(picon); - const isTemaki = picon && /^temaki-/.test(picon); - const isFa = picon && /^fa[srb]-/.test(picon); - const isTnp = picon && /^tnp-/.test(picon); - const isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp); - const isCategory = !p.setTags; - const drawPoint = picon && geom === 'point' && isSmall() && !isFallback; - const drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback); - const drawLine = picon && geom === 'line' && !isFallback && !isCategory; - const drawArea = picon && geom === 'area' && !isFallback; - const drawRoute = picon && geom === 'route'; - const isFramed = (drawVertex || drawArea || drawLine || drawRoute); - - let tags = !isCategory ? p.setTags({}, geom) : {}; - for (let k in tags) { - if (tags[k] === '*') { - tags[k] = 'yes'; - } - } - - let tagClasses = svgTagClasses().getClassesString(tags, ''); - let selection = select(this); - - let container = selection.selectAll('.preset-icon-container') - .data([0]); - - container = container.enter() - .append('div') - .attr('class', `preset-icon-container ${_sizeClass}`) - .merge(container); - - container - .classed('showing-img', !!imageURL) - .classed('fallback', isFallback); - - - let pointBorder = container.selectAll('.preset-icon-point-border') - .data(drawPoint ? [0] : []); - - pointBorder.exit() - .remove(); - - let pointBorderEnter = pointBorder.enter(); - renderPointBorder(pointBorderEnter); - pointBorder = pointBorderEnter.merge(pointBorder); - - - let vertexFill = container.selectAll('.preset-icon-fill-vertex') - .data(drawVertex ? [0] : []); - - vertexFill.exit() - .remove(); - - let vertexFillEnter = vertexFill.enter(); - renderCircleFill(vertexFillEnter); - vertexFill = vertexFillEnter.merge(vertexFill); - - - let fill = container.selectAll('.preset-icon-fill-area') - .data(drawArea ? [0] : []); - - fill.exit() - .remove(); - - let fillEnter = fill.enter(); - renderSquareFill(fillEnter); - fill = fillEnter.merge(fill); - - fill.selectAll('path.stroke') - .attr('class', `area stroke ${tagClasses}`); - fill.selectAll('path.fill') - .attr('class', `area fill ${tagClasses}`); - - - let line = container.selectAll('.preset-icon-line') - .data(drawLine ? [0] : []); - - line.exit() - .remove(); - - let lineEnter = line.enter(); - renderLine(lineEnter); - line = lineEnter.merge(line); - - line.selectAll('path.stroke') - .attr('class', `line stroke ${tagClasses}`); - line.selectAll('path.casing') - .attr('class', `line casing ${tagClasses}`); - - - let route = container.selectAll('.preset-icon-route') - .data(drawRoute ? [0] : []); - - route.exit() - .remove(); - - let routeEnter = route.enter(); - renderRoute(routeEnter); - route = routeEnter.merge(route); - - if (drawRoute) { - let routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route; - const segmentPresetIDs = routeSegments[routeType]; - for (let i in segmentPresetIDs) { - const segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]); - const segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, ''); - route.selectAll(`path.stroke.segment${i}`) - .attr('class', `segment${i} line stroke ${segmentTagClasses}`); - route.selectAll(`path.casing.segment${i}`) - .attr('class', `segment${i} line casing ${segmentTagClasses}`); - } - } - - - let icon = container.selectAll('.preset-icon') - .data(picon ? [0] : []); - - icon.exit() - .remove(); - - icon = icon.enter() - .append('div') - .attr('class', 'preset-icon') - .call(svgIcon('')) - .merge(icon); - - icon - .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')) - .classed('framed', isFramed) - .classed('preset-icon-iD', isiDIcon); - - icon.selectAll('svg') - .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses)); - - icon.selectAll('use') - .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : '')); - - let imageIcon = container.selectAll('img.image-icon') - .data(imageURL ? [0] : []); - - imageIcon.exit() - .remove(); - - imageIcon = imageIcon.enter() - .append('img') - .attr('class', 'image-icon') - .on('load', () => container.classed('showing-img', true) ) - .on('error', () => container.classed('showing-img', false) ) - .merge(imageIcon); - - imageIcon - .attr('src', imageURL); - } - - - presetIcon.preset = function(val) { - if (!arguments.length) return _preset; - _preset = utilFunctor(val); - return presetIcon; - }; - - - presetIcon.geometry = function(val) { - if (!arguments.length) return _geometry; - _geometry = utilFunctor(val); - return presetIcon; - }; - - - presetIcon.sizeClass = function(val) { - if (!arguments.length) return _sizeClass; - _sizeClass = val; - return presetIcon; - }; - - return presetIcon; - } - - function uiSectionFeatureType(context) { - - var dispatch$1 = dispatch('choose'); - - var _entityIDs = []; - var _presets = []; - - var _tagReference; - - var section = uiSection('feature-type', context) - .title(_t('inspector.feature_type')) - .disclosureContent(renderDisclosureContent); - - function renderDisclosureContent(selection) { - - selection.classed('preset-list-item', true); - selection.classed('mixed-types', _presets.length > 1); - - var presetButtonWrap = selection - .selectAll('.preset-list-button-wrap') - .data([0]) - .enter() - .append('div') - .attr('class', 'preset-list-button-wrap'); - - var presetButton = presetButtonWrap - .append('button') - .attr('class', 'preset-list-button preset-reset') - .call(uiTooltip() - .title(_t('inspector.back_tooltip')) - .placement('bottom') - ); - - presetButton.append('div') - .attr('class', 'preset-icon-container'); - - presetButton - .append('div') - .attr('class', 'label') - .append('div') - .attr('class', 'label-inner'); - - presetButtonWrap.append('div') - .attr('class', 'accessory-buttons'); - - var tagReferenceBodyWrap = selection - .selectAll('.tag-reference-body-wrap') - .data([0]); - - tagReferenceBodyWrap = tagReferenceBodyWrap - .enter() - .append('div') - .attr('class', 'tag-reference-body-wrap') - .merge(tagReferenceBodyWrap); - - // update header - if (_tagReference) { - selection.selectAll('.preset-list-button-wrap .accessory-buttons') - .style('display', _presets.length === 1 ? null : 'none') - .call(_tagReference.button); - - tagReferenceBodyWrap - .style('display', _presets.length === 1 ? null : 'none') - .call(_tagReference.body); - } - - selection.selectAll('.preset-reset') - .on('click', function() { - dispatch$1.call('choose', this, _presets); - }) - .on('pointerdown pointerup mousedown mouseup', function() { - event.preventDefault(); - event.stopPropagation(); - }); - - var geometries = entityGeometries(); - selection.select('.preset-list-item button') - .call(uiPresetIcon() - .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null) - .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point')) - ); - - // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names) - var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')]; - - var label = selection.select('.label-inner'); - var nameparts = label.selectAll('.namepart') - .data(names, function(d) { return d; }); - - nameparts.exit() - .remove(); - - nameparts - .enter() - .append('div') - .attr('class', 'namepart') - .text(function(d) { return d; }); - } - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return section; - }; - - section.presets = function(val) { - if (!arguments.length) return _presets; - - // don't reload the same preset - if (!utilArrayIdentical(val, _presets)) { - _presets = val; - - var geometries = entityGeometries(); - if (_presets.length === 1 && geometries.length) { - _tagReference = uiTagReference(_presets[0].reference(geometries[0])) - .showing(false); - } - } - - return section; - }; - - function entityGeometries() { - - var counts = {}; - - for (var i in _entityIDs) { - var geometry = context.graph().geometry(_entityIDs[i]); - if (!counts[geometry]) counts[geometry] = 0; - counts[geometry] += 1; - } - - return Object.keys(counts).sort(function(geom1, geom2) { - return counts[geom2] - counts[geom1]; - }); - } - - return utilRebind(section, dispatch$1, 'on'); - } - - // This currently only works with the 'restrictions' field - // It borrows some code from uiHelp - - function uiFieldHelp(context, fieldName) { - var fieldHelp = {}; - var _inspector = select(null); - var _wrap = select(null); - var _body = select(null); - - var fieldHelpKeys = { - restrictions: [ - ['about',[ - 'about', - 'from_via_to', - 'maxdist', - 'maxvia' - ]], - ['inspecting',[ - 'about', - 'from_shadow', - 'allow_shadow', - 'restrict_shadow', - 'only_shadow', - 'restricted', - 'only' - ]], - ['modifying',[ - 'about', - 'indicators', - 'allow_turn', - 'restrict_turn', - 'only_turn' - ]], - ['tips',[ - 'simple', - 'simple_example', - 'indirect', - 'indirect_example', - 'indirect_noedit' - ]] - ] - }; - - var fieldHelpHeadings = {}; - - var replacements = { - distField: _t('restriction.controls.distance'), - viaField: _t('restriction.controls.via'), - fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'), - allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'), - restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'), - onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'), - allowTurn: icon('#iD-turn-yes', 'pre-text turn'), - restrictTurn: icon('#iD-turn-no', 'pre-text turn'), - onlyTurn: icon('#iD-turn-only', 'pre-text turn') - }; - - - // For each section, squash all the texts into a single markdown document - var docs = fieldHelpKeys[fieldName].map(function(key) { - var helpkey = 'help.field.' + fieldName + '.' + key[0]; - var text = key[1].reduce(function(all, part) { - var subkey = helpkey + '.' + part; - var depth = fieldHelpHeadings[subkey]; // is this subkey a heading? - var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s - return all + hhh + _t(subkey, replacements) + '\n\n'; - }, ''); - - return { - key: helpkey, - title: _t(helpkey + '.title'), - html: marked_1(text.trim()) - }; - }); - - - function show() { - updatePosition(); - - _body - .classed('hide', false) - .style('opacity', '0') - .transition() - .duration(200) - .style('opacity', '1'); - } - - - function hide() { - _body - .classed('hide', true) - .transition() - .duration(200) - .style('opacity', '0') - .on('end', function () { - _body.classed('hide', true); - }); - } - - - function clickHelp(index) { - var d = docs[index]; - var tkeys = fieldHelpKeys[fieldName][index][1]; - - _body.selectAll('.field-help-nav-item') - .classed('active', function(d, i) { return i === index; }); - - var content = _body.selectAll('.field-help-content') - .html(d.html); - - // class the paragraphs so we can find and style them - content.selectAll('p') - .attr('class', function(d, i) { return tkeys[i]; }); - - // insert special content for certain help sections - if (d.key === 'help.field.restrictions.inspecting') { - content - .insert('img', 'p.from_shadow') - .attr('class', 'field-help-image cf') - .attr('src', context.imagePath('tr_inspect.gif')); - - } else if (d.key === 'help.field.restrictions.modifying') { - content - .insert('img', 'p.allow_turn') - .attr('class', 'field-help-image cf') - .attr('src', context.imagePath('tr_modify.gif')); - } - } - - - fieldHelp.button = function(selection) { - if (_body.empty()) return; - - var button = selection.selectAll('.field-help-button') - .data([0]); - - // enter/update - button.enter() - .append('button') - .attr('class', 'field-help-button') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-help')) - .merge(button) - .on('click', function () { - event.stopPropagation(); - event.preventDefault(); - if (_body.classed('hide')) { - show(); - } else { - hide(); - } - }); - }; - - - function updatePosition() { - var wrap = _wrap.node(); - var inspector = _inspector.node(); - var wRect = wrap.getBoundingClientRect(); - var iRect = inspector.getBoundingClientRect(); - - _body - .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px'); - } - - - fieldHelp.body = function(selection) { - // This control expects the field to have a form-field-input-wrap div - _wrap = selection.selectAll('.form-field-input-wrap'); - if (_wrap.empty()) return; - - // absolute position relative to the inspector, so it "floats" above the fields - _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body'); - if (_inspector.empty()) return; - - _body = _inspector.selectAll('.field-help-body') - .data([0]); - - var enter = _body.enter() - .append('div') - .attr('class', 'field-help-body hide'); // initially hidden - - var titleEnter = enter - .append('div') - .attr('class', 'field-help-title cf'); - - titleEnter - .append('h2') - .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl')) - .text(_t('help.field.' + fieldName + '.title')); - - titleEnter - .append('button') - .attr('class', 'fr close') - .on('click', function() { - event.stopPropagation(); - event.preventDefault(); - hide(); - }) - .call(svgIcon('#iD-icon-close')); - - var navEnter = enter - .append('div') - .attr('class', 'field-help-nav cf'); - - var titles = docs.map(function(d) { return d.title; }); - navEnter.selectAll('.field-help-nav-item') - .data(titles) - .enter() - .append('div') - .attr('class', 'field-help-nav-item') - .text(function(d) { return d; }) - .on('click', function(d, i) { - event.stopPropagation(); - event.preventDefault(); - clickHelp(i); - }); - - enter - .append('div') - .attr('class', 'field-help-content'); - - _body = _body - .merge(enter); - - clickHelp(0); - }; - - - return fieldHelp; - } - - function uiFieldCheck(field, context) { - var dispatch$1 = dispatch('change'); - var options = field.strings && field.strings.options; - var values = []; - var texts = []; - - var _tags; - - var input = select(null); - var text = select(null); - var label = select(null); - var reverser = select(null); - - var _impliedYes; - var _entityIDs = []; - var _value; - - - if (options) { - for (var k in options) { - values.push(k === 'undefined' ? undefined : k); - texts.push(field.t('options.' + k, { 'default': options[k] })); - } - } else { - values = [undefined, 'yes']; - texts = [_t('inspector.unknown'), _t('inspector.check.yes')]; - if (field.type !== 'defaultCheck') { - values.push('no'); - texts.push(_t('inspector.check.no')); - } - } - - - // Checks tags to see whether an undefined value is "Assumed to be Yes" - function checkImpliedYes() { - _impliedYes = (field.id === 'oneway_yes'); - - // hack: pretend `oneway` field is a `oneway_yes` field - // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841 - if (field.id === 'oneway') { - var entity = context.entity(_entityIDs[0]); - for (var key in entity.tags) { - if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) { - _impliedYes = true; - texts[0] = _t('presets.fields.oneway_yes.options.undefined'); - break; - } - } - } - } - - - function reverserHidden() { - if (!context.container().select('div.inspector-hover').empty()) return true; - return !(_value === 'yes' || (_impliedYes && !_value)); - } - - - function reverserSetText(selection) { - var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]); - if (reverserHidden() || !entity) return selection; - - var first = entity.first(); - var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last(); - var pseudoDirection = first < last; - var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward'; - - selection.selectAll('.reverser-span') - .text(_t('inspector.check.reverser')) - .call(svgIcon(icon, 'inline')); - - return selection; - } - - - var check = function(selection) { - checkImpliedYes(); - - label = selection.selectAll('.form-field-input-wrap') - .data([0]); - - var enter = label.enter() - .append('label') - .attr('class', 'form-field-input-wrap form-field-input-check'); - - enter - .append('input') - .property('indeterminate', field.type !== 'defaultCheck') - .attr('type', 'checkbox') - .attr('id', field.domId); - - enter - .append('span') - .text(texts[0]) - .attr('class', 'value'); - - if (field.type === 'onewayCheck') { - enter - .append('a') - .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : '')) - .attr('href', '#') - .append('span') - .attr('class', 'reverser-span'); - } - - label = label.merge(enter); - input = label.selectAll('input'); - text = label.selectAll('span.value'); - - input - .on('click', function() { - event.stopPropagation(); - var t = {}; - - if (Array.isArray(_tags[field.key])) { - if (values.indexOf('yes') !== -1) { - t[field.key] = 'yes'; - } else { - t[field.key] = values[0]; - } - } else { - t[field.key] = values[(values.indexOf(_value) + 1) % values.length]; - } - - // Don't cycle through `alternating` or `reversible` states - #4970 - // (They are supported as translated strings, but should not toggle with clicks) - if (t[field.key] === 'reversible' || t[field.key] === 'alternating') { - t[field.key] = values[0]; - } - - dispatch$1.call('change', this, t); - }); - - if (field.type === 'onewayCheck') { - reverser = label.selectAll('.reverser'); - - reverser - .call(reverserSetText) - .on('click', function() { - event.preventDefault(); - event.stopPropagation(); - context.perform( - function(graph) { - for (var i in _entityIDs) { - graph = actionReverse(_entityIDs[i])(graph); - } - return graph; - }, - _t('operations.reverse.annotation') - ); - - // must manually revalidate since no 'change' event was called - context.validator().validate(); - - select(this) - .call(reverserSetText); - }); - } - }; - - - check.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return check; - }; - - - check.tags = function(tags) { - - _tags = tags; - - function isChecked(val) { - return val !== 'no' && val !== '' && val !== undefined && val !== null; - } - - function textFor(val) { - if (val === '') val = undefined; - var index = values.indexOf(val); - return (index !== -1 ? texts[index] : ('"' + val + '"')); - } - - checkImpliedYes(); - - var isMixed = Array.isArray(tags[field.key]); - - _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase(); - - if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) { - _value = 'yes'; - } - - input - .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value)) - .property('checked', isChecked(_value)); - - text - .text(isMixed ? _t('inspector.multiple_values') : textFor(_value)) - .classed('mixed', isMixed); - - label - .classed('set', !!_value); - - if (field.type === 'onewayCheck') { - reverser - .classed('hide', reverserHidden()) - .call(reverserSetText); - } - }; - - - check.focus = function() { - input.node().focus(); - }; - - return utilRebind(check, dispatch$1, 'on'); - } - - function uiFieldCombo(field, context) { - var dispatch$1 = dispatch('change'); - var taginfo = services.taginfo; - var isMulti = (field.type === 'multiCombo'); - var isNetwork = (field.type === 'networkCombo'); - var isSemi = (field.type === 'semiCombo'); - var optstrings = field.strings && field.strings.options; - var optarray = field.options; - var snake_case = (field.snake_case || (field.snake_case === undefined)); - var caseSensitive = field.caseSensitive; - var combobox = uiCombobox(context, 'combo-' + field.safeid) - .caseSensitive(caseSensitive) - .minItems(isMulti || isSemi ? 1 : 2); - var container = select(null); - var inputWrap = select(null); - var input = select(null); - var _comboData = []; - var _multiData = []; - var _entityIDs = []; - var _tags; - var _countryCode; - var _staticPlaceholder; - - // initialize deprecated tags array - var _dataDeprecated = []; - _mainFileFetcher.get('deprecated') - .then(function(d) { _dataDeprecated = d; }) - .catch(function() { /* ignore */ }); - - - // ensure multiCombo field.key ends with a ':' - if (isMulti && /[^:]$/.test(field.key)) { - field.key += ':'; - } - - - function snake(s) { - return s.replace(/\s+/g, '_'); - } - - function unsnake(s) { - return s.replace(/_+/g, ' '); - } - - function clean(s) { - return s.split(';') - .map(function(s) { return s.trim(); }) - .join(';'); - } - - - // returns the tag value for a display value - // (for multiCombo, dval should be the key suffix, not the entire key) - function tagValue(dval) { - dval = clean(dval || ''); - - if (optstrings) { - var found = _comboData.find(function(o) { - return o.key && clean(o.value) === dval; - }); - if (found) { - return found.key; - } - } - - if (field.type === 'typeCombo' && !dval) { - return 'yes'; - } - - return (snake_case ? snake(dval) : dval) || undefined; - } - - - // returns the display value for a tag value - // (for multiCombo, tval should be the key suffix, not the entire key) - function displayValue(tval) { - tval = tval || ''; - - if (optstrings) { - var found = _comboData.find(function(o) { - return o.key === tval && o.value; - }); - if (found) { - return found.value; - } - } - - if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') { - return ''; - } - - return snake_case ? unsnake(tval) : tval; - } - - - // Compute the difference between arrays of objects by `value` property - // - // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}]) - // > [{value:1}, {value:3}] - // - function objectDifference(a, b) { - return a.filter(function(d1) { - return !b.some(function(d2) { - return !d2.isMixed && d1.value === d2.value; - }); - }); - } - - - function initCombo(selection, attachTo) { - if (optstrings) { - selection.attr('readonly', 'readonly'); - selection.call(combobox, attachTo); - setStaticValues(setPlaceholder); - - } else if (optarray) { - selection.call(combobox, attachTo); - setStaticValues(setPlaceholder); - - } else if (taginfo) { - selection.call(combobox.fetcher(setTaginfoValues), attachTo); - setTaginfoValues('', setPlaceholder); - } - } - - - function setStaticValues(callback) { - if (!(optstrings || optarray)) return; - - if (optstrings) { - _comboData = Object.keys(optstrings).map(function(k) { - var v = field.t('options.' + k, { 'default': optstrings[k] }); - return { - key: k, - value: v, - title: v - }; - }); - - } else if (optarray) { - _comboData = optarray.map(function(k) { - var v = snake_case ? unsnake(k) : k; - return { - key: k, - value: v, - title: v - }; - }); - } - - combobox.data(objectDifference(_comboData, _multiData)); - if (callback) callback(_comboData); - } - - - function setTaginfoValues(q, callback) { - var fn = isMulti ? 'multikeys' : 'values'; - var query = (isMulti ? field.key : '') + q; - var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0; - if (hasCountryPrefix) { - query = _countryCode + ':'; - } - - var params = { - debounce: (q !== ''), - key: field.key, - query: query - }; - - if (_entityIDs.length) { - params.geometry = context.graph().geometry(_entityIDs[0]); - } - - taginfo[fn](params, function(err, data) { - if (err) return; - - data = data.filter(function(d) { - - if (field.type === 'typeCombo' && d.value === 'yes') { - // don't show the fallback value - return false; - } - - // don't show values with very low usage - return !d.count || d.count > 10; - }); - - var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key]; - if (deprecatedValues) { - // don't suggest deprecated tag values - data = data.filter(function(d) { - return deprecatedValues.indexOf(d.value) === -1; - }); - } - - if (hasCountryPrefix) { - data = data.filter(function(d) { - return d.value.toLowerCase().indexOf(_countryCode + ':') === 0; - }); - } - - // hide the caret if there are no suggestions - container.classed('empty-combobox', data.length === 0); - - _comboData = data.map(function(d) { - var k = d.value; - if (isMulti) k = k.replace(field.key, ''); - var v = snake_case ? unsnake(k) : k; - return { - key: k, - value: v, - title: isMulti ? v : d.title - }; - }); - - _comboData = objectDifference(_comboData, _multiData); - if (callback) callback(_comboData); - }); - } - - - function setPlaceholder(values) { - - if (isMulti || isSemi) { - _staticPlaceholder = field.placeholder() || _t('inspector.add'); - } else { - var vals = values - .map(function(d) { return d.value; }) - .filter(function(s) { return s.length < 20; }); - - var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; }); - _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', '); - } - - if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) { - _staticPlaceholder += '…'; - } - - var ph; - if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) { - ph = _t('inspector.multiple_values'); - } else { - ph = _staticPlaceholder; - } - - container.selectAll('input') - .attr('placeholder', ph); - } - - - function change() { - var t = {}; - var val; - - if (isMulti || isSemi) { - val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || ''; - container.classed('active', false); - utilGetSetValue(input, ''); - - var vals = val.split(';').filter(Boolean); - if (!vals.length) return; - - if (isMulti) { - utilArrayUniq(vals).forEach(function(v) { - var key = field.key + v; - if (_tags) { - // don't set a multicombo value to 'yes' if it already has a non-'no' value - // e.g. `language:de=main` - var old = _tags[key]; - if (typeof old === 'string' && old.toLowerCase() !== 'no') return; - } - key = context.cleanTagKey(key); - field.keys.push(key); - t[key] = 'yes'; - }); - - } else if (isSemi) { - var arr = _multiData.map(function(d) { return d.key; }); - arr = arr.concat(vals); - t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';')); - } - - window.setTimeout(function() { input.node().focus(); }, 10); - - } else { - var rawValue = utilGetSetValue(input); - - // don't override multiple values with blank string - if (!rawValue && Array.isArray(_tags[field.key])) return; - - val = context.cleanTagValue(tagValue(rawValue)); - t[field.key] = val || undefined; - } - - dispatch$1.call('change', this, t); - } - - - function removeMultikey(d) { - event.stopPropagation(); - var t = {}; - if (isMulti) { - t[d.key] = undefined; - } else if (isSemi) { - var arr = _multiData.map(function(md) { - return md.key === d.key ? null : md.key; - }).filter(Boolean); - - arr = utilArrayUniq(arr); - t[field.key] = arr.length ? arr.join(';') : undefined; - } - dispatch$1.call('change', this, t); - } - - - function combo(selection) { - container = selection.selectAll('.form-field-input-wrap') - .data([0]); - - var type = (isMulti || isSemi) ? 'multicombo': 'combo'; - container = container.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + type) - .merge(container); - - if (isMulti || isSemi) { - container = container.selectAll('.chiplist') - .data([0]); - - var listClass = 'chiplist'; - - // Use a separate line for each value in the Destinations field - // to mimic highway exit signs - if (field.key === 'destination') { - listClass += ' full-line-chips'; - } - - container = container.enter() - .append('ul') - .attr('class', listClass) - .on('click', function() { - window.setTimeout(function() { input.node().focus(); }, 10); - }) - .merge(container); - - - inputWrap = container.selectAll('.input-wrap') - .data([0]); - - inputWrap = inputWrap.enter() - .append('li') - .attr('class', 'input-wrap') - .merge(inputWrap); - - input = inputWrap.selectAll('input') - .data([0]); - } else { - input = container.selectAll('input') - .data([0]); - } - - input = input.enter() - .append('input') - .attr('type', 'text') - .attr('id', field.domId) - .call(utilNoAuto) - .call(initCombo, selection) - .merge(input); - - if (isNetwork) { - var extent = combinedEntityExtent(); - var countryCode = extent && iso1A2Code(extent.center()); - _countryCode = countryCode && countryCode.toLowerCase(); - } - - input - .on('change', change) - .on('blur', change); - - input - .on('keydown.field', function() { - switch (event.keyCode) { - case 13: // ↩ Return - input.node().blur(); // blurring also enters the value - event.stopPropagation(); - break; - } - }); - - if (isMulti || isSemi) { - combobox - .on('accept', function() { - input.node().blur(); - input.node().focus(); - }); - - input - .on('focus', function() { container.classed('active', true); }); - } - } - - - combo.tags = function(tags) { - _tags = tags; - - if (isMulti || isSemi) { - _multiData = []; - - var maxLength; - - if (isMulti) { - // Build _multiData array containing keys already set.. - for (var k in tags) { - if (k.indexOf(field.key) !== 0) continue; - var v = tags[k]; - if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) continue; - - var suffix = k.substring(field.key.length); - _multiData.push({ - key: k, - value: displayValue(suffix), - isMixed: Array.isArray(v) - }); - } - - // Set keys for form-field modified (needed for undo and reset buttons).. - field.keys = _multiData.map(function(d) { return d.key; }); - - // limit the input length so it fits after prepending the key prefix - maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key); - - } else if (isSemi) { - - var allValues = []; - var commonValues; - if (Array.isArray(tags[field.key])) { - - tags[field.key].forEach(function(tagVal) { - var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean); - allValues = allValues.concat(thisVals); - if (!commonValues) { - commonValues = thisVals; - } else { - commonValues = commonValues.filter(value => thisVals.includes(value)); - } - }); - allValues = utilArrayUniq(allValues).filter(Boolean); - - } else { - allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean); - commonValues = allValues; - } - - _multiData = allValues.map(function(v) { - return { - key: v, - value: displayValue(v), - isMixed: !commonValues.includes(v) - }; - }); - - var currLength = utilUnicodeCharsCount(commonValues.join(';')); - - // limit the input length to the remaining available characters - maxLength = context.maxCharsForTagValue() - currLength; - - if (currLength > 0) { - // account for the separator if a new value will be appended to existing - maxLength -= 1; - } - } - // a negative maxlength doesn't make sense - maxLength = Math.max(0, maxLength); - - var allowDragAndDrop = isSemi // only semiCombo values are ordered - && !Array.isArray(tags[field.key]); - - // Exclude existing multikeys from combo options.. - var available = objectDifference(_comboData, _multiData); - combobox.data(available); - - // Hide 'Add' button if this field uses fixed set of - // translateable optstrings and they're all currently used, - // or if the field is already at its character limit - var hideAdd = (optstrings && !available.length) || maxLength <= 0; - container.selectAll('.chiplist .input-wrap') - .style('display', hideAdd ? 'none' : null); - - - // Render chips - var chips = container.selectAll('.chip') - .data(_multiData); - - chips.exit() - .remove(); - - var enter = chips.enter() - .insert('li', '.input-wrap') - .attr('class', 'chip'); - - enter.append('span'); - enter.append('a'); - - chips = chips.merge(enter) - .order() - .classed('draggable', allowDragAndDrop) - .classed('mixed', function(d) { - return d.isMixed; - }) - .attr('title', function(d) { - return d.isMixed ? _t('inspector.unshared_value_tooltip') : null; - }); - - if (allowDragAndDrop) { - registerDragAndDrop(chips); - } - - chips.select('span') - .text(function(d) { return d.value; }); - - chips.select('a') - .on('click', removeMultikey) - .attr('class', 'remove') - .text('×'); - - } else { - var isMixed = Array.isArray(tags[field.key]); - - var mixedValues = isMixed && tags[field.key].map(function(val) { - return displayValue(val); - }).filter(Boolean); - - utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '') - .attr('title', isMixed ? mixedValues.join('\n') : undefined) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '') - .classed('mixed', isMixed); - } - }; - - function registerDragAndDrop(selection) { - - // allow drag and drop re-ordering of chips - var dragOrigin, targetIndex; - selection.call(d3_drag() - .on('start', function() { - dragOrigin = { - x: event.x, - y: event.y - }; - targetIndex = null; - }) - .on('drag', function(d, index) { - var x = event.x - dragOrigin.x, - y = event.y - dragOrigin.y; - - if (!select(this).classed('dragging') && - // don't display drag until dragging beyond a distance threshold - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; - - select(this) - .classed('dragging', true); - - targetIndex = null; - var targetIndexOffsetTop = null; - var draggedTagWidth = select(this).node().offsetWidth; - - if (field.key === 'destination') { // meaning tags are full width - container.selectAll('.chip') - .style('transform', function(d2, index2) { - var node = select(this).node(); - - if (index === index2) { - return 'translate(' + x + 'px, ' + y + 'px)'; - // move the dragged tag up the order - } else if (index2 > index && event.y > node.offsetTop) { - if (targetIndex === null || index2 > targetIndex) { - targetIndex = index2; - } - return 'translateY(-100%)'; - // move the dragged tag down the order - } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) { - if (targetIndex === null || index2 < targetIndex) { - targetIndex = index2; - } - return 'translateY(100%)'; - } - return null; - }); - } else { - container.selectAll('.chip') - .each(function(d2, index2) { - var node = select(this).node(); - - // check the cursor is in the bounding box - if ( - index !== index2 && - event.x < node.offsetLeft + node.offsetWidth + 5 && - event.x > node.offsetLeft && - event.y < node.offsetTop + node.offsetHeight && - event.y > node.offsetTop - ) { - targetIndex = index2; - targetIndexOffsetTop = node.offsetTop; - } - }) - .style('transform', function(d2, index2) { - var node = select(this).node(); - - if (index === index2) { - return 'translate(' + x + 'px, ' + y + 'px)'; - } - - // only translate tags in the same row - if (node.offsetTop === targetIndexOffsetTop) { - if (index2 < index && index2 >= targetIndex) { - return 'translateX(' + draggedTagWidth + 'px)'; - } else if (index2 > index && index2 <= targetIndex) { - return 'translateX(-' + draggedTagWidth + 'px)'; - } - } - return null; - }); - } - }) - .on('end', function(d, index) { - if (!select(this).classed('dragging')) { - return; - } - - select(this) - .classed('dragging', false); - - container.selectAll('.chip') - .style('transform', null); - - if (typeof targetIndex === 'number') { - var element = _multiData[index]; - _multiData.splice(index, 1); - _multiData.splice(targetIndex, 0, element); - - var t = {}; - - if (_multiData.length) { - t[field.key] = _multiData.map(function(element) { - return element.key; - }).join(';'); - } else { - t[field.key] = undefined; - } - - dispatch$1.call('change', this, t); - } - dragOrigin = undefined; - targetIndex = undefined; - }) - ); - } - - - combo.focus = function() { - input.node().focus(); - }; - - - combo.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return combo; - }; - - - function combinedEntityExtent() { - return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); - } - - - return utilRebind(combo, dispatch$1, 'on'); - } - - function uiFieldText(field, context) { - var dispatch$1 = dispatch('change'); - var input = select(null); - var outlinkButton = select(null); - var _entityIDs = []; - var _tags; - var _phoneFormats = {}; - - if (field.type === 'tel') { - _mainFileFetcher.get('phone_formats') - .then(function(d) { - _phoneFormats = d; - updatePhonePlaceholder(); - }) - .catch(function() { /* ignore */ }); - } - - function i(selection) { - var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]); - var preset = entity && _mainPresetIndex.match(entity, context.graph()); - var isLocked = preset && preset.suggestion && field.id === 'brand'; - field.locked(isLocked); - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - input = wrap.selectAll('input') - .data([0]); - - input = input.enter() - .append('input') - .attr('type', field.type === 'identifier' ? 'text' : field.type) - .attr('id', field.domId) - .classed(field.type, true) - .call(utilNoAuto) - .merge(input); - - input - .classed('disabled', !!isLocked) - .attr('readonly', isLocked || null) - .on('input', change(true)) - .on('blur', change()) - .on('change', change()); - - - if (field.type === 'tel') { - updatePhonePlaceholder(); - - } else if (field.type === 'number') { - var rtl = (_mainLocalizer.textDirection() === 'rtl'); - - input.attr('type', 'text'); - - var buttons = wrap.selectAll('.increment, .decrement') - .data(rtl ? [1, -1] : [-1, 1]); - - buttons.enter() - .append('button') - .attr('tabindex', -1) - .attr('class', function(d) { - var which = (d === 1 ? 'increment' : 'decrement'); - return 'form-field-button ' + which; - }) - .merge(buttons) - .on('click', function(d) { - event.preventDefault(); - var raw_vals = input.node().value || '0'; - var vals = raw_vals.split(';'); - vals = vals.map(function(v) { - var num = parseFloat(v.trim(), 10); - return isFinite(num) ? clamped(num + d) : v.trim(); - }); - input.node().value = vals.join(';'); - change()(); - }); - } else if (field.type === 'identifier' && field.urlFormat && field.pattern) { - - input.attr('type', 'text'); - - outlinkButton = wrap.selectAll('.foreign-id-permalink') - .data([0]); - - outlinkButton.enter() - .append('button') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-out-link')) - .attr('class', 'form-field-button foreign-id-permalink') - .attr('title', function() { - var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat); - if (domainResults.length >= 2 && domainResults[1]) { - var domain = domainResults[1]; - return _t('icons.view_on', { domain: domain }); - } - return ''; - }) - .on('click', function() { - event.preventDefault(); - - var value = validIdentifierValueForLink(); - if (value) { - var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value)); - window.open(url, '_blank'); - } - }) - .merge(outlinkButton); - } - } - - - function updatePhonePlaceholder() { - if (input.empty() || !Object.keys(_phoneFormats).length) return; - - var extent = combinedEntityExtent(); - var countryCode = extent && iso1A2Code(extent.center()); - var format = countryCode && _phoneFormats[countryCode.toLowerCase()]; - if (format) input.attr('placeholder', format); - } - - - function validIdentifierValueForLink() { - if (field.type === 'identifier' && field.pattern) { - var value = utilGetSetValue(input).trim().split(';')[0]; - return value && value.match(new RegExp(field.pattern)); - } - return null; - } - - - // clamp number to min/max - function clamped(num) { - if (field.minValue !== undefined) { - num = Math.max(num, field.minValue); - } - if (field.maxValue !== undefined) { - num = Math.min(num, field.maxValue); - } - return num; - } - - - function change(onInput) { - return function() { - var t = {}; - var val = utilGetSetValue(input); - if (!onInput) val = context.cleanTagValue(val); - - // don't override multiple values with blank string - if (!val && Array.isArray(_tags[field.key])) return; - - if (!onInput) { - if (field.type === 'number' && val) { - var vals = val.split(';'); - vals = vals.map(function(v) { - var num = parseFloat(v.trim(), 10); - return isFinite(num) ? clamped(num) : v.trim(); - }); - val = vals.join(';'); - } - utilGetSetValue(input, val); - } - t[field.key] = val || undefined; - dispatch$1.call('change', this, t, onInput); - }; - } - - - i.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return i; - }; - - - i.tags = function(tags) { - _tags = tags; - - var isMixed = Array.isArray(tags[field.key]); - - utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '') - .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown'))) - .classed('mixed', isMixed); - - if (outlinkButton && !outlinkButton.empty()) { - var disabled = !validIdentifierValueForLink(); - outlinkButton.classed('disabled', disabled); - } - }; - - - i.focus = function() { - var node = input.node(); - if (node) node.focus(); - }; - - function combinedEntityExtent() { - return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); - } - - return utilRebind(i, dispatch$1, 'on'); - } - - function uiFieldAccess(field, context) { - var dispatch$1 = dispatch('change'); - var items = select(null); - var _tags; - - function access(selection) { - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - var list = wrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'rows') - .merge(list); - - - items = list.selectAll('li') - .data(field.keys); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function(d) { return 'labeled-input preset-access-' + d; }); - - enter - .append('span') - .attr('class', 'label preset-label-access') - .attr('for', function(d) { return 'preset-input-access-' + d; }) - .text(function(d) { return field.t('types.' + d); }); - - enter - .append('div') - .attr('class', 'preset-input-access-wrap') - .append('input') - .attr('type', 'text') - .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; }) - .call(utilNoAuto) - .each(function(d) { - select(this) - .call(uiCombobox(context, 'access-' + d) - .data(access.options(d)) - ); - }); - - - // Update - items = items.merge(enter); - - wrap.selectAll('.preset-input-access') - .on('change', change) - .on('blur', change); - } - - - function change(d) { - var tag = {}; - var value = context.cleanTagValue(utilGetSetValue(select(this))); - - // don't override multiple values with blank string - if (!value && typeof _tags[d] !== 'string') return; - - tag[d] = value || undefined; - dispatch$1.call('change', this, tag); - } - - - access.options = function(type) { - var options = ['no', 'permissive', 'private', 'permit', 'destination']; - - if (type !== 'access') { - options.unshift('yes'); - options.push('designated'); - - if (type === 'bicycle') { - options.push('dismount'); - } - } - - return options.map(function(option) { - return { - title: field.t('options.' + option + '.description'), - value: option - }; - }); - }; - - - var placeholdersByHighway = { - footway: { - foot: 'designated', - motor_vehicle: 'no' - }, - steps: { - foot: 'yes', - motor_vehicle: 'no', - bicycle: 'no', - horse: 'no' - }, - pedestrian: { - foot: 'yes', - motor_vehicle: 'no' - }, - cycleway: { - motor_vehicle: 'no', - bicycle: 'designated' - }, - bridleway: { - motor_vehicle: 'no', - horse: 'designated' - }, - path: { - foot: 'yes', - motor_vehicle: 'no', - bicycle: 'yes', - horse: 'yes' - }, - motorway: { - foot: 'no', - motor_vehicle: 'yes', - bicycle: 'no', - horse: 'no' - }, - trunk: { - motor_vehicle: 'yes' - }, - primary: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - secondary: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - tertiary: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - residential: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - unclassified: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - service: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - motorway_link: { - foot: 'no', - motor_vehicle: 'yes', - bicycle: 'no', - horse: 'no' - }, - trunk_link: { - motor_vehicle: 'yes' - }, - primary_link: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - secondary_link: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - }, - tertiary_link: { - foot: 'yes', - motor_vehicle: 'yes', - bicycle: 'yes', - horse: 'yes' - } - }; - - - access.tags = function(tags) { - _tags = tags; - - utilGetSetValue(items.selectAll('.preset-input-access'), function(d) { - return typeof tags[d] === 'string' ? tags[d] : ''; - }) - .classed('mixed', function(d) { - return tags[d] && Array.isArray(tags[d]); - }) - .attr('title', function(d) { - return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n'); - }) - .attr('placeholder', function(d) { - if (tags[d] && Array.isArray(tags[d])) { - return _t('inspector.multiple_values'); - } - if (d === 'access') { - return 'yes'; - } - if (tags.access && typeof tags.access === 'string') { - return tags.access; - } - if (tags.highway) { - if (typeof tags.highway === 'string') { - if (placeholdersByHighway[tags.highway] && - placeholdersByHighway[tags.highway][d]) { - - return placeholdersByHighway[tags.highway][d]; - } - } else { - var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) { - return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d]; - }).filter(Boolean); - - if (impliedAccesses.length === tags.highway.length && - new Set(impliedAccesses).size === 1) { - // if all the highway values have the same implied access for this type then use that - return impliedAccesses[0]; - } - } - } - return field.placeholder(); - }); - }; - - - access.focus = function() { - items.selectAll('.preset-input-access') - .node().focus(); - }; - - - return utilRebind(access, dispatch$1, 'on'); - } - - function uiFieldAddress(field, context) { - var dispatch$1 = dispatch('change'); - var _selection = select(null); - var _wrap = select(null); - var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings - - var _entityIDs = []; - var _tags; - var _countryCode; - var _addressFormats = [{ - format: [ - ['housenumber', 'street'], - ['city', 'postcode'] - ] - }]; - - _mainFileFetcher.get('address_formats') - .then(function(d) { - _addressFormats = d; - if (!_selection.empty()) { - _selection.call(address); - } - }) - .catch(function() { /* ignore */ }); - - - function getNearStreets() { - var extent = combinedEntityExtent(); - var l = extent.center(); - var box = geoExtent(l).padByMeters(200); - - var streets = context.history().intersects(box) - .filter(isAddressable) - .map(function(d) { - var loc = context.projection([ - (extent[0][0] + extent[1][0]) / 2, - (extent[0][1] + extent[1][1]) / 2 - ]); - var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection); - - return { - title: d.tags.name, - value: d.tags.name, - dist: choice.distance - }; - }) - .sort(function(a, b) { - return a.dist - b.dist; - }); - - return utilArrayUniqBy(streets, 'value'); - - function isAddressable(d) { - return d.tags.highway && d.tags.name && d.type === 'way'; - } - } - - - function getNearCities() { - var extent = combinedEntityExtent(); - var l = extent.center(); - var box = geoExtent(l).padByMeters(200); - - var cities = context.history().intersects(box) - .filter(isAddressable) - .map(function(d) { - return { - title: d.tags['addr:city'] || d.tags.name, - value: d.tags['addr:city'] || d.tags.name, - dist: geoSphericalDistance(d.extent(context.graph()).center(), l) - }; - }) - .sort(function(a, b) { - return a.dist - b.dist; - }); - - return utilArrayUniqBy(cities, 'value'); - - - function isAddressable(d) { - if (d.tags.name) { - if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') - return true; - if (d.tags.border_type === 'city') - return true; - if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') - return true; - } - - if (d.tags['addr:city']) - return true; - - return false; - } - } - - function getNearValues(key) { - var extent = combinedEntityExtent(); - var l = extent.center(); - var box = geoExtent(l).padByMeters(200); - - var results = context.history().intersects(box) - .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; }) - .map(function(d) { - return { - title: d.tags[key], - value: d.tags[key], - dist: geoSphericalDistance(d.extent(context.graph()).center(), l) - }; - }) - .sort(function(a, b) { - return a.dist - b.dist; - }); - - return utilArrayUniqBy(results, 'value'); - } - - - function updateForCountryCode() { - - if (!_countryCode) return; - - var addressFormat; - for (var i = 0; i < _addressFormats.length; i++) { - var format = _addressFormats[i]; - if (!format.countryCodes) { - addressFormat = format; // choose the default format, keep going - } else if (format.countryCodes.indexOf(_countryCode) !== -1) { - addressFormat = format; // choose the country format, stop here - break; - } - } - - var dropdowns = addressFormat.dropdowns || [ - 'city', 'county', 'country', 'district', 'hamlet', - 'neighbourhood', 'place', 'postcode', 'province', - 'quarter', 'state', 'street', 'subdistrict', 'suburb' - ]; - - var widths = addressFormat.widths || { - housenumber: 1/3, street: 2/3, - city: 2/3, state: 1/4, postcode: 1/3 - }; - - function row(r) { - // Normalize widths. - var total = r.reduce(function(sum, key) { - return sum + (widths[key] || 0.5); - }, 0); - - return r.map(function(key) { - return { - id: key, - width: (widths[key] || 0.5) / total - }; - }); - } - - var rows = _wrap.selectAll('.addr-row') - .data(addressFormat.format, function(d) { - return d.toString(); - }); - - rows.exit() - .remove(); - - rows - .enter() - .append('div') - .attr('class', 'addr-row') - .selectAll('input') - .data(row) - .enter() - .append('input') - .property('type', 'text') - .call(updatePlaceholder) - .attr('class', function (d) { return 'addr-' + d.id; }) - .call(utilNoAuto) - .each(addDropdown) - .style('width', function (d) { return d.width * 100 + '%'; }); - - - function addDropdown(d) { - if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown - - var nearValues = (d.id === 'street') ? getNearStreets - : (d.id === 'city') ? getNearCities - : getNearValues; - - select(this) - .call(uiCombobox(context, 'address-' + d.id) - .minItems(1) - .caseSensitive(true) - .fetcher(function(value, callback) { - callback(nearValues('addr:' + d.id)); - }) - ); - } - - _wrap.selectAll('input') - .on('blur', change()) - .on('change', change()); - - _wrap.selectAll('input:not(.combobox-input)') - .on('input', change(true)); - - if (_tags) updateTags(_tags); - } - - - function address(selection) { - _selection = selection; - - _wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - _wrap = _wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(_wrap); - - var extent = combinedEntityExtent(); - - if (extent) { - var countryCode; - if (context.inIntro()) { - // localize the address format for the walkthrough - countryCode = _t('intro.graph.countrycode'); - } else { - var center = extent.center(); - countryCode = iso1A2Code(center); - } - if (countryCode) { - _countryCode = countryCode.toLowerCase(); - updateForCountryCode(); - } - } - } - - - function change(onInput) { - return function() { - var tags = {}; - - _wrap.selectAll('input') - .each(function (subfield) { - var key = field.key + ':' + subfield.id; - - var value = this.value; - if (!onInput) value = context.cleanTagValue(value); - - // don't override multiple values with blank string - if (Array.isArray(_tags[key]) && !value) return; - - tags[key] = value || undefined; - }); - - dispatch$1.call('change', this, tags, onInput); - }; - } - - function updatePlaceholder(inputSelection) { - return inputSelection.attr('placeholder', function(subfield) { - if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) { - return _t('inspector.multiple_values'); - } - if (_countryCode) { - var localkey = subfield.id + '!' + _countryCode; - var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id; - return addrField.t('placeholders.' + tkey); - } - }); - } - - - function updateTags(tags) { - utilGetSetValue(_wrap.selectAll('input'), function (subfield) { - var val = tags[field.key + ':' + subfield.id]; - return typeof val === 'string' ? val : ''; - }) - .attr('title', function(subfield) { - var val = tags[field.key + ':' + subfield.id]; - return val && Array.isArray(val) && val.filter(Boolean).join('\n'); - }) - .classed('mixed', function(subfield) { - return Array.isArray(tags[field.key + ':' + subfield.id]); - }) - .call(updatePlaceholder); - } - - - function combinedEntityExtent() { - return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); - } - - - address.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return address; - }; - - - address.tags = function(tags) { - _tags = tags; - updateTags(tags); - }; - - - address.focus = function() { - var node = _wrap.selectAll('input').node(); - if (node) node.focus(); - }; - - - return utilRebind(address, dispatch$1, 'on'); - } - - function uiFieldCycleway(field, context) { - var dispatch$1 = dispatch('change'); - var items = select(null); - var wrap = select(null); - var _tags; - - function cycleway(selection) { - - function stripcolon(s) { - return s.replace(':', ''); - } - - - wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - - var div = wrap.selectAll('ul') - .data([0]); - - div = div.enter() - .append('ul') - .attr('class', 'rows') - .merge(div); - - var keys = ['cycleway:left', 'cycleway:right']; - - items = div.selectAll('li') - .data(keys); - - var enter = items.enter() - .append('li') - .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); }); - - enter - .append('span') - .attr('class', 'label preset-label-cycleway') - .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); }) - .text(function(d) { return field.t('types.' + d); }); - - enter - .append('div') - .attr('class', 'preset-input-cycleway-wrap') - .append('input') - .attr('type', 'text') - .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); }) - .call(utilNoAuto) - .each(function(d) { - select(this) - .call(uiCombobox(context, 'cycleway-' + stripcolon(d)) - .data(cycleway.options(d)) - ); - }); - - items = items.merge(enter); - - // Update - wrap.selectAll('.preset-input-cycleway') - .on('change', change) - .on('blur', change); - } - - - function change(key) { - - var newValue = context.cleanTagValue(utilGetSetValue(select(this))); - - // don't override multiple values with blank string - if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return; - - if (newValue === 'none' || newValue === '') { newValue = undefined; } - - var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left'; - var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey]; - if (otherValue && Array.isArray(otherValue)) { - // we must always have an explicit value for comparison - otherValue = otherValue[0]; - } - if (otherValue === 'none' || otherValue === '') { otherValue = undefined; } - - var tag = {}; - - // If the left and right tags match, use the cycleway tag to tag both - // sides the same way - if (newValue === otherValue) { - tag = { - cycleway: newValue, - 'cycleway:left': undefined, - 'cycleway:right': undefined - }; - } else { - // Always set both left and right as changing one can affect the other - tag = { - cycleway: undefined - }; - tag[key] = newValue; - tag[otherKey] = otherValue; - } - - dispatch$1.call('change', this, tag); - } - - - cycleway.options = function() { - return Object.keys(field.strings.options).map(function(option) { - return { - title: field.t('options.' + option + '.description'), - value: option - }; - }); - }; - - - cycleway.tags = function(tags) { - _tags = tags; - - // If cycleway is set, use that instead of individual values - var commonValue = typeof tags.cycleway === 'string' && tags.cycleway; - - utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) { - if (commonValue) return commonValue; - return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : ''; - }) - .attr('title', function(d) { - if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) { - var vals = []; - if (Array.isArray(tags.cycleway)) { - vals = vals.concat(tags.cycleway); - } - if (Array.isArray(tags[d])) { - vals = vals.concat(tags[d]); - } - return vals.filter(Boolean).join('\n'); - } - return null; - }) - .attr('placeholder', function(d) { - if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) { - return _t('inspector.multiple_values'); - } - return field.placeholder(); - }) - .classed('mixed', function(d) { - return Array.isArray(tags.cycleway) || Array.isArray(tags[d]); - }); - }; - - - cycleway.focus = function() { - var node = wrap.selectAll('input').node(); - if (node) node.focus(); - }; - - - return utilRebind(cycleway, dispatch$1, 'on'); - } - - function uiFieldLanes(field, context) { - var dispatch$1 = dispatch('change'); - var LANE_WIDTH = 40; - var LANE_HEIGHT = 200; - var _entityIDs = []; - - function lanes(selection) { - var lanesData = context.entity(_entityIDs[0]).lanes(); - - if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { - selection.call(lanes.off); - return; - } - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - var surface = wrap.selectAll('.surface') - .data([0]); - - var d = utilGetDimensions(wrap); - var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; - - surface = surface.enter() - .append('svg') - .attr('width', d[0]) - .attr('height', 300) - .attr('class', 'surface') - .merge(surface); - - - var lanesSelection = surface.selectAll('.lanes') - .data([0]); - - lanesSelection = lanesSelection.enter() - .append('g') - .attr('class', 'lanes') - .merge(lanesSelection); - - lanesSelection - .attr('transform', function () { - return 'translate(' + (freeSpace / 2) + ', 0)'; - }); - - - var lane = lanesSelection.selectAll('.lane') - .data(lanesData.lanes); - - lane.exit() - .remove(); - - var enter = lane.enter() - .append('g') - .attr('class', 'lane'); - - enter - .append('g') - .append('rect') - .attr('y', 50) - .attr('width', LANE_WIDTH) - .attr('height', LANE_HEIGHT); - - enter - .append('g') - .attr('class', 'forward') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▲'); - - enter - .append('g') - .attr('class', 'bothways') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▲▼'); - - enter - .append('g') - .attr('class', 'backward') - .append('text') - .attr('y', 40) - .attr('x', 14) - .text('▼'); - - - lane = lane - .merge(enter); - - lane - .attr('transform', function(d) { - return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)'; - }); - - lane.select('.forward') - .style('visibility', function(d) { - return d.direction === 'forward' ? 'visible' : 'hidden'; - }); - - lane.select('.bothways') - .style('visibility', function(d) { - return d.direction === 'bothways' ? 'visible' : 'hidden'; - }); - - lane.select('.backward') - .style('visibility', function(d) { - return d.direction === 'backward' ? 'visible' : 'hidden'; - }); - } - - - lanes.entityIDs = function(val) { - _entityIDs = val; - }; - - lanes.tags = function() {}; - lanes.focus = function() {}; - lanes.off = function() {}; - - return utilRebind(lanes, dispatch$1, 'on'); - } - - uiFieldLanes.supportsMultiselection = false; - - var _languagesArray = []; - - - function uiFieldLocalized(field, context) { - var dispatch$1 = dispatch('change', 'input'); - var wikipedia = services.wikipedia; - var input = select(null); - var localizedInputs = select(null); - var _countryCode; - var _tags; - - - // A concern here in switching to async data means that _languagesArray will not - // be available the first time through, so things like the fetchers and - // the language() function will not work immediately. - _mainFileFetcher.get('languages') - .then(loadLanguagesArray) - .catch(function() { /* ignore */ }); - - var _territoryLanguages = {}; - _mainFileFetcher.get('territory_languages') - .then(function(d) { _territoryLanguages = d; }) - .catch(function() { /* ignore */ }); - - - var allSuggestions = _mainPresetIndex.collection.filter(function(p) { - return p.suggestion === true; - }); - - // reuse these combos - var langCombo = uiCombobox(context, 'localized-lang') - .fetcher(fetchLanguages) - .minItems(0); - - var brandCombo = uiCombobox(context, 'localized-brand') - .canAutocomplete(false) - .minItems(1); - - var _selection = select(null); - var _multilingual = []; - var _buttonTip = uiTooltip() - .title(_t('translate.translate')) - .placement('left'); - var _wikiTitles; - var _entityIDs = []; - - - function loadLanguagesArray(dataLanguages) { - if (_languagesArray.length !== 0) return; - - // some conversion is needed to ensure correct OSM tags are used - var replacements = { - sr: 'sr-Cyrl', // in OSM, `sr` implies Cyrillic - 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM - }; - - for (var code in dataLanguages) { - if (replacements[code] === false) continue; - var metaCode = code; - if (replacements[code]) metaCode = replacements[code]; - - _languagesArray.push({ - localName: _mainLocalizer.languageName(metaCode, { localOnly: true }), - nativeName: dataLanguages[metaCode].nativeName, - code: code, - label: _mainLocalizer.languageName(metaCode) - }); - } - } - - - function calcLocked() { - - // only lock the Name field - var isLocked = field.id === 'name' && - _entityIDs.length && - // lock the field if any feature needs it - _entityIDs.some(function(entityID) { - - var entity = context.graph().hasEntity(entityID); - if (!entity) return false; - - var original = context.graph().base().entities[_entityIDs[0]]; - var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name; - // if the name was already edited manually then allow further editing - if (!hasOriginalName) return false; - - // features linked to Wikidata are likely important and should be protected - if (entity.tags.wikidata) return true; - - // assume the name has already been confirmed if its source has been researched - if (entity.tags['name:etymology:wikidata']) return true; - - var preset = _mainPresetIndex.match(entity, context.graph()); - var isSuggestion = preset && preset.suggestion; - var showsBrand = preset && preset.originalFields.filter(function(d) { - return d.id === 'brand'; - }).length; - // protect standardized brand names - return isSuggestion && !showsBrand; - }); - - field.locked(isLocked); - } - - - // update _multilingual, maintaining the existing order - function calcMultilingual(tags) { - var existingLangsOrdered = _multilingual.map(function(item) { - return item.lang; - }); - var existingLangs = new Set(existingLangsOrdered.filter(Boolean)); - - for (var k in tags) { - var m = k.match(/^(.*):([a-zA-Z_-]+)$/); - if (m && m[1] === field.key && m[2]) { - var item = { lang: m[2], value: tags[k] }; - if (existingLangs.has(item.lang)) { - // update the value - _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value; - existingLangs.delete(item.lang); - } else { - _multilingual.push(item); - } - } - } - - _multilingual = _multilingual.filter(function(item) { - return !item.lang || !existingLangs.has(item.lang); - }); - } - - - function localized(selection) { - _selection = selection; - calcLocked(); - var isLocked = field.locked(); - var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]); - var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph()); - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - // enter/update - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - input = wrap.selectAll('.localized-main') - .data([0]); - - // enter/update - input = input.enter() - .append('input') - .attr('type', 'text') - .attr('id', field.domId) - .attr('class', 'localized-main') - .call(utilNoAuto) - .merge(input); - - if (preset && field.id === 'name') { - var pTag = preset.id.split('/', 2); - var pKey = pTag[0]; - var pValue = pTag[1]; - - if (!preset.suggestion) { - // Not a suggestion preset - Add a suggestions dropdown if it makes sense to. - // This code attempts to determine if the matched preset is the - // kind of preset that even can benefit from name suggestions.. - // - true = shops, cafes, hotels, etc. (also generic and fallback presets) - // - false = churches, parks, hospitals, etc. (things not in the index) - var isFallback = preset.isFallback(); - var goodSuggestions = allSuggestions.filter(function(s) { - if (isFallback) return true; - var sTag = s.id.split('/', 2); - var sKey = sTag[0]; - var sValue = sTag[1]; - return pKey === sKey && (!pValue || pValue === sValue); - }); - - // Show the suggestions.. If the user picks one, change the tags.. - if (allSuggestions.length && goodSuggestions.length) { - input - .on('blur.localized', checkBrandOnBlur) - .call(brandCombo - .fetcher(fetchBrandNames(preset, allSuggestions)) - .on('accept', acceptBrand) - .on('cancel', cancelBrand) - ); - } - } - } - - input - .classed('disabled', !!isLocked) - .attr('readonly', isLocked || null) - .on('input', change(true)) - .on('blur', change()) - .on('change', change()); - - - var translateButton = wrap.selectAll('.localized-add') - .data([0]); - - translateButton = translateButton.enter() - .append('button') - .attr('class', 'localized-add form-field-button') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-plus')) - .merge(translateButton); - - translateButton - .classed('disabled', !!isLocked) - .call(isLocked ? _buttonTip.destroy : _buttonTip) - .on('click', addNew); - - - if (_tags && !_multilingual.length) { - calcMultilingual(_tags); - } - - localizedInputs = selection.selectAll('.localized-multilingual') - .data([0]); - - localizedInputs = localizedInputs.enter() - .append('div') - .attr('class', 'localized-multilingual') - .merge(localizedInputs); - - localizedInputs - .call(renderMultilingual); - - localizedInputs.selectAll('button, input') - .classed('disabled', !!isLocked) - .attr('readonly', isLocked || null); - - - - // We are not guaranteed to get an `accept` or `cancel` when blurring the field. - // (This can happen if the user actives the combo, arrows down, and then clicks off to blur) - // So compare the current field value against the suggestions one last time. - function checkBrandOnBlur() { - var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]); - if (!latest) return; // deleting the entity blurred the field? - - var preset = _mainPresetIndex.match(latest, context.graph()); - if (preset && preset.suggestion) return; // already accepted - - // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe' - var name = utilGetSetValue(input).trim(); - var matched = allSuggestions.filter(function(s) { return name === s.name(); }); - - if (matched.length === 1) { - acceptBrand({ suggestion: matched[0] }); - } else { - cancelBrand(); - } - } - - - function acceptBrand(d) { - - var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]); - - if (!d || !entity) { - cancelBrand(); - return; - } - - var tags = entity.tags; - var geometry = entity.geometry(context.graph()); - var removed = preset.unsetTags(tags, geometry); - for (var k in tags) { - tags[k] = removed[k]; // set removed tags to `undefined` - } - tags = d.suggestion.setTags(tags, geometry); - utilGetSetValue(input, tags.name); - dispatch$1.call('change', this, tags); - } - - - // user hit escape, clean whatever preset name appears after the last ' – ' - function cancelBrand() { - var name = utilGetSetValue(input); - var clean = cleanName(name); - if (clean !== name) { - utilGetSetValue(input, clean); - dispatch$1.call('change', this, { name: clean }); - } - } - - // Remove whatever is after the last ' – ' - // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc) - function cleanName(name) { - var parts = name.split(' – '); - if (parts.length > 1) { - parts.pop(); - name = parts.join(' – '); - } - return name; - } - - - function fetchBrandNames(preset, suggestions) { - var pTag = preset.id.split('/', 2); - var pKey = pTag[0]; - var pValue = pTag[1]; - - return function(value, callback) { - var results = []; - if (value && value.length > 2) { - for (var i = 0; i < suggestions.length; i++) { - var s = suggestions[i]; - - // don't suggest brands from incompatible countries - if (_countryCode && s.countryCodes && - s.countryCodes.indexOf(_countryCode) === -1) continue; - - var sTag = s.id.split('/', 2); - var sKey = sTag[0]; - var sValue = sTag[1]; - var name = s.name(); - var dist = utilEditDistance(value, name.substring(0, value.length)); - var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue)); - - if (dist < 1 || (matchesPreset && dist < 3)) { - var obj = { - title: name, - value: name, - suggestion: s, - dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset - }; - results.push(obj); - } - } - results.sort(function(a, b) { return a.dist - b.dist; }); - } - results = results.slice(0, 10); - callback(results); - }; - } - - - function addNew() { - event.preventDefault(); - if (field.locked()) return; - - var defaultLang = _mainLocalizer.languageCode().toLowerCase(); - var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; }); - var isLangEn = defaultLang.indexOf('en') > -1; - if (isLangEn || langExists) { - defaultLang = ''; - langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; }); - } - - if (!langExists) { - // prepend the value so it appears at the top - _multilingual.unshift({ lang: defaultLang, value: '' }); - - localizedInputs - .call(renderMultilingual); - } - } - - - function change(onInput) { - return function() { - if (field.locked()) { - event.preventDefault(); - return; - } - - var val = utilGetSetValue(select(this)); - if (!onInput) val = context.cleanTagValue(val); - - // don't override multiple values with blank string - if (!val && Array.isArray(_tags[field.key])) return; - - var t = {}; - - t[field.key] = val || undefined; - dispatch$1.call('change', this, t, onInput); - }; - } - } - - - function key(lang) { - return field.key + ':' + lang; - } - - - function changeLang(d) { - var tags = {}; - - // make sure unrecognized suffixes are lowercase - #7156 - var lang = utilGetSetValue(select(this)).toLowerCase(); - - var language = _languagesArray.find(function(d) { - return d.label.toLowerCase() === lang || - (d.localName && d.localName.toLowerCase() === lang) || - (d.nativeName && d.nativeName.toLowerCase() === lang); - }); - if (language) lang = language.code; - - if (d.lang && d.lang !== lang) { - tags[key(d.lang)] = undefined; - } - - var newKey = lang && context.cleanTagKey(key(lang)); - - var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value')); - - if (newKey && value) { - tags[newKey] = value; - } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) { - tags[newKey] = _wikiTitles[d.lang]; - } - - d.lang = lang; - dispatch$1.call('change', this, tags); - } - - - function changeValue(d) { - if (!d.lang) return; - var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; - - // don't override multiple values with blank string - if (!value && Array.isArray(d.value)) return; - - var t = {}; - t[key(d.lang)] = value; - d.value = value; - dispatch$1.call('change', this, t); - } - - - function fetchLanguages(value, cb) { - var v = value.toLowerCase(); - - // show the user's language first - var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()]; - - if (_countryCode && _territoryLanguages[_countryCode]) { - langCodes = langCodes.concat(_territoryLanguages[_countryCode]); - } - - var langItems = []; - langCodes.forEach(function(code) { - var langItem = _languagesArray.find(function(item) { - return item.code === code; - }); - if (langItem) langItems.push(langItem); - }); - langItems = utilArrayUniq(langItems.concat(_languagesArray)); - - cb(langItems.filter(function(d) { - return d.label.toLowerCase().indexOf(v) >= 0 || - (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) || - (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) || - d.code.toLowerCase().indexOf(v) >= 0; - }).map(function(d) { - return { value: d.label }; - })); - } - - - function renderMultilingual(selection) { - var entries = selection.selectAll('div.entry') - .data(_multilingual, function(d) { return d.lang; }); - - entries.exit() - .style('top', '0') - .style('max-height', '240px') - .transition() - .duration(200) - .style('opacity', '0') - .style('max-height', '0px') - .remove(); - - var entriesEnter = entries.enter() - .append('div') - .attr('class', 'entry') - .each(function(_, index) { - var wrap = select(this); - - var domId = utilUniqueDomId(index); - - var label = wrap - .append('label') - .attr('class', 'field-label') - .attr('for', domId); - - var text = label - .append('span') - .attr('class', 'label-text'); - - text - .append('span') - .attr('class', 'label-textvalue') - .text(_t('translate.localized_translation_label')); - - text - .append('span') - .attr('class', 'label-textannotation'); - - label - .append('button') - .attr('class', 'remove-icon-multilingual') - .on('click', function(d, index) { - if (field.locked()) return; - event.preventDefault(); - - if (!d.lang || !d.value) { - _multilingual.splice(index, 1); - renderMultilingual(selection); - } else { - // remove from entity tags - var t = {}; - t[key(d.lang)] = undefined; - dispatch$1.call('change', this, t); - } - - }) - .call(svgIcon('#iD-operation-delete')); - - wrap - .append('input') - .attr('class', 'localized-lang') - .attr('id', domId) - .attr('type', 'text') - .attr('placeholder', _t('translate.localized_translation_language')) - .on('blur', changeLang) - .on('change', changeLang) - .call(langCombo); - - wrap - .append('input') - .attr('type', 'text') - .attr('class', 'localized-value') - .on('blur', changeValue) - .on('change', changeValue); - }); - - entriesEnter - .style('margin-top', '0px') - .style('max-height', '0px') - .style('opacity', '0') - .transition() - .duration(200) - .style('margin-top', '10px') - .style('max-height', '240px') - .style('opacity', '1') - .on('end', function() { - select(this) - .style('max-height', '') - .style('overflow', 'visible'); - }); - - entries = entries.merge(entriesEnter); - - entries.order(); - - entries.classed('present', function(d) { - return d.lang && d.value; - }); - - utilGetSetValue(entries.select('.localized-lang'), function(d) { - return _mainLocalizer.languageName(d.lang); - }); - - utilGetSetValue(entries.select('.localized-value'), function(d) { - return typeof d.value === 'string' ? d.value : ''; - }) - .attr('title', function(d) { - return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null; - }) - .attr('placeholder', function(d) { - return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name'); - }) - .classed('mixed', function(d) { - return Array.isArray(d.value); - }); - } - - - localized.tags = function(tags) { - _tags = tags; - - // Fetch translations from wikipedia - if (typeof tags.wikipedia === 'string' && !_wikiTitles) { - _wikiTitles = {}; - var wm = tags.wikipedia.match(/([^:]+):(.+)/); - if (wm && wm[0] && wm[1]) { - wikipedia.translations(wm[1], wm[2], function(err, d) { - if (err || !d) return; - _wikiTitles = d; - }); - } - } - - var isMixed = Array.isArray(tags[field.key]); - - utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '') - .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()) - .classed('mixed', isMixed); - - calcMultilingual(tags); - - _selection - .call(localized); - }; - - - localized.focus = function() { - input.node().focus(); - }; - - - localized.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - _multilingual = []; - loadCountryCode(); - return localized; - }; - - function loadCountryCode() { - var extent = combinedEntityExtent(); - var countryCode = extent && iso1A2Code(extent.center()); - _countryCode = countryCode && countryCode.toLowerCase(); - } - - function combinedEntityExtent() { - return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); - } - - return utilRebind(localized, dispatch$1, 'on'); - } - - function uiFieldMaxspeed(field, context) { - var dispatch$1 = dispatch('change'); - var unitInput = select(null); - var input = select(null); - var _entityIDs = []; - var _tags; - var _isImperial; - - var speedCombo = uiCombobox(context, 'maxspeed'); - var unitCombo = uiCombobox(context, 'maxspeed-unit') - .data(['km/h', 'mph'].map(comboValues)); - - var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]; - var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80]; - - - function maxspeed(selection) { - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - - input = wrap.selectAll('input.maxspeed-number') - .data([0]); - - input = input.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'maxspeed-number') - .attr('id', field.domId) - .call(utilNoAuto) - .call(speedCombo) - .merge(input); - - input - .on('change', change) - .on('blur', change); - - var loc = combinedEntityExtent().center(); - _isImperial = roadSpeedUnit(loc) === 'mph'; - - unitInput = wrap.selectAll('input.maxspeed-unit') - .data([0]); - - unitInput = unitInput.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'maxspeed-unit') - .call(unitCombo) - .merge(unitInput); - - unitInput - .on('blur', changeUnits) - .on('change', changeUnits); - - - function changeUnits() { - _isImperial = utilGetSetValue(unitInput) === 'mph'; - utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h'); - setUnitSuggestions(); - change(); - } - } - - - function setUnitSuggestions() { - speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues)); - utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h'); - } - - - function comboValues(d) { - return { - value: d.toString(), - title: d.toString() - }; - } - - - function change() { - var tag = {}; - var value = utilGetSetValue(input).trim(); - - // don't override multiple values with blank string - if (!value && Array.isArray(_tags[field.key])) return; - - if (!value) { - tag[field.key] = undefined; - } else if (isNaN(value) || !_isImperial) { - tag[field.key] = context.cleanTagValue(value); - } else { - tag[field.key] = context.cleanTagValue(value + ' mph'); - } - - dispatch$1.call('change', this, tag); - } - - - maxspeed.tags = function(tags) { - _tags = tags; - - var value = tags[field.key]; - var isMixed = Array.isArray(value); - - if (!isMixed) { - if (value && value.indexOf('mph') >= 0) { - value = parseInt(value, 10).toString(); - _isImperial = true; - } else if (value) { - _isImperial = false; - } - } - - setUnitSuggestions(); - - utilGetSetValue(input, typeof value === 'string' ? value : '') - .attr('title', isMixed ? value.filter(Boolean).join('\n') : null) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder()) - .classed('mixed', isMixed); - }; - - - maxspeed.focus = function() { - input.node().focus(); - }; - - - maxspeed.entityIDs = function(val) { - _entityIDs = val; - }; - - - function combinedEntityExtent() { - return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph()); - } - - - return utilRebind(maxspeed, dispatch$1, 'on'); - } - - function uiFieldRadio(field, context) { - var dispatch$1 = dispatch('change'); - var placeholder = select(null); - var wrap = select(null); - var labels = select(null); - var radios = select(null); - var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice(); // shallow copy - var typeField; - var layerField; - var _oldType = {}; - var _entityIDs = []; - - - function selectedKey() { - var node = wrap.selectAll('.form-field-input-radio label.active input'); - return !node.empty() && node.datum(); - } - - - function radio(selection) { - selection.classed('preset-radio', true); - - wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - var enter = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-radio'); - - enter - .append('span') - .attr('class', 'placeholder'); - - wrap = wrap - .merge(enter); - - - placeholder = wrap.selectAll('.placeholder'); - - labels = wrap.selectAll('label') - .data(radioData); - - enter = labels.enter() - .append('label'); - - enter - .append('input') - .attr('type', 'radio') - .attr('name', field.id) - .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); }) - .attr('checked', false); - - enter - .append('span') - .text(function(d) { return field.t('options.' + d, { 'default': d }); }); - - labels = labels - .merge(enter); - - radios = labels.selectAll('input') - .on('change', changeRadio); - - } - - - function structureExtras(selection, tags) { - var selected = selectedKey() || tags.layer !== undefined; - var type = _mainPresetIndex.field(selected); - var layer = _mainPresetIndex.field('layer'); - var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined); - - - var extrasWrap = selection.selectAll('.structure-extras-wrap') - .data(selected ? [0] : []); - - extrasWrap.exit() - .remove(); - - extrasWrap = extrasWrap.enter() - .append('div') - .attr('class', 'structure-extras-wrap') - .merge(extrasWrap); - - var list = extrasWrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'rows') - .merge(list); - - - // Type - if (type) { - if (!typeField || typeField.id !== selected) { - typeField = uiField(context, type, _entityIDs, { wrap: false }) - .on('change', changeType); - } - typeField.tags(tags); - } else { - typeField = null; - } - - var typeItem = list.selectAll('.structure-type-item') - .data(typeField ? [typeField] : [], function(d) { return d.id; }); - - // Exit - typeItem.exit() - .remove(); - - // Enter - var typeEnter = typeItem.enter() - .insert('li', ':first-child') - .attr('class', 'labeled-input structure-type-item'); - - typeEnter - .append('span') - .attr('class', 'label structure-label-type') - .attr('for', 'preset-input-' + selected) - .text(_t('inspector.radio.structure.type')); - - typeEnter - .append('div') - .attr('class', 'structure-input-type-wrap'); - - // Update - typeItem = typeItem - .merge(typeEnter); - - if (typeField) { - typeItem.selectAll('.structure-input-type-wrap') - .call(typeField.render); - } - - - // Layer - if (layer && showLayer) { - if (!layerField) { - layerField = uiField(context, layer, _entityIDs, { wrap: false }) - .on('change', changeLayer); - } - layerField.tags(tags); - field.keys = utilArrayUnion(field.keys, ['layer']); - } else { - layerField = null; - field.keys = field.keys.filter(function(k) { return k !== 'layer'; }); - } - - var layerItem = list.selectAll('.structure-layer-item') - .data(layerField ? [layerField] : []); - - // Exit - layerItem.exit() - .remove(); - - // Enter - var layerEnter = layerItem.enter() - .append('li') - .attr('class', 'labeled-input structure-layer-item'); - - layerEnter - .append('span') - .attr('class', 'label structure-label-layer') - .attr('for', 'preset-input-layer') - .text(_t('inspector.radio.structure.layer')); - - layerEnter - .append('div') - .attr('class', 'structure-input-layer-wrap'); - - // Update - layerItem = layerItem - .merge(layerEnter); - - if (layerField) { - layerItem.selectAll('.structure-input-layer-wrap') - .call(layerField.render); - } - } - - - function changeType(t, onInput) { - var key = selectedKey(); - if (!key) return; - - var val = t[key]; - if (val !== 'no') { - _oldType[key] = val; - } - - if (field.type === 'structureRadio') { - // remove layer if it should not be set - if (val === 'no' || - (key !== 'bridge' && key !== 'tunnel') || - (key === 'tunnel' && val === 'building_passage')) { - t.layer = undefined; - } - // add layer if it should be set - if (t.layer === undefined) { - if (key === 'bridge' && val !== 'no') { - t.layer = '1'; - } - if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') { - t.layer = '-1'; - } - } - } - - dispatch$1.call('change', this, t, onInput); - } - - - function changeLayer(t, onInput) { - if (t.layer === '0') { - t.layer = undefined; - } - dispatch$1.call('change', this, t, onInput); - } - - - function changeRadio() { - var t = {}; - var activeKey; - - if (field.key) { - t[field.key] = undefined; - } - - radios.each(function(d) { - var active = select(this).property('checked'); - if (active) activeKey = d; - - if (field.key) { - if (active) t[field.key] = d; - } else { - var val = _oldType[activeKey] || 'yes'; - t[d] = active ? val : undefined; - } - }); - - if (field.type === 'structureRadio') { - if (activeKey === 'bridge') { - t.layer = '1'; - } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') { - t.layer = '-1'; - } else { - t.layer = undefined; - } - } - - dispatch$1.call('change', this, t); - } - - - radio.tags = function(tags) { - - radios.property('checked', function(d) { - if (field.key) { - return tags[field.key] === d; - } - return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no'); - }); - - function isMixed(d) { - if (field.key) { - return Array.isArray(tags[field.key]) && tags[field.key].includes(d); - } - return Array.isArray(tags[d]); - } - - labels - .classed('active', function(d) { - if (field.key) { - return (Array.isArray(tags[field.key]) && tags[field.key].includes(d)) - || tags[field.key] === d; - } - return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no'); - }) - .classed('mixed', isMixed) - .attr('title', function(d) { - return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null; - }); - - - var selection = radios.filter(function() { return this.checked; }); - - if (selection.empty()) { - placeholder.text(_t('inspector.none')); - } else { - placeholder.text(selection.attr('value')); - _oldType[selection.datum()] = tags[selection.datum()]; - } - - if (field.type === 'structureRadio') { - // For waterways without a tunnel tag, set 'culvert' as - // the _oldType to default to if the user picks 'tunnel' - if (!!tags.waterway && !_oldType.tunnel) { - _oldType.tunnel = 'culvert'; - } - - wrap.call(structureExtras, tags); - } - }; - - - radio.focus = function() { - radios.node().focus(); - }; - - - radio.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - _oldType = {}; - return radio; - }; - - - radio.isAllowed = function() { - return _entityIDs.length === 1; - }; - - - return utilRebind(radio, dispatch$1, 'on'); - } - - function uiFieldRestrictions(field, context) { - var dispatch$1 = dispatch('change'); - var breathe = behaviorBreathe(); - - corePreferences('turn-restriction-via-way', null); // remove old key - var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922 - var storedDistance = corePreferences('turn-restriction-distance'); - - var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0; - var _maxDistance = storedDistance ? (+storedDistance) : 30; - var _initialized = false; - var _parent = select(null); // the entire field - var _container = select(null); // just the map - var _oldTurns; - var _graph; - var _vertexID; - var _intersection; - var _fromWayID; - - var _lastXPos; - - - function restrictions(selection) { - _parent = selection; - - // try to reuse the intersection, but always rebuild it if the graph has changed - if (_vertexID && (context.graph() !== _graph || !_intersection)) { - _graph = context.graph(); - _intersection = osmIntersection(_graph, _vertexID, _maxDistance); - } - - // It's possible for there to be no actual intersection here. - // for example, a vertex of two `highway=path` - // In this case, hide the field. - var isOK = ( - _intersection && - _intersection.vertices.length && // has vertices - _intersection.vertices // has the vertex that the user selected - .filter(function(vertex) { return vertex.id === _vertexID; }).length && - _intersection.ways.length > 2 && // has more than 2 ways - _intersection.ways // has more than 1 TO way - .filter(function(way) { return way.__to; }).length > 1 - ); - - // Also hide in the case where - select(selection.node().parentNode).classed('hide', !isOK); - - // if form field is hidden or has detached from dom, clean up. - if (!isOK || - !context.container().select('.inspector-wrap.inspector-hidden').empty() || - !selection.node().parentNode || - !selection.node().parentNode.parentNode) { - selection.call(restrictions.off); - return; - } - - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - var container = wrap.selectAll('.restriction-container') - .data([0]); - - // enter - var containerEnter = container.enter() - .append('div') - .attr('class', 'restriction-container'); - - containerEnter - .append('div') - .attr('class', 'restriction-help'); - - // update - _container = containerEnter - .merge(container) - .call(renderViewer); - - var controls = wrap.selectAll('.restriction-controls') - .data([0]); - - // enter/update - controls.enter() - .append('div') - .attr('class', 'restriction-controls-container') - .append('div') - .attr('class', 'restriction-controls') - .merge(controls) - .call(renderControls); - } - - - function renderControls(selection) { - var distControl = selection.selectAll('.restriction-distance') - .data([0]); - - distControl.exit() - .remove(); - - var distControlEnter = distControl.enter() - .append('div') - .attr('class', 'restriction-control restriction-distance'); - - distControlEnter - .append('span') - .attr('class', 'restriction-control-label restriction-distance-label') - .text(_t('restriction.controls.distance') + ':'); - - distControlEnter - .append('input') - .attr('class', 'restriction-distance-input') - .attr('type', 'range') - .attr('min', '20') - .attr('max', '50') - .attr('step', '5'); - - distControlEnter - .append('span') - .attr('class', 'restriction-distance-text'); - - // update - selection.selectAll('.restriction-distance-input') - .property('value', _maxDistance) - .on('input', function() { - var val = select(this).property('value'); - _maxDistance = +val; - _intersection = null; - _container.selectAll('.layer-osm .layer-turns *').remove(); - corePreferences('turn-restriction-distance', _maxDistance); - _parent.call(restrictions); - }); - - selection.selectAll('.restriction-distance-text') - .text(displayMaxDistance(_maxDistance)); - - - var viaControl = selection.selectAll('.restriction-via-way') - .data([0]); - - viaControl.exit() - .remove(); - - var viaControlEnter = viaControl.enter() - .append('div') - .attr('class', 'restriction-control restriction-via-way'); - - viaControlEnter - .append('span') - .attr('class', 'restriction-control-label restriction-via-way-label') - .text(_t('restriction.controls.via') + ':'); - - viaControlEnter - .append('input') - .attr('class', 'restriction-via-way-input') - .attr('type', 'range') - .attr('min', '0') - .attr('max', '2') - .attr('step', '1'); - - viaControlEnter - .append('span') - .attr('class', 'restriction-via-way-text'); - - // update - selection.selectAll('.restriction-via-way-input') - .property('value', _maxViaWay) - .on('input', function() { - var val = select(this).property('value'); - _maxViaWay = +val; - _container.selectAll('.layer-osm .layer-turns *').remove(); - corePreferences('turn-restriction-via-way0', _maxViaWay); - _parent.call(restrictions); - }); - - selection.selectAll('.restriction-via-way-text') - .text(displayMaxVia(_maxViaWay)); - } - - - function renderViewer(selection) { - if (!_intersection) return; - - var vgraph = _intersection.graph; - var filter = utilFunctor(true); - var projection = geoRawMercator(); - - // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect` - // Instead of asking the restriction-container for its dimensions, - // we can ask the .sidebar, which can have its dimensions cached. - // width: calc as sidebar - padding - // height: hardcoded (from `80_app.css`) - // var d = utilGetDimensions(selection); - var sdims = utilGetDimensions(context.container().select('.sidebar')); - var d = [ sdims[0] - 50, 370 ]; - var c = geoVecScale(d, 0.5); - var z = 22; - - projection.scale(geoZoomToScale(z)); - - // Calculate extent of all key vertices - var extent = geoExtent(); - for (var i = 0; i < _intersection.vertices.length; i++) { - extent._extend(_intersection.vertices[i].extent()); - } - - // If this is a large intersection, adjust zoom to fit extent - if (_intersection.vertices.length > 1) { - var padding = 180; // in z22 pixels - var tl = projection([extent[0][0], extent[1][1]]); - var br = projection([extent[1][0], extent[0][1]]); - var hFactor = (br[0] - tl[0]) / (d[0] - padding); - var vFactor = (br[1] - tl[1]) / (d[1] - padding); - var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2; - var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2; - z = z - Math.max(hZoomDiff, vZoomDiff); - projection.scale(geoZoomToScale(z)); - } - - var padTop = 35; // reserve top space for hint text - var extentCenter = projection(extent.center()); - extentCenter[1] = extentCenter[1] - padTop; - - projection - .translate(geoVecSubtract(c, extentCenter)) - .clipExtent([[0, 0], d]); - - var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d); - var drawVertices = svgVertices(projection, context); - var drawLines = svgLines(projection, context); - var drawTurns = svgTurns(projection, context); - - var firstTime = selection.selectAll('.surface').empty(); - - selection - .call(drawLayers); - - var surface = selection.selectAll('.surface') - .classed('tr', true); - - if (firstTime) { - _initialized = true; - - surface - .call(breathe); - } - - // This can happen if we've lowered the detail while a FROM way - // is selected, and that way is no longer part of the intersection. - if (_fromWayID && !vgraph.hasEntity(_fromWayID)) { - _fromWayID = null; - _oldTurns = null; - } - - surface - .call(utilSetDimensions, d) - .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z) - .call(drawLines, vgraph, _intersection.ways, filter) - .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay)); - - surface - .on('click.restrictions', click) - .on('mouseover.restrictions', mouseover); - - surface - .selectAll('.selected') - .classed('selected', false); - - surface - .selectAll('.related') - .classed('related', false); - - if (_fromWayID) { - var way = vgraph.entity(_fromWayID); - surface - .selectAll('.' + _fromWayID) - .classed('selected', true) - .classed('related', true); - } - - document.addEventListener('resizeWindow', function () { - utilSetDimensions(_container, null); - redraw(1); - }, false); - - updateHints(null); - - - function click() { - surface - .call(breathe.off) - .call(breathe); - - var datum = event.target.__data__; - var entity = datum && datum.properties && datum.properties.entity; - if (entity) { - datum = entity; - } - - if (datum instanceof osmWay && (datum.__from || datum.__via)) { - _fromWayID = datum.id; - _oldTurns = null; - redraw(); - - } else if (datum instanceof osmTurn) { - var actions, extraActions, turns, i; - var restrictionType = osmInferRestriction(vgraph, datum, projection); - - if (datum.restrictionID && !datum.direct) { - return; - - } else if (datum.restrictionID && !datum.only) { // NO -> ONLY - var seen = {}; - var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum - datumOnly.only = true; // but change this property - restrictionType = restrictionType.replace(/^no/, 'only'); - - // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA. - // We will remember them in _oldTurns, and restore them if the user clicks again. - turns = _intersection.turns(_fromWayID, 2); - extraActions = []; - _oldTurns = []; - for (i = 0; i < turns.length; i++) { - var turn = turns[i]; - if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928) - - if (turn.direct && turn.path[1] === datum.path[1]) { - seen[turns[i].restrictionID] = true; - turn.restrictionType = osmInferRestriction(vgraph, turn, projection); - _oldTurns.push(turn); - extraActions.push(actionUnrestrictTurn(turn)); - } - } - - actions = _intersection.actions.concat(extraActions, [ - actionRestrictTurn(datumOnly, restrictionType), - _t('operations.restriction.annotation.create') - ]); - - } else if (datum.restrictionID) { // ONLY -> Allowed - // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state. - // This relies on the assumption that the intersection was already split up when we - // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed. - turns = _oldTurns || []; - extraActions = []; - for (i = 0; i < turns.length; i++) { - if (turns[i].key !== datum.key) { - extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType)); - } - } - _oldTurns = null; - - actions = _intersection.actions.concat(extraActions, [ - actionUnrestrictTurn(datum), - _t('operations.restriction.annotation.delete') - ]); - - } else { // Allowed -> NO - actions = _intersection.actions.concat([ - actionRestrictTurn(datum, restrictionType), - _t('operations.restriction.annotation.create') - ]); - } - - context.perform.apply(context, actions); - - // At this point the datum will be changed, but will have same key.. - // Refresh it and update the help.. - var s = surface.selectAll('.' + datum.key); - datum = s.empty() ? null : s.datum(); - updateHints(datum); - - } else { - _fromWayID = null; - _oldTurns = null; - redraw(); - } - } - - - function mouseover() { - var datum = event.target.__data__; - updateHints(datum); - } - - _lastXPos = _lastXPos || sdims[0]; - - function redraw(minChange) { - var xPos = -1; - - if (minChange) { - xPos = utilGetDimensions(context.container().select('.sidebar'))[0]; - } - - if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) { - if (context.hasEntity(_vertexID)) { - _lastXPos = xPos; - _container.call(renderViewer); - } - } - } - - - function highlightPathsFrom(wayID) { - surface.selectAll('.related') - .classed('related', false) - .classed('allow', false) - .classed('restrict', false) - .classed('only', false); - - surface.selectAll('.' + wayID) - .classed('related', true); - - if (wayID) { - var turns = _intersection.turns(wayID, _maxViaWay); - for (var i = 0; i < turns.length; i++) { - var turn = turns[i]; - var ids = [turn.to.way]; - var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow')); - - if (turn.only || turns.length === 1) { - if (turn.via.ways) { - ids = ids.concat(turn.via.ways); - } - } else if (turn.to.way === wayID) { - continue; - } - - surface.selectAll(utilEntitySelector(ids)) - .classed('related', true) - .classed('allow', (klass === 'allow')) - .classed('restrict', (klass === 'restrict')) - .classed('only', (klass === 'only')); - } - } - } - - - function updateHints(datum) { - var help = _container.selectAll('.restriction-help').html(''); - - var placeholders = {}; - ['from', 'via', 'to'].forEach(function(k) { - placeholders[k] = '' + _t('restriction.help.' + k) + ''; - }); - - var entity = datum && datum.properties && datum.properties.entity; - if (entity) { - datum = entity; - } - - if (_fromWayID) { - way = vgraph.entity(_fromWayID); - surface - .selectAll('.' + _fromWayID) - .classed('selected', true) - .classed('related', true); - } - - // Hovering a way - if (datum instanceof osmWay && datum.__from) { - way = datum; - - highlightPathsFrom(_fromWayID ? null : way.id); - surface.selectAll('.' + way.id) - .classed('related', true); - - var clickSelect = (!_fromWayID || _fromWayID !== way.id); - help - .append('div') // "Click to select FROM {fromName}." / "FROM {fromName}" - .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), { - from: placeholders.from, - fromName: displayName(way.id, vgraph) - })); - - - // Hovering a turn arrow - } else if (datum instanceof osmTurn) { - var restrictionType = osmInferRestriction(vgraph, datum, projection); - var turnType = restrictionType.replace(/^(only|no)\_/, ''); - var indirect = (datum.direct === false ? _t('restriction.help.indirect') : ''); - var klass, turnText, nextText; - - if (datum.no) { - klass = 'restrict'; - turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect }); - nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' }); - } else if (datum.only) { - klass = 'only'; - turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect }); - nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' }); - } else { - klass = 'allow'; - turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect }); - nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' }); - } - - help - .append('div') // "NO Right Turn (indirect)" - .attr('class', 'qualifier ' + klass) - .text(turnText); - - help - .append('div') // "FROM {fromName} TO {toName}" - .html(_t('restriction.help.from_name_to_name', { - from: placeholders.from, - fromName: displayName(datum.from.way, vgraph), - to: placeholders.to, - toName: displayName(datum.to.way, vgraph) - })); - - if (datum.via.ways && datum.via.ways.length) { - var names = []; - for (var i = 0; i < datum.via.ways.length; i++) { - var prev = names[names.length - 1]; - var curr = displayName(datum.via.ways[i], vgraph); - if (!prev || curr !== prev) // collapse identical names - names.push(curr); - } - - help - .append('div') // "VIA {viaNames}" - .html(_t('restriction.help.via_names', { - via: placeholders.via, - viaNames: names.join(', ') - })); - } - - if (!indirect) { - help - .append('div') // Click for "No Right Turn" - .text(_t('restriction.help.toggle', { turn: nextText.trim() })); - } - - highlightPathsFrom(null); - var alongIDs = datum.path.slice(); - surface.selectAll(utilEntitySelector(alongIDs)) - .classed('related', true) - .classed('allow', (klass === 'allow')) - .classed('restrict', (klass === 'restrict')) - .classed('only', (klass === 'only')); - - - // Hovering empty surface - } else { - highlightPathsFrom(null); - if (_fromWayID) { - help - .append('div') // "FROM {fromName}" - .html(_t('restriction.help.from_name', { - from: placeholders.from, - fromName: displayName(_fromWayID, vgraph) - })); - - } else { - help - .append('div') // "Click to select a FROM segment." - .html(_t('restriction.help.select_from', { - from: placeholders.from - })); - } - } - } - } - - - function displayMaxDistance(maxDist) { - var isImperial = !_mainLocalizer.usesMetric(); - var opts; - - if (isImperial) { - var distToFeet = { // imprecise conversion for prettier display - 20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160 - }[maxDist]; - opts = { distance: _t('units.feet', { quantity: distToFeet }) }; - } else { - opts = { distance: _t('units.meters', { quantity: maxDist }) }; - } - - return _t('restriction.controls.distance_up_to', opts); - } - - - function displayMaxVia(maxVia) { - return maxVia === 0 ? _t('restriction.controls.via_node_only') - : maxVia === 1 ? _t('restriction.controls.via_up_to_one') - : _t('restriction.controls.via_up_to_two'); - } - - - function displayName(entityID, graph) { - var entity = graph.entity(entityID); - var name = utilDisplayName(entity) || ''; - var matched = _mainPresetIndex.match(entity, graph); - var type = (matched && matched.name()) || utilDisplayType(entity.id); - return name || type; - } - - - restrictions.entityIDs = function(val) { - _intersection = null; - _fromWayID = null; - _oldTurns = null; - _vertexID = val[0]; - }; - - - restrictions.tags = function() {}; - restrictions.focus = function() {}; - - - restrictions.off = function(selection) { - if (!_initialized) return; - - selection.selectAll('.surface') - .call(breathe.off) - .on('click.restrictions', null) - .on('mouseover.restrictions', null); - - select(window) - .on('resize.restrictions', null); - }; - - - return utilRebind(restrictions, dispatch$1, 'on'); - } - - uiFieldRestrictions.supportsMultiselection = false; - - function uiFieldTextarea(field, context) { - var dispatch$1 = dispatch('change'); - var input = select(null); - var _tags; - - - function textarea(selection) { - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - input = wrap.selectAll('textarea') - .data([0]); - - input = input.enter() - .append('textarea') - .attr('id', field.domId) - .call(utilNoAuto) - .on('input', change(true)) - .on('blur', change()) - .on('change', change()) - .merge(input); - } - - - function change(onInput) { - return function() { - - var val = utilGetSetValue(input); - if (!onInput) val = context.cleanTagValue(val); - - // don't override multiple values with blank string - if (!val && Array.isArray(_tags[field.key])) return; - - var t = {}; - t[field.key] = val || undefined; - dispatch$1.call('change', this, t, onInput); - }; - } - - - textarea.tags = function(tags) { - _tags = tags; - - var isMixed = Array.isArray(tags[field.key]); - - utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '') - .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown'))) - .classed('mixed', isMixed); - }; - - - textarea.focus = function() { - input.node().focus(); - }; - - - return utilRebind(textarea, dispatch$1, 'on'); - } - - function uiFieldWikidata(field, context) { - var wikidata = services.wikidata; - var dispatch$1 = dispatch('change'); - - var _selection = select(null); - var _searchInput = select(null); - var _qid = null; - var _wikidataEntity = null; - var _wikiURL = ''; - var _entityIDs = []; - - var _wikipediaKey = field.keys && field.keys.find(function(key) { - return key.includes('wikipedia'); - }), - _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0]; - - var combobox = uiCombobox(context, 'combo-' + field.safeid) - .caseSensitive(true) - .minItems(1); - - function wiki(selection) { - - _selection = selection; - - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); - - - var list = wrap.selectAll('ul') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'rows') - .merge(list); - - var searchRow = list.selectAll('li.wikidata-search') - .data([0]); - - var searchRowEnter = searchRow.enter() - .append('li') - .attr('class', 'wikidata-search'); - - searchRowEnter - .append('input') - .attr('type', 'text') - .attr('id', field.domId) - .style('flex', '1') - .call(utilNoAuto) - .on('focus', function() { - var node = select(this).node(); - node.setSelectionRange(0, node.value.length); - }) - .on('blur', function() { - setLabelForEntity(); - }) - .call(combobox.fetcher(fetchWikidataItems)); - - combobox.on('accept', function(d) { - _qid = d.id; - change(); - }).on('cancel', function() { - setLabelForEntity(); - }); - - searchRowEnter - .append('button') - .attr('class', 'form-field-button wiki-link') - .attr('title', _t('icons.view_on', { domain: 'wikidata.org' })) - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-out-link')) - .on('click', function() { - event.preventDefault(); - if (_wikiURL) window.open(_wikiURL, '_blank'); - }); - - searchRow = searchRow.merge(searchRowEnter); - - _searchInput = searchRow.select('input'); - - var wikidataProperties = ['description', 'identifier']; - - var items = list.selectAll('li.labeled-input') - .data(wikidataProperties); - - // Enter - var enter = items.enter() - .append('li') - .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; }); - - enter - .append('span') - .attr('class', 'label') - .text(function(d) { return _t('wikidata.' + d); }); - - enter - .append('input') - .attr('type', 'text') - .call(utilNoAuto) - .classed('disabled', 'true') - .attr('readonly', 'true'); - - enter - .append('button') - .attr('class', 'form-field-button') - .attr('title', _t('icons.copy')) - .attr('tabindex', -1) - .call(svgIcon('#iD-operation-copy')) - .on('click', function() { - event.preventDefault(); - select(this.parentNode) - .select('input') - .node() - .select(); - document.execCommand('copy'); - }); - - } - - function fetchWikidataItems(q, callback) { - - if (!q && _hintKey) { - // other tags may be good search terms - for (var i in _entityIDs) { - var entity = context.hasEntity(_entityIDs[i]); - if (entity.tags[_hintKey]) { - q = entity.tags[_hintKey]; - break; - } - } - } - - wikidata.itemsForSearchQuery(q, function(err, data) { - if (err) return; - - for (var i in data) { - data[i].value = data[i].label + ' (' + data[i].id + ')'; - data[i].title = data[i].description; - } - - if (callback) callback(data); - }); - } - - - function change() { - var syncTags = {}; - syncTags[field.key] = _qid; - dispatch$1.call('change', this, syncTags); - - // attempt asynchronous update of wikidata tag.. - var initGraph = context.graph(); - var initEntityIDs = _entityIDs; - - wikidata.entityByQID(_qid, function(err, entity) { - if (err) return; - - // If graph has changed, we can't apply this update. - if (context.graph() !== initGraph) return; - - if (!entity.sitelinks) return; - - var langs = wikidata.languagesToQuery(); - // use the label and description languages as fallbacks - ['labels', 'descriptions'].forEach(function(key) { - if (!entity[key]) return; - - var valueLangs = Object.keys(entity[key]); - if (valueLangs.length === 0) return; - var valueLang = valueLangs[0]; - - if (langs.indexOf(valueLang) === -1) { - langs.push(valueLang); - } - }); - - var newWikipediaValue; - - if (_wikipediaKey) { - var foundPreferred; - for (var i in langs) { - var lang = langs[i]; - var siteID = lang.replace('-', '_') + 'wiki'; - if (entity.sitelinks[siteID]) { - foundPreferred = true; - newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; - // use the first match - break; - } - } - - if (!foundPreferred) { - // No wikipedia sites available in the user's language or the fallback languages, - // default to any wikipedia sitelink - - var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) { - return site.endsWith('wiki'); - }); - - if (wikiSiteKeys.length === 0) { - // if no wikipedia pages are linked to this wikidata entity, delete that tag - newWikipediaValue = null; - } else { - var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-'); - var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title; - newWikipediaValue = wikiLang + ':' + wikiTitle; - } - } - } - - if (newWikipediaValue) { - newWikipediaValue = context.cleanTagValue(newWikipediaValue); - } - - if (typeof newWikipediaValue === 'undefined') return; - - var actions = initEntityIDs.map(function(entityID) { - var entity = context.hasEntity(entityID); - if (!entity) return; - - var currTags = Object.assign({}, entity.tags); // shallow copy - if (newWikipediaValue === null) { - if (!currTags[_wikipediaKey]) return; - - delete currTags[_wikipediaKey]; - } else { - currTags[_wikipediaKey] = newWikipediaValue; - } - - return actionChangeTags(entityID, currTags); - }).filter(Boolean); - - if (!actions.length) return; - - // Coalesce the update of wikidata tag into the previous tag change - context.overwrite( - function actionUpdateWikipediaTags(graph) { - actions.forEach(function(action) { - graph = action(graph); - }); - return graph; - }, - context.history().undoAnnotation() - ); - - // do not dispatch.call('change') here, because entity_editor - // changeTags() is not intended to be called asynchronously - }); - } - - function setLabelForEntity() { - var label = ''; - if (_wikidataEntity) { - label = entityPropertyForDisplay(_wikidataEntity, 'labels'); - if (label.length === 0) { - label = _wikidataEntity.id.toString(); - } - } - utilGetSetValue(_searchInput, label); - } - - - wiki.tags = function(tags) { - - var isMixed = Array.isArray(tags[field.key]); - _searchInput - .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null) - .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '') - .classed('mixed', isMixed); - - _qid = typeof tags[field.key] === 'string' && tags[field.key] || ''; - - if (!/^Q[0-9]*$/.test(_qid)) { // not a proper QID - unrecognized(); - return; - } - - // QID value in correct format - _wikiURL = 'https://wikidata.org/wiki/' + _qid; - wikidata.entityByQID(_qid, function(err, entity) { - if (err) { - unrecognized(); - return; - } - _wikidataEntity = entity; - - setLabelForEntity(); - - var description = entityPropertyForDisplay(entity, 'descriptions'); - - _selection.select('button.wiki-link') - .classed('disabled', false); - - _selection.select('.preset-wikidata-description') - .style('display', function(){ - return description.length > 0 ? 'flex' : 'none'; - }) - .select('input') - .attr('value', description); - - _selection.select('.preset-wikidata-identifier') - .style('display', function(){ - return entity.id ? 'flex' : 'none'; - }) - .select('input') - .attr('value', entity.id); - }); - - - // not a proper QID - function unrecognized() { - _wikidataEntity = null; - setLabelForEntity(); - - _selection.select('.preset-wikidata-description') - .style('display', 'none'); - _selection.select('.preset-wikidata-identifier') - .style('display', 'none'); - - _selection.select('button.wiki-link') - .classed('disabled', true); - - if (_qid && _qid !== '') { - _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid; - } else { - _wikiURL = ''; - } - } - }; - - function entityPropertyForDisplay(wikidataEntity, propKey) { - if (!wikidataEntity[propKey]) return ''; - var propObj = wikidataEntity[propKey]; - var langKeys = Object.keys(propObj); - if (langKeys.length === 0) return ''; - // sorted by priority, since we want to show the user's language first if possible - var langs = wikidata.languagesToQuery(); - for (var i in langs) { - var lang = langs[i]; - var valueObj = propObj[lang]; - if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value; - } - // default to any available value - return propObj[langKeys[0]].value; - } - - - wiki.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return wiki; - }; - - - wiki.focus = function() { - _searchInput.node().focus(); - }; - - - return utilRebind(wiki, dispatch$1, 'on'); - } - - function uiFieldWikipedia(field, context) { - const dispatch$1 = dispatch('change'); - const wikipedia = services.wikipedia; - const wikidata = services.wikidata; - let _langInput = select(null); - let _titleInput = select(null); - let _wikiURL = ''; - let _entityIDs; - let _tags; - - let _dataWikipedia = []; - _mainFileFetcher.get('wmf_sitematrix') - .then(d => { - _dataWikipedia = d; - if (_tags) updateForTags(_tags); - }) - .catch(() => { /* ignore */ }); - - - const langCombo = uiCombobox(context, 'wikipedia-lang') - .fetcher((value, callback) => { - const v = value.toLowerCase(); - callback(_dataWikipedia - .filter(d => { - return d[0].toLowerCase().indexOf(v) >= 0 || - d[1].toLowerCase().indexOf(v) >= 0 || - d[2].toLowerCase().indexOf(v) >= 0; - }) - .map(d => ({ value: d[1] })) - ); - }); - - const titleCombo = uiCombobox(context, 'wikipedia-title') - .fetcher((value, callback) => { - if (!value) { - value = ''; - for (let i in _entityIDs) { - let entity = context.hasEntity(_entityIDs[i]); - if (entity.tags.name) { - value = entity.tags.name; - break; - } - } - } - const searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions; - searchfn(language()[2], value, (query, data) => { - callback( data.map(d => ({ value: d })) ); - }); - }); - - - function wiki(selection) { - let wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); - - wrap = wrap.enter() - .append('div') - .attr('class', `form-field-input-wrap form-field-input-${field.type}`) - .merge(wrap); - - - let langContainer = wrap.selectAll('.wiki-lang-container') - .data([0]); - - langContainer = langContainer.enter() - .append('div') - .attr('class', 'wiki-lang-container') - .merge(langContainer); - - - _langInput = langContainer.selectAll('input.wiki-lang') - .data([0]); - - _langInput = _langInput.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'wiki-lang') - .attr('placeholder', _t('translate.localized_translation_language')) - .call(utilNoAuto) - .call(langCombo) - .merge(_langInput); - - utilGetSetValue(_langInput, language()[1]); - - _langInput - .on('blur', changeLang) - .on('change', changeLang); - - - let titleContainer = wrap.selectAll('.wiki-title-container') - .data([0]); - - titleContainer = titleContainer.enter() - .append('div') - .attr('class', 'wiki-title-container') - .merge(titleContainer); - - _titleInput = titleContainer.selectAll('input.wiki-title') - .data([0]); - - _titleInput = _titleInput.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'wiki-title') - .attr('id', field.domId) - .call(utilNoAuto) - .call(titleCombo) - .merge(_titleInput); - - _titleInput - .on('blur', blur) - .on('change', change); - - - let link = titleContainer.selectAll('.wiki-link') - .data([0]); - - link = link.enter() - .append('button') - .attr('class', 'form-field-button wiki-link') - .attr('tabindex', -1) - .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' })) - .call(svgIcon('#iD-icon-out-link')) - .merge(link); - - link - .on('click', () => { - event.preventDefault(); - if (_wikiURL) window.open(_wikiURL, '_blank'); - }); - } - - - function language() { - const value = utilGetSetValue(_langInput).toLowerCase(); - const locale = _mainLocalizer.localeCode().toLowerCase(); - let localeLanguage; - return _dataWikipedia.find(d => { - if (d[2] === locale) localeLanguage = d; - return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value; - }) || localeLanguage || ['English', 'English', 'en']; - } - - - function changeLang() { - utilGetSetValue(_langInput, language()[1]); - change(true); - } - - - function blur() { - change(true); - } - - - function change(skipWikidata) { - let value = utilGetSetValue(_titleInput); - const m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/); - const l = m && _dataWikipedia.find(d => m[1] === d[2]); - let syncTags = {}; - - if (l) { - // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization - value = decodeURIComponent(m[2]).replace(/_/g, ' '); - if (m[3]) { - let anchor; - // try { - // leave this out for now - #6232 - // Best-effort `anchordecode:` implementation - // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1')); - // } catch (e) { - anchor = decodeURIComponent(m[3]); - // } - value += '#' + anchor.replace(/_/g, ' '); - } - value = value.slice(0, 1).toUpperCase() + value.slice(1); - utilGetSetValue(_langInput, l[1]); - utilGetSetValue(_titleInput, value); - } - - if (value) { - syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value); - } else { - syncTags.wikipedia = undefined; - } - - dispatch$1.call('change', this, syncTags); - - - if (skipWikidata || !value || !language()[2]) return; - - // attempt asynchronous update of wikidata tag.. - const initGraph = context.graph(); - const initEntityIDs = _entityIDs; - - wikidata.itemsByTitle(language()[2], value, (err, data) => { - if (err || !data || !Object.keys(data).length) return; - - // If graph has changed, we can't apply this update. - if (context.graph() !== initGraph) return; - - const qids = Object.keys(data); - const value = qids && qids.find(id => id.match(/^Q\d+$/)); - - let actions = initEntityIDs.map((entityID) => { - let entity = context.entity(entityID).tags; - let currTags = Object.assign({}, entity); // shallow copy - if (currTags.wikidata !== value) { - currTags.wikidata = value; - return actionChangeTags(entityID, currTags); - } - }).filter(Boolean); - - if (!actions.length) return; - - // Coalesce the update of wikidata tag into the previous tag change - context.overwrite( - function actionUpdateWikidataTags(graph) { - actions.forEach(function(action) { - graph = action(graph); - }); - return graph; - }, - context.history().undoAnnotation() - ); - - // do not dispatch.call('change') here, because entity_editor - // changeTags() is not intended to be called asynchronously - }); - } - - - wiki.tags = (tags) => { - _tags = tags; - updateForTags(tags); - }; - - function updateForTags(tags) { - - const value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; - const m = value.match(/([^:]+):([^#]+)(?:#(.+))?/); - const l = m && _dataWikipedia.find(d => m[1] === d[2]); - let anchor = m && m[3]; - - // value in correct format - if (l) { - utilGetSetValue(_langInput, l[1]); - utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : '')); - if (anchor) { - try { - // Best-effort `anchorencode:` implementation - anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.'); - } catch (e) { - anchor = anchor.replace(/ /g, '_'); - } - } - _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' + - m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''); - - // unrecognized value format - } else { - utilGetSetValue(_titleInput, value); - if (value && value !== '') { - utilGetSetValue(_langInput, ''); - _wikiURL = `https://en.wikipedia.org/wiki/Special:Search?search=${value}`; - } else { - _wikiURL = ''; - } - } - } - - - wiki.entityIDs = (val) => { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return wiki; - }; - - - wiki.focus = () => { - _titleInput.node().focus(); - }; - - - return utilRebind(wiki, dispatch$1, 'on'); - } - - uiFieldWikipedia.supportsMultiselection = false; - - var uiFields = { - access: uiFieldAccess, - address: uiFieldAddress, - check: uiFieldCheck, - combo: uiFieldCombo, - cycleway: uiFieldCycleway, - defaultCheck: uiFieldCheck, - email: uiFieldText, - identifier: uiFieldText, - lanes: uiFieldLanes, - localized: uiFieldLocalized, - maxspeed: uiFieldMaxspeed, - multiCombo: uiFieldCombo, - networkCombo: uiFieldCombo, - number: uiFieldText, - onewayCheck: uiFieldCheck, - radio: uiFieldRadio, - restrictions: uiFieldRestrictions, - semiCombo: uiFieldCombo, - structureRadio: uiFieldRadio, - tel: uiFieldText, - text: uiFieldText, - textarea: uiFieldTextarea, - typeCombo: uiFieldCombo, - url: uiFieldText, - wikidata: uiFieldWikidata, - wikipedia: uiFieldWikipedia - }; - - function uiField(context, presetField, entityIDs, options) { - options = Object.assign({ - show: true, - wrap: true, - remove: true, - revert: true, - info: true - }, options); - - var dispatch$1 = dispatch('change', 'revert'); - var field = Object.assign({}, presetField); // shallow copy - field.domId = utilUniqueDomId('form-field-' + field.safeid); - var _show = options.show; - var _state = ''; - var _tags = {}; - - var _locked = false; - var _lockedTip = uiTooltip() - .title(_t('inspector.lock.suggestion', { label: field.label })) - .placement('bottom'); - - - field.keys = field.keys || [field.key]; - - // only create the fields that are actually being shown - if (_show && !field.impl) { - createField(); - } - - // Creates the field.. This is done lazily, - // once we know that the field will be shown. - function createField() { - field.impl = uiFields[field.type](field, context) - .on('change', function(t, onInput) { - dispatch$1.call('change', field, t, onInput); - }); - - if (entityIDs) { - field.entityIDs = entityIDs; - // if this field cares about the entities, pass them along - if (field.impl.entityIDs) { - field.impl.entityIDs(entityIDs); - } - } - } - - - function isModified() { - if (!entityIDs || !entityIDs.length) return false; - return entityIDs.some(function(entityID) { - var original = context.graph().base().entities[entityID]; - var latest = context.graph().entity(entityID); - return field.keys.some(function(key) { - return original ? latest.tags[key] !== original.tags[key] : latest.tags[key]; - }); - }); - } - - - function tagsContainFieldKey() { - return field.keys.some(function(key) { - if (field.type === 'multiCombo') { - for (var tagKey in _tags) { - if (tagKey.indexOf(key) === 0) { - return true; - } - } - return false; - } - return _tags[key] !== undefined; - }); - } - - - function revert(d) { - event.stopPropagation(); - event.preventDefault(); - if (!entityIDs || _locked) return; - - dispatch$1.call('revert', d, d.keys); - } - - - function remove(d) { - event.stopPropagation(); - event.preventDefault(); - if (_locked) return; - - var t = {}; - d.keys.forEach(function(key) { - t[key] = undefined; - }); - - dispatch$1.call('change', d, t); - } - - - field.render = function(selection) { - var container = selection.selectAll('.form-field') - .data([field]); - - // Enter - var enter = container.enter() - .append('div') - .attr('class', function(d) { return 'form-field form-field-' + d.safeid; }) - .classed('nowrap', !options.wrap); - - if (options.wrap) { - var labelEnter = enter - .append('label') - .attr('class', 'field-label') - .attr('for', function(d) { return d.domId; }); - - var textEnter = labelEnter - .append('span') - .attr('class', 'label-text'); - - textEnter - .append('span') - .attr('class', 'label-textvalue') - .text(function(d) { return d.label(); }); - - textEnter - .append('span') - .attr('class', 'label-textannotation'); - - if (options.remove) { - labelEnter - .append('button') - .attr('class', 'remove-icon') - .attr('title', _t('icons.remove')) - .attr('tabindex', -1) - .call(svgIcon('#iD-operation-delete')); - } - - if (options.revert) { - labelEnter - .append('button') - .attr('class', 'modified-icon') - .attr('title', _t('icons.undo')) - .attr('tabindex', -1) - .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo')); - } - } - - - // Update - container = container - .merge(enter); - - container.select('.field-label > .remove-icon') // propagate bound data - .on('click', remove); - - container.select('.field-label > .modified-icon') // propagate bound data - .on('click', revert); - - container - .each(function(d) { - var selection = select(this); - - if (!d.impl) { - createField(); - } - - var reference, help; - - // instantiate field help - if (options.wrap && field.type === 'restrictions') { - help = uiFieldHelp(context, 'restrictions'); - } - - // instantiate tag reference - if (options.wrap && options.info) { - var referenceKey = d.key; - if (d.type === 'multiCombo') { // lookup key without the trailing ':' - referenceKey = referenceKey.replace(/:$/, ''); - } - - reference = uiTagReference(d.reference || { key: referenceKey }); - if (_state === 'hover') { - reference.showing(false); - } - } - - selection - .call(d.impl); - - // add field help components - if (help) { - selection - .call(help.body) - .select('.field-label') - .call(help.button); - } - - // add tag reference components - if (reference) { - selection - .call(reference.body) - .select('.field-label') - .call(reference.button); - } - - d.impl.tags(_tags); - }); - - - container - .classed('locked', _locked) - .classed('modified', isModified()) - .classed('present', tagsContainFieldKey()); - - - // show a tip and lock icon if the field is locked - var annotation = container.selectAll('.field-label .label-textannotation'); - var icon = annotation.selectAll('.icon') - .data(_locked ? [0]: []); - - icon.exit() - .remove(); - - icon.enter() - .append('svg') - .attr('class', 'icon') - .append('use') - .attr('xlink:href', '#fas-lock'); - - container.call(_locked ? _lockedTip : _lockedTip.destroy); - }; - - - field.state = function(val) { - if (!arguments.length) return _state; - _state = val; - return field; - }; - - - field.tags = function(val) { - if (!arguments.length) return _tags; - _tags = val; - - if (tagsContainFieldKey() && !_show) { - // always show a field if it has a value to display - _show = true; - if (!field.impl) { - createField(); - } - } - - return field; - }; - - - field.locked = function(val) { - if (!arguments.length) return _locked; - _locked = val; - return field; - }; - - - field.show = function() { - _show = true; - if (!field.impl) { - createField(); - } - if (field.default && field.key && _tags[field.key] !== field.default) { - var t = {}; - t[field.key] = field.default; - dispatch$1.call('change', this, t); - } - }; - - // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown - field.isShown = function() { - return _show; - }; - - - // An allowed field can appear in the UI or in the 'Add field' dropdown. - // A non-allowed field is hidden from the user altogether - field.isAllowed = function() { - - if (entityIDs && - entityIDs.length > 1 && - uiFields[field.type].supportsMultiselection === false) return false; - - if (field.geometry && !entityIDs.every(function(entityID) { - return field.matchGeometry(context.graph().geometry(entityID)); - })) return false; - - if (field.countryCodes || field.notCountryCodes) { - var extent = combinedEntityExtent(); - if (!extent) return true; - - var center = extent.center(); - var countryCode = iso1A2Code(center); - - if (!countryCode) return false; - - countryCode = countryCode.toLowerCase(); - - if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) { - return false; - } - if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) { - return false; - } - } - - var prerequisiteTag = field.prerequisiteTag; - - if (entityIDs && - !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present - prerequisiteTag) { - - if (!entityIDs.every(function(entityID) { - var entity = context.graph().entity(entityID); - if (prerequisiteTag.key) { - var value = entity.tags[prerequisiteTag.key]; - if (!value) return false; - - if (prerequisiteTag.valueNot) { - return prerequisiteTag.valueNot !== value; - } - if (prerequisiteTag.value) { - return prerequisiteTag.value === value; - } - } else if (prerequisiteTag.keyNot) { - if (entity.tags[prerequisiteTag.keyNot]) return false; - } - return true; - })) return false; - } - - return true; - }; - - - field.focus = function() { - if (field.impl) { - field.impl.focus(); - } - }; - - - function combinedEntityExtent() { - return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) { - var entity = context.graph().entity(entityID); - return extent.extend(entity.extent(context.graph())); - }, geoExtent()); - } - - - return utilRebind(field, dispatch$1, 'on'); - } - - function uiFormFields(context) { - var moreCombo = uiCombobox(context, 'more-fields').minItems(1); - var _fieldsArr = []; - var _lastPlaceholder = ''; - var _state = ''; - var _klass = ''; - - - function formFields(selection) { - var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); }); - var shown = allowedFields.filter(function(field) { return field.isShown(); }); - var notShown = allowedFields.filter(function(field) { return !field.isShown(); }); - - var container = selection.selectAll('.form-fields-container') - .data([0]); - - container = container.enter() - .append('div') - .attr('class', 'form-fields-container ' + (_klass || '')) - .merge(container); - - - var fields = container.selectAll('.wrap-form-field') - .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); }); - - fields.exit() - .remove(); - - // Enter - var enter = fields.enter() - .append('div') - .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; }); - - // Update - fields = fields - .merge(enter); - - fields - .order() - .each(function(d) { - select(this) - .call(d.render); - }); - - - var titles = []; - var moreFields = notShown.map(function(field) { - var label = field.label(); - titles.push(label); - - var terms = field.terms(); - if (field.key) terms.push(field.key); - if (field.keys) terms = terms.concat(field.keys); - - return { - title: label, - value: label, - field: field, - terms: terms - }; - }); - - var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : ''); - - - var more = selection.selectAll('.more-fields') - .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]); - - more.exit() - .remove(); - - var moreEnter = more.enter() - .append('div') - .attr('class', 'more-fields') - .append('label'); - - moreEnter - .append('span') - .text(_t('inspector.add_fields')); - - more = moreEnter - .merge(more); - - - var input = more.selectAll('.value') - .data([0]); - - input.exit() - .remove(); - - input = input.enter() - .append('input') - .attr('class', 'value') - .attr('type', 'text') - .attr('placeholder', placeholder) - .call(utilNoAuto) - .merge(input); - - input - .call(utilGetSetValue, '') - .call(moreCombo - .data(moreFields) - .on('accept', function (d) { - if (!d) return; // user entered something that was not matched - var field = d.field; - field.show(); - selection.call(formFields); // rerender - if (field.type !== 'semiCombo' && field.type !== 'multiCombo') { - field.focus(); - } - }) - ); - - // avoid updating placeholder excessively (triggers style recalc) - if (_lastPlaceholder !== placeholder) { - input.attr('placeholder', placeholder); - _lastPlaceholder = placeholder; - } - } - - - formFields.fieldsArr = function(val) { - if (!arguments.length) return _fieldsArr; - _fieldsArr = val || []; - return formFields; - }; - - formFields.state = function(val) { - if (!arguments.length) return _state; - _state = val; - return formFields; - }; - - formFields.klass = function(val) { - if (!arguments.length) return _klass; - _klass = val; - return formFields; - }; - - - return formFields; - } - - function uiSectionPresetFields(context) { - - var section = uiSection('preset-fields', context) - .title(function() { - return _t('inspector.fields'); - }) - .disclosureContent(renderDisclosureContent); - - var dispatch$1 = dispatch('change', 'revert'); - var formFields = uiFormFields(context); - var _state; - var _fieldsArr; - var _presets = []; - var _tags; - var _entityIDs; - - function renderDisclosureContent(selection) { - if (!_fieldsArr) { - - var graph = context.graph(); - - var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) { - return geoms[graph.entity(entityID).geometry(graph)] = true; - }, {})); - - var presetsManager = _mainPresetIndex; - - var allFields = []; - var allMoreFields = []; - var sharedTotalFields; - - _presets.forEach(function(preset) { - var fields = preset.fields(); - var moreFields = preset.moreFields(); - - allFields = utilArrayUnion(allFields, fields); - allMoreFields = utilArrayUnion(allMoreFields, moreFields); - - if (!sharedTotalFields) { - sharedTotalFields = utilArrayUnion(fields, moreFields); - } else { - sharedTotalFields = sharedTotalFields.filter(function(field) { - return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1; - }); - } - }); - - var sharedFields = allFields.filter(function(field) { - return sharedTotalFields.indexOf(field) !== -1; - }); - var sharedMoreFields = allMoreFields.filter(function(field) { - return sharedTotalFields.indexOf(field) !== -1; - }); - - _fieldsArr = []; - - sharedFields.forEach(function(field) { - if (field.matchAllGeometry(geometries)) { - _fieldsArr.push( - uiField(context, field, _entityIDs) - ); - } - }); - - var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]); - if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) { - _fieldsArr.push( - uiField(context, presetsManager.field('restrictions'), _entityIDs) - ); - } - - var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal()); - additionalFields.sort(function(field1, field2) { - return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode()); - }); - - additionalFields.forEach(function(field) { - if (sharedFields.indexOf(field) === -1 && - field.matchAllGeometry(geometries)) { - _fieldsArr.push( - uiField(context, field, _entityIDs, { show: false }) - ); - } - }); - - _fieldsArr.forEach(function(field) { - field - .on('change', function(t, onInput) { - dispatch$1.call('change', field, _entityIDs, t, onInput); - }) - .on('revert', function(keys) { - dispatch$1.call('revert', field, keys); - }); - }); - } - - _fieldsArr.forEach(function(field) { - field - .state(_state) - .tags(_tags); - }); - - - selection - .call(formFields - .fieldsArr(_fieldsArr) - .state(_state) - .klass('grouped-items-area') - ); - - - selection.selectAll('.wrap-form-field input') - .on('keydown', function() { - // if user presses enter, and combobox is not active, accept edits.. - if (event.keyCode === 13 && context.container().select('.combobox').empty()) { - context.enter(modeBrowse(context)); - } - }); - } - - section.presets = function(val) { - if (!arguments.length) return _presets; - if (!_presets || !val || !utilArrayIdentical(_presets, val)) { - _presets = val; - _fieldsArr = null; - } - return section; - }; - - section.state = function(val) { - if (!arguments.length) return _state; - _state = val; - return section; - }; - - section.tags = function(val) { - if (!arguments.length) return _tags; - _tags = val; - // Don't reset _fieldsArr here. - return section; - }; - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) { - _entityIDs = val; - _fieldsArr = null; - } - return section; - }; - - return utilRebind(section, dispatch$1, 'on'); - } - - function uiSectionRawMemberEditor(context) { - - var section = uiSection('raw-member-editor', context) - .shouldDisplay(function() { - if (!_entityIDs || _entityIDs.length !== 1) return false; - - var entity = context.hasEntity(_entityIDs[0]); - return entity && entity.type === 'relation'; - }) - .title(function() { - var entity = context.hasEntity(_entityIDs[0]); - if (!entity) return ''; - - var gt = entity.members.length > _maxMembers ? '>' : ''; - var count = gt + entity.members.slice(0, _maxMembers).length; - return _t('inspector.title_count', { title: _t('inspector.members'), count: count }); - }) - .disclosureContent(renderDisclosureContent); - - var taginfo = services.taginfo; - var _entityIDs; - var _maxMembers = 1000; - - function downloadMember(d) { - event.preventDefault(); - - // display the loading indicator - select(this.parentNode).classed('tag-reference-loading', true); - context.loadEntity(d.id, function() { - section.reRender(); - }); - } - - function zoomToMember(d) { - event.preventDefault(); - - var entity = context.entity(d.id); - context.map().zoomToEase(entity); - - // highlight the feature in case it wasn't previously on-screen - utilHighlightEntities([d.id], true, context); - } - - - function selectMember(d) { - event.preventDefault(); - - // remove the hover-highlight styling - utilHighlightEntities([d.id], false, context); - - var entity = context.entity(d.id); - var mapExtent = context.map().extent(); - if (!entity.intersects(mapExtent, context.graph())) { - // zoom to the entity if its extent is not visible now - context.map().zoomToEase(entity); - } - - context.enter(modeSelect(context, [d.id])); - } - - - function changeRole(d) { - var oldRole = d.role; - var newRole = context.cleanRelationRole(select(this).property('value')); - - if (oldRole !== newRole) { - var member = { id: d.id, type: d.type, role: newRole }; - context.perform( - actionChangeMember(d.relation.id, member, d.index), - _t('operations.change_role.annotation') - ); - } - } - - - function deleteMember(d) { - - // remove the hover-highlight styling - utilHighlightEntities([d.id], false, context); - - context.perform( - actionDeleteMember(d.relation.id, d.index), - _t('operations.delete_member.annotation') - ); - - if (!context.hasEntity(d.relation.id)) { - context.enter(modeBrowse(context)); - } - } - - function renderDisclosureContent(selection) { - - var entityID = _entityIDs[0]; - - var memberships = []; - var entity = context.entity(entityID); - entity.members.slice(0, _maxMembers).forEach(function(member, index) { - memberships.push({ - index: index, - id: member.id, - type: member.type, - role: member.role, - relation: entity, - member: context.hasEntity(member.id), - domId: utilUniqueDomId(entityID + '-member-' + index) - }); - }); - - var list = selection.selectAll('.member-list') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'member-list') - .merge(list); - - - var items = list.selectAll('li') - .data(memberships, function(d) { - return osmEntity.key(d.relation) + ',' + d.index + ',' + - (d.member ? osmEntity.key(d.member) : 'incomplete'); - }); - - items.exit() - .each(unbind) - .remove(); - - var itemsEnter = items.enter() - .append('li') - .attr('class', 'member-row form-field') - .classed('member-incomplete', function(d) { return !d.member; }); - - itemsEnter - .each(function(d) { - var item = select(this); - - var label = item - .append('label') - .attr('class', 'field-label') - .attr('for', d.domId); - - if (d.member) { - // highlight the member feature in the map while hovering on the list item - item - .on('mouseover', function() { - utilHighlightEntities([d.id], true, context); - }) - .on('mouseout', function() { - utilHighlightEntities([d.id], false, context); - }); - - var labelLink = label - .append('span') - .attr('class', 'label-text') - .append('a') - .attr('href', '#') - .on('click', selectMember); - - labelLink - .append('span') - .attr('class', 'member-entity-type') - .text(function(d) { - var matched = _mainPresetIndex.match(d.member, context.graph()); - return (matched && matched.name()) || utilDisplayType(d.member.id); - }); - - labelLink - .append('span') - .attr('class', 'member-entity-name') - .text(function(d) { return utilDisplayName(d.member); }); - - label - .append('button') - .attr('tabindex', -1) - .attr('title', _t('icons.remove')) - .attr('class', 'remove member-delete') - .call(svgIcon('#iD-operation-delete')); - - label - .append('button') - .attr('class', 'member-zoom') - .attr('title', _t('icons.zoom_to')) - .call(svgIcon('#iD-icon-framed-dot', 'monochrome')) - .on('click', zoomToMember); - - } else { - var labelText = label - .append('span') - .attr('class', 'label-text'); - - labelText - .append('span') - .attr('class', 'member-entity-type') - .text(_t('inspector.' + d.type, { id: d.id })); - - labelText - .append('span') - .attr('class', 'member-entity-name') - .text(_t('inspector.incomplete', { id: d.id })); - - label - .append('button') - .attr('class', 'member-download') - .attr('title', _t('icons.download')) - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-load')) - .on('click', downloadMember); - } - }); - - var wrapEnter = itemsEnter - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-member'); - - wrapEnter - .append('input') - .attr('class', 'member-role') - .attr('id', function(d) { - return d.domId; - }) - .property('type', 'text') - .attr('placeholder', _t('inspector.role')) - .call(utilNoAuto); - - if (taginfo) { - wrapEnter.each(bindTypeahead); - } - - // update - items = items - .merge(itemsEnter) - .order(); - - items.select('input.member-role') - .property('value', function(d) { return d.role; }) - .on('blur', changeRole) - .on('change', changeRole); - - items.select('button.member-delete') - .on('click', deleteMember); - - var dragOrigin, targetIndex; - - items.call(d3_drag() - .on('start', function() { - dragOrigin = { - x: event.x, - y: event.y - }; - targetIndex = null; - }) - .on('drag', function(d, index) { - var x = event.x - dragOrigin.x, - y = event.y - dragOrigin.y; - - if (!select(this).classed('dragging') && - // don't display drag until dragging beyond a distance threshold - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; - - select(this) - .classed('dragging', true); - - targetIndex = null; - - selection.selectAll('li.member-row') - .style('transform', function(d2, index2) { - var node = select(this).node(); - if (index === index2) { - return 'translate(' + x + 'px, ' + y + 'px)'; - } else if (index2 > index && event.y > node.offsetTop) { - if (targetIndex === null || index2 > targetIndex) { - targetIndex = index2; - } - return 'translateY(-100%)'; - } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) { - if (targetIndex === null || index2 < targetIndex) { - targetIndex = index2; - } - return 'translateY(100%)'; - } - return null; - }); - }) - .on('end', function(d, index) { - - if (!select(this).classed('dragging')) { - return; - } - - select(this) - .classed('dragging', false); - - selection.selectAll('li.member-row') - .style('transform', null); - - if (targetIndex !== null) { - // dragged to a new position, reorder - context.perform( - actionMoveMember(d.relation.id, index, targetIndex), - _t('operations.reorder_members.annotation') - ); - } - }) - ); - - - - function bindTypeahead(d) { - var row = select(this); - var role = row.selectAll('input.member-role'); - var origValue = role.property('value'); - - function sort(value, data) { - var sameletter = []; - var other = []; - for (var i = 0; i < data.length; i++) { - if (data[i].value.substring(0, value.length) === value) { - sameletter.push(data[i]); - } else { - other.push(data[i]); - } - } - return sameletter.concat(other); - } - - role.call(uiCombobox(context, 'member-role') - .fetcher(function(role, callback) { - // The `geometry` param is used in the `taginfo.js` interface for - // filtering results, as a key into the `tag_members_fractions` - // object. If we don't know the geometry because the member is - // not yet downloaded, it's ok to guess based on type. - var geometry; - if (d.member) { - geometry = context.graph().geometry(d.member.id); - } else if (d.type === 'relation') { - geometry = 'relation'; - } else if (d.type === 'way') { - geometry = 'line'; - } else { - geometry = 'point'; - } - - var rtype = entity.tags.type; - taginfo.roles({ - debounce: true, - rtype: rtype || '', - geometry: geometry, - query: role - }, function(err, data) { - if (!err) callback(sort(role, data)); - }); - }) - .on('cancel', function() { - role.property('value', origValue); - }) - ); - } - - - function unbind() { - var row = select(this); - - row.selectAll('input.member-role') - .call(uiCombobox.off, context); - } - } - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return section; - }; - - - return section; - } - - function uiSectionRawMembershipEditor(context) { - - var section = uiSection('raw-membership-editor', context) - .shouldDisplay(function() { - return _entityIDs && _entityIDs.length === 1; - }) - .title(function() { - var entity = context.hasEntity(_entityIDs[0]); - if (!entity) return ''; - - var parents = context.graph().parentRelations(entity); - var gt = parents.length > _maxMemberships ? '>' : ''; - var count = gt + parents.slice(0, _maxMemberships).length; - return _t('inspector.title_count', { title: _t('inspector.relations'), count: count }); - }) - .disclosureContent(renderDisclosureContent); - - var taginfo = services.taginfo; - var nearbyCombo = uiCombobox(context, 'parent-relation') - .minItems(1) - .fetcher(fetchNearbyRelations) - .itemsMouseEnter(function(d) { - if (d.relation) utilHighlightEntities([d.relation.id], true, context); - }) - .itemsMouseLeave(function(d) { - if (d.relation) utilHighlightEntities([d.relation.id], false, context); - }); - var _inChange = false; - var _entityIDs = []; - var _showBlank; - var _maxMemberships = 1000; - - function selectRelation(d) { - event.preventDefault(); - - // remove the hover-highlight styling - utilHighlightEntities([d.relation.id], false, context); - - context.enter(modeSelect(context, [d.relation.id])); - } - - function zoomToRelation(d) { - event.preventDefault(); - - var entity = context.entity(d.relation.id); - context.map().zoomToEase(entity); - - // highlight the relation in case it wasn't previously on-screen - utilHighlightEntities([d.relation.id], true, context); - } - - - function changeRole(d) { - if (d === 0) return; // called on newrow (shoudn't happen) - if (_inChange) return; // avoid accidental recursive call #5731 - - var oldRole = d.member.role; - var newRole = context.cleanRelationRole(select(this).property('value')); - - if (oldRole !== newRole) { - _inChange = true; - context.perform( - actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index), - _t('operations.change_role.annotation') - ); - } - _inChange = false; - } - - - function addMembership(d, role) { - this.blur(); // avoid keeping focus on the button - _showBlank = false; - - var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role }; - - if (d.relation) { - context.perform( - actionAddMember(d.relation.id, member), - _t('operations.add_member.annotation') - ); - - } else { - var relation = osmRelation(); - context.perform( - actionAddEntity(relation), - actionAddMember(relation.id, member), - _t('operations.add.annotation.relation') - ); - - context.enter(modeSelect(context, [relation.id]).newFeature(true)); - } - } - - - function deleteMembership(d) { - this.blur(); // avoid keeping focus on the button - if (d === 0) return; // called on newrow (shoudn't happen) - - // remove the hover-highlight styling - utilHighlightEntities([d.relation.id], false, context); - - context.perform( - actionDeleteMember(d.relation.id, d.index), - _t('operations.delete_member.annotation') - ); - } - - - function fetchNearbyRelations(q, callback) { - var newRelation = { relation: null, value: _t('inspector.new_relation') }; - - var entityID = _entityIDs[0]; - - var result = []; - - var graph = context.graph(); - - function baseDisplayLabel(entity) { - var matched = _mainPresetIndex.match(entity, graph); - var presetName = (matched && matched.name()) || _t('inspector.relation'); - var entityName = utilDisplayName(entity) || ''; - - return presetName + ' ' + entityName; - } - - var explicitRelation = q && context.hasEntity(q.toLowerCase()); - if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) { - // loaded relation is specified explicitly, only show that - - result.push({ - relation: explicitRelation, - value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id - }); - } else { - - context.history().intersects(context.map().extent()).forEach(function(entity) { - if (entity.type !== 'relation' || entity.id === entityID) return; - - var value = baseDisplayLabel(entity); - if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return; - - result.push({ relation: entity, value: value }); - }); - - result.sort(function(a, b) { - return osmRelation.creationOrder(a.relation, b.relation); - }); - - // Dedupe identical names by appending relation id - see #2891 - var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')) - .filter(function(v) { return v.length > 1; }); - - dupeGroups.forEach(function(group) { - group.forEach(function(obj) { - obj.value += ' ' + obj.relation.id; - }); - }); - } - - result.forEach(function(obj) { - obj.title = obj.value; - }); - - result.unshift(newRelation); - callback(result); - } - - function renderDisclosureContent(selection) { - - var entityID = _entityIDs[0]; - - var entity = context.entity(entityID); - var parents = context.graph().parentRelations(entity); - - var memberships = []; - - parents.slice(0, _maxMemberships).forEach(function(relation) { - relation.members.forEach(function(member, index) { - if (member.id === entity.id) { - memberships.push({ - relation: relation, - member: member, - index: index, - domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index) - }); - } - }); - }); - - var list = selection.selectAll('.member-list') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'member-list') - .merge(list); - - - var items = list.selectAll('li.member-row-normal') - .data(memberships, function(d) { - return osmEntity.key(d.relation) + ',' + d.index; - }); - - items.exit() - .each(unbind) - .remove(); - - // Enter - var itemsEnter = items.enter() - .append('li') - .attr('class', 'member-row member-row-normal form-field'); - - // highlight the relation in the map while hovering on the list item - itemsEnter.on('mouseover', function(d) { - utilHighlightEntities([d.relation.id], true, context); - }) - .on('mouseout', function(d) { - utilHighlightEntities([d.relation.id], false, context); - }); - - var labelEnter = itemsEnter - .append('label') - .attr('class', 'field-label') - .attr('for', function(d) { - return d.domId; - }); - - var labelLink = labelEnter - .append('span') - .attr('class', 'label-text') - .append('a') - .attr('href', '#') - .on('click', selectRelation); - - labelLink - .append('span') - .attr('class', 'member-entity-type') - .text(function(d) { - var matched = _mainPresetIndex.match(d.relation, context.graph()); - return (matched && matched.name()) || _t('inspector.relation'); - }); - - labelLink - .append('span') - .attr('class', 'member-entity-name') - .text(function(d) { return utilDisplayName(d.relation); }); - - labelEnter - .append('button') - .attr('tabindex', -1) - .attr('class', 'remove member-delete') - .call(svgIcon('#iD-operation-delete')) - .on('click', deleteMembership); - - labelEnter - .append('button') - .attr('class', 'member-zoom') - .attr('title', _t('icons.zoom_to')) - .call(svgIcon('#iD-icon-framed-dot', 'monochrome')) - .on('click', zoomToRelation); - - var wrapEnter = itemsEnter - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-member'); - - wrapEnter - .append('input') - .attr('class', 'member-role') - .attr('id', function(d) { - return d.domId; - }) - .property('type', 'text') - .attr('placeholder', _t('inspector.role')) - .call(utilNoAuto) - .property('value', function(d) { return d.member.role; }) - .on('blur', changeRole) - .on('change', changeRole); - - if (taginfo) { - wrapEnter.each(bindTypeahead); - } - - - var newMembership = list.selectAll('.member-row-new') - .data(_showBlank ? [0] : []); - - // Exit - newMembership.exit() - .remove(); - - // Enter - var newMembershipEnter = newMembership.enter() - .append('li') - .attr('class', 'member-row member-row-new form-field'); - - var newLabelEnter = newMembershipEnter - .append('label') - .attr('class', 'field-label'); - - newLabelEnter - .append('input') - .attr('placeholder', _t('inspector.choose_relation')) - .attr('type', 'text') - .attr('class', 'member-entity-input') - .call(utilNoAuto); - - newLabelEnter - .append('button') - .attr('tabindex', -1) - .attr('class', 'remove member-delete') - .call(svgIcon('#iD-operation-delete')) - .on('click', function() { - list.selectAll('.member-row-new') - .remove(); - }); - - var newWrapEnter = newMembershipEnter - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-member'); - - newWrapEnter - .append('input') - .attr('class', 'member-role') - .property('type', 'text') - .attr('placeholder', _t('inspector.role')) - .call(utilNoAuto); - - // Update - newMembership = newMembership - .merge(newMembershipEnter); - - newMembership.selectAll('.member-entity-input') - .on('blur', cancelEntity) // if it wasn't accepted normally, cancel it - .call(nearbyCombo - .on('accept', acceptEntity) - .on('cancel', cancelEntity) - ); - - - // Container for the Add button - var addRow = selection.selectAll('.add-row') - .data([0]); - - // enter - var addRowEnter = addRow.enter() - .append('div') - .attr('class', 'add-row'); - - var addRelationButton = addRowEnter - .append('button') - .attr('class', 'add-relation'); - - addRelationButton - .call(svgIcon('#iD-icon-plus', 'light')); - addRelationButton - .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left')); - - addRowEnter - .append('div') - .attr('class', 'space-value'); // preserve space - - addRowEnter - .append('div') - .attr('class', 'space-buttons'); // preserve space - - // update - addRow = addRow - .merge(addRowEnter); - - addRow.select('.add-relation') - .on('click', function() { - _showBlank = true; - section.reRender(); - list.selectAll('.member-entity-input').node().focus(); - }); - - - function acceptEntity(d) { - if (!d) { - cancelEntity(); - return; - } - // remove hover-higlighting - if (d.relation) utilHighlightEntities([d.relation.id], false, context); - - var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value')); - addMembership(d, role); - } - - - function cancelEntity() { - var input = newMembership.selectAll('.member-entity-input'); - input.property('value', ''); - - // remove hover-higlighting - context.surface().selectAll('.highlighted') - .classed('highlighted', false); - } - - - function bindTypeahead(d) { - var row = select(this); - var role = row.selectAll('input.member-role'); - var origValue = role.property('value'); - - function sort(value, data) { - var sameletter = []; - var other = []; - for (var i = 0; i < data.length; i++) { - if (data[i].value.substring(0, value.length) === value) { - sameletter.push(data[i]); - } else { - other.push(data[i]); - } - } - return sameletter.concat(other); - } - - role.call(uiCombobox(context, 'member-role') - .fetcher(function(role, callback) { - var rtype = d.relation.tags.type; - taginfo.roles({ - debounce: true, - rtype: rtype || '', - geometry: context.graph().geometry(entityID), - query: role - }, function(err, data) { - if (!err) callback(sort(role, data)); - }); - }) - .on('cancel', function() { - role.property('value', origValue); - }) - ); - } - - - function unbind() { - var row = select(this); - - row.selectAll('input.member-role') - .call(uiCombobox.off, context); - } - } - - - section.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - _showBlank = false; - return section; - }; - - - return section; - } - - function uiSectionSelectionList(context) { - - var _selectedIDs = []; - - var section = uiSection('selected-features', context) - .shouldDisplay(function() { - return _selectedIDs.length > 1; - }) - .title(function() { - return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length }); - }) - .disclosureContent(renderDisclosureContent); - - context.history() - .on('change.selectionList', function(difference) { - if (difference) { - section.reRender(); - } - }); - - section.entityIDs = function(val) { - if (!arguments.length) return _selectedIDs; - _selectedIDs = val; - return section; - }; - - function selectEntity(entity) { - context.enter(modeSelect(context, [entity.id])); - } - - function deselectEntity(entity) { - event.stopPropagation(); - - var selectedIDs = _selectedIDs.slice(); - var index = selectedIDs.indexOf(entity.id); - if (index > -1) { - selectedIDs.splice(index, 1); - context.enter(modeSelect(context, selectedIDs)); - } - } - - function renderDisclosureContent(selection) { - - var list = selection.selectAll('.feature-list') - .data([0]); - - list = list.enter() - .append('div') - .attr('class', 'feature-list') - .merge(list); - - var entities = _selectedIDs - .map(function(id) { return context.hasEntity(id); }) - .filter(Boolean); - - var items = list.selectAll('.feature-list-item') - .data(entities, osmEntity.key); - - items.exit() - .remove(); - - // Enter - var enter = items.enter() - .append('div') - .attr('class', 'feature-list-item') - .on('click', selectEntity); - - enter - .each(function(d) { - select(this).on('mouseover', function() { - utilHighlightEntities([d.id], true, context); - }); - select(this).on('mouseout', function() { - utilHighlightEntities([d.id], false, context); - }); - }); - - var label = enter - .append('button') - .attr('class', 'label'); - - enter - .append('button') - .attr('class', 'close') - .attr('title', _t('icons.deselect')) - .on('click', deselectEntity) - .call(svgIcon('#iD-icon-close')); - - label - .append('span') - .attr('class', 'entity-geom-icon') - .call(svgIcon('', 'pre-text')); - - label - .append('span') - .attr('class', 'entity-type'); - - label - .append('span') - .attr('class', 'entity-name'); - - // Update - items = items.merge(enter); - - items.selectAll('.entity-geom-icon use') - .attr('href', function() { - var entity = this.parentNode.parentNode.__data__; - return '#iD-icon-' + entity.geometry(context.graph()); - }); - - items.selectAll('.entity-type') - .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); }); - - items.selectAll('.entity-name') - .text(function(d) { - // fetch latest entity - var entity = context.entity(d.id); - return utilDisplayName(entity); - }); - } - - return section; - } - - function uiEntityEditor(context) { - var dispatch$1 = dispatch('choose'); - var _state = 'select'; - var _coalesceChanges = false; - var _modified = false; - var _base; - var _entityIDs; - var _activePresets = []; - var _newFeature; - - var _sections; - - function entityEditor(selection) { - - var combinedTags = utilCombinedTags(_entityIDs, context.graph()); - - // Header - var header = selection.selectAll('.header') - .data([0]); - - // Enter - var headerEnter = header.enter() - .append('div') - .attr('class', 'header fillL cf'); - - headerEnter - .append('button') - .attr('class', 'preset-reset preset-choose') - .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward')); - - headerEnter - .append('button') - .attr('class', 'close') - .on('click', function() { context.enter(modeBrowse(context)); }) - .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close')); - - headerEnter - .append('h3'); - - // Update - header = header - .merge(headerEnter); - - header.selectAll('h3') - .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features')); - - header.selectAll('.preset-reset') - .on('click', function() { - dispatch$1.call('choose', this, _activePresets); - }); - - // Body - var body = selection.selectAll('.inspector-body') - .data([0]); - - // Enter - var bodyEnter = body.enter() - .append('div') - .attr('class', 'entity-editor inspector-body sep-top'); - - // Update - body = body - .merge(bodyEnter); - - if (!_sections) { - _sections = [ - uiSectionSelectionList(context), - uiSectionFeatureType(context).on('choose', function(presets) { - dispatch$1.call('choose', this, presets); - }), - uiSectionEntityIssues(context), - uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), - uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), - uiSectionRawMemberEditor(context), - uiSectionRawMembershipEditor(context) - ]; - } - - _sections.forEach(function(section) { - if (section.entityIDs) { - section.entityIDs(_entityIDs); - } - if (section.presets) { - section.presets(_activePresets); - } - if (section.tags) { - section.tags(combinedTags); - } - if (section.state) { - section.state(_state); - } - body.call(section.render); - }); - - body - .selectAll('.key-trap-wrap') - .data([0]) - .enter() - .append('div') - .attr('class', 'key-trap-wrap') - .append('input') - .attr('type', 'text') - .attr('class', 'key-trap') - .on('keydown.key-trap', function() { - // On tabbing, send focus back to the first field on the inspector-body - // (probably the `name` field) #4159 - if (event.keyCode === 9 && !event.shiftKey) { - event.preventDefault(); - body.select('input').node().focus(); - } - }); - - context.history() - .on('change.entity-editor', historyChanged); - - function historyChanged(difference) { - if (selection.selectAll('.entity-editor').empty()) return; - if (_state === 'hide') return; - var significant = !difference || - difference.didChange.properties || - difference.didChange.addition || - difference.didChange.deletion; - if (!significant) return; - - _entityIDs = _entityIDs.filter(context.hasEntity); - if (!_entityIDs.length) return; - - var priorActivePreset = _activePresets.length === 1 && _activePresets[0]; - - loadActivePresets(); - - var graph = context.graph(); - entityEditor.modified(_base !== graph); - entityEditor(selection); - - if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) { - // flash the button to indicate the preset changed - context.container().selectAll('.entity-editor button.preset-reset .label') - .style('background-color', '#fff') - .transition() - .duration(750) - .style('background-color', null); - } - } - } - - - // Tag changes that fire on input can all get coalesced into a single - // history operation when the user leaves the field. #2342 - // Use explicit entityIDs in case the selection changes before the event is fired. - function changeTags(entityIDs, changed, onInput) { - - var actions = []; - for (var i in entityIDs) { - var entityID = entityIDs[i]; - var entity = context.entity(entityID); - - var tags = Object.assign({}, entity.tags); // shallow copy - - for (var k in changed) { - if (!k) continue; - var v = changed[k]; - if (v !== undefined || tags.hasOwnProperty(k)) { - tags[k] = v; - } - } - - if (!onInput) { - tags = utilCleanTags(tags); - } - - if (!fastDeepEqual(entity.tags, tags)) { - actions.push(actionChangeTags(entityID, tags)); - } - } - - if (actions.length) { - var combinedAction = function(graph) { - actions.forEach(function(action) { - graph = action(graph); - }); - return graph; - }; - - var annotation = _t('operations.change_tags.annotation'); - - if (_coalesceChanges) { - context.overwrite(combinedAction, annotation); - } else { - context.perform(combinedAction, annotation); - _coalesceChanges = !!onInput; - } - } - - // if leaving field (blur event), rerun validation - if (!onInput) { - context.validator().validate(); - } - } - - function revertTags(keys) { - - var actions = []; - for (var i in _entityIDs) { - var entityID = _entityIDs[i]; - - var original = context.graph().base().entities[entityID]; - var changed = {}; - for (var j in keys) { - var key = keys[j]; - changed[key] = original ? original.tags[key] : undefined; - } - - var entity = context.entity(entityID); - var tags = Object.assign({}, entity.tags); // shallow copy - - for (var k in changed) { - if (!k) continue; - var v = changed[k]; - if (v !== undefined || tags.hasOwnProperty(k)) { - tags[k] = v; - } - } - - - tags = utilCleanTags(tags); - - if (!fastDeepEqual(entity.tags, tags)) { - actions.push(actionChangeTags(entityID, tags)); - } - - } - - if (actions.length) { - var combinedAction = function(graph) { - actions.forEach(function(action) { - graph = action(graph); - }); - return graph; - }; - - var annotation = _t('operations.change_tags.annotation'); - - if (_coalesceChanges) { - context.overwrite(combinedAction, annotation); - } else { - context.perform(combinedAction, annotation); - _coalesceChanges = false; - } - } - - context.validator().validate(); - } - - - entityEditor.modified = function(val) { - if (!arguments.length) return _modified; - _modified = val; - return entityEditor; - }; - - - entityEditor.state = function(val) { - if (!arguments.length) return _state; - _state = val; - return entityEditor; - }; - - - entityEditor.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change - - _entityIDs = val; - _base = context.graph(); - _coalesceChanges = false; - - loadActivePresets(); - - return entityEditor - .modified(false); - }; - - - entityEditor.newFeature = function(val) { - if (!arguments.length) return _newFeature; - _newFeature = val; - return entityEditor; - }; - - - function loadActivePresets() { - - var graph = context.graph(); - - var counts = {}; - - for (var i in _entityIDs) { - var entity = graph.hasEntity(_entityIDs[i]); - if (!entity) return; - - var match = _mainPresetIndex.match(entity, graph); - - if (!counts[match.id]) counts[match.id] = 0; - counts[match.id] += 1; - } - - var matches = Object.keys(counts).sort(function(p1, p2) { - return counts[p2] - counts[p1]; - }).map(function(pID) { - return _mainPresetIndex.item(pID); - }); - - // A "weak" preset doesn't set any tags. (e.g. "Address") - var weakPreset = _activePresets.length === 1 && - Object.keys(_activePresets[0].addTags || {}).length === 0; - // Don't replace a weak preset with a fallback preset (e.g. "Point") - if (weakPreset && matches.length === 1 && matches[0].isFallback()) return; - - entityEditor.presets(matches); - } - - entityEditor.presets = function(val) { - if (!arguments.length) return _activePresets; - - // don't reload the same preset - if (!utilArrayIdentical(val, _activePresets)) { - _activePresets = val; - } - return entityEditor; - }; - - return utilRebind(entityEditor, dispatch$1, 'on'); - } - - function uiPresetList(context) { - var dispatch$1 = dispatch('cancel', 'choose'); - var _entityIDs; - var _currentPresets; - var _autofocus = false; - - - function presetList(selection) { - if (!_entityIDs) return; - - var presets = _mainPresetIndex.matchAllGeometry(entityGeometries()); - - selection.html(''); - - var messagewrap = selection - .append('div') - .attr('class', 'header fillL'); - - var message = messagewrap - .append('h3') - .text(_t('inspector.choose')); - - messagewrap - .append('button') - .attr('class', 'preset-choose') - .on('click', function() { dispatch$1.call('cancel', this); }) - .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward')); - - function initialKeydown() { - // hack to let delete shortcut work when search is autofocused - if (search.property('value').length === 0 && - (event.keyCode === utilKeybinding.keyCodes['⌫'] || - event.keyCode === utilKeybinding.keyCodes['⌦'])) { - event.preventDefault(); - event.stopPropagation(); - operationDelete(context, _entityIDs)(); - - // hack to let undo work when search is autofocused - } else if (search.property('value').length === 0 && - (event.ctrlKey || event.metaKey) && - event.keyCode === utilKeybinding.keyCodes.z) { - event.preventDefault(); - event.stopPropagation(); - context.undo(); - } else if (!event.ctrlKey && !event.metaKey) { - // don't check for delete/undo hack on future keydown events - select(this).on('keydown', keydown); - keydown.call(this); - } - } - - function keydown() { - // down arrow - if (event.keyCode === utilKeybinding.keyCodes['↓'] && - // if insertion point is at the end of the string - search.node().selectionStart === search.property('value').length) { - event.preventDefault(); - event.stopPropagation(); - // move focus to the first item in the preset list - var buttons = list.selectAll('.preset-list-button'); - if (!buttons.empty()) buttons.nodes()[0].focus(); - } - } - - function keypress() { - // enter - var value = search.property('value'); - if (event.keyCode === 13 && value.length) { - list.selectAll('.preset-list-item:first-child') - .each(function(d) { d.choose.call(this); }); - } - } - - function inputevent() { - var value = search.property('value'); - list.classed('filtered', value.length); - var extent = combinedEntityExtent(); - var results, messageText; - if (value.length && extent) { - var center = extent.center(); - var countryCode = iso1A2Code(center); - - results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase()); - messageText = _t('inspector.results', { - n: results.collection.length, - search: value - }); - } else { - results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()); - messageText = _t('inspector.choose'); - } - list.call(drawList, results); - message.text(messageText); - } - - var searchWrap = selection - .append('div') - .attr('class', 'search-header'); - - var search = searchWrap - .append('input') - .attr('class', 'preset-search-input') - .attr('placeholder', _t('inspector.search')) - .attr('type', 'search') - .call(utilNoAuto) - .on('keydown', initialKeydown) - .on('keypress', keypress) - .on('input', inputevent); - - searchWrap - .call(svgIcon('#iD-icon-search', 'pre-text')); - - if (_autofocus) { - search.node().focus(); - } - - var listWrap = selection - .append('div') - .attr('class', 'inspector-body'); - - var list = listWrap - .append('div') - .attr('class', 'preset-list') - .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro())); - - context.features().on('change.preset-list', updateForFeatureHiddenState); - } - - - function drawList(list, presets) { - presets = presets.matchAllGeometry(entityGeometries()); - var collection = presets.collection.reduce(function(collection, preset) { - if (!preset) return collection; - - if (preset.members) { - if (preset.members.collection.filter(function(preset) { - return preset.addable(); - }).length > 1) { - collection.push(CategoryItem(preset)); - } - } else if (preset.addable()) { - collection.push(PresetItem(preset)); - } - return collection; - }, []); - - var items = list.selectAll('.preset-list-item') - .data(collection, function(d) { return d.preset.id; }); - - items.order(); - - items.exit() - .remove(); - - items.enter() - .append('div') - .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); }) - .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; }) - .each(function(item) { select(this).call(item); }) - .style('opacity', 0) - .transition() - .style('opacity', 1); - - updateForFeatureHiddenState(); - } - - function itemKeydown(){ - // the actively focused item - var item = select(this.closest('.preset-list-item')); - var parentItem = select(item.node().parentNode.closest('.preset-list-item')); - - // arrow down, move focus to the next, lower item - if (event.keyCode === utilKeybinding.keyCodes['↓']) { - event.preventDefault(); - event.stopPropagation(); - // the next item in the list at the same level - var nextItem = select(item.node().nextElementSibling); - // if there is no next item in this list - if (nextItem.empty()) { - // if there is a parent item - if (!parentItem.empty()) { - // the item is the last item of a sublist, - // select the next item at the parent level - nextItem = select(parentItem.node().nextElementSibling); - } - // if the focused item is expanded - } else if (select(this).classed('expanded')) { - // select the first subitem instead - nextItem = item.select('.subgrid .preset-list-item:first-child'); - } - if (!nextItem.empty()) { - // focus on the next item - nextItem.select('.preset-list-button').node().focus(); - } - - // arrow up, move focus to the previous, higher item - } else if (event.keyCode === utilKeybinding.keyCodes['↑']) { - event.preventDefault(); - event.stopPropagation(); - // the previous item in the list at the same level - var previousItem = select(item.node().previousElementSibling); - - // if there is no previous item in this list - if (previousItem.empty()) { - // if there is a parent item - if (!parentItem.empty()) { - // the item is the first subitem of a sublist select the parent item - previousItem = parentItem; - } - // if the previous item is expanded - } else if (previousItem.select('.preset-list-button').classed('expanded')) { - // select the last subitem of the sublist of the previous item - previousItem = previousItem.select('.subgrid .preset-list-item:last-child'); - } - - if (!previousItem.empty()) { - // focus on the previous item - previousItem.select('.preset-list-button').node().focus(); - } else { - // the focus is at the top of the list, move focus back to the search field - var search = select(this.closest('.preset-list-pane')).select('.preset-search-input'); - search.node().focus(); - } - - // arrow left, move focus to the parent item if there is one - } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) { - event.preventDefault(); - event.stopPropagation(); - // if there is a parent item, focus on the parent item - if (!parentItem.empty()) { - parentItem.select('.preset-list-button').node().focus(); - } - - // arrow right, choose this item - } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) { - event.preventDefault(); - event.stopPropagation(); - item.datum().choose.call(select(this).node()); - } - } - - - function CategoryItem(preset) { - var box, sublist, shown = false; - - function item(selection) { - var wrap = selection.append('div') - .attr('class', 'preset-list-button-wrap category'); - - function click() { - var isExpanded = select(this).classed('expanded'); - var iconName = isExpanded ? - (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down'; - select(this) - .classed('expanded', !isExpanded); - select(this).selectAll('div.label-inner svg.icon use') - .attr('href', iconName); - item.choose(); - } - - var geometries = entityGeometries(); - - var button = wrap - .append('button') - .attr('class', 'preset-list-button') - .classed('expanded', false) - .call(uiPresetIcon() - .geometry(geometries.length === 1 && geometries[0]) - .preset(preset)) - .on('click', click) - .on('keydown', function() { - // right arrow, expand the focused item - if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) { - event.preventDefault(); - event.stopPropagation(); - // if the item isn't expanded - if (!select(this).classed('expanded')) { - // toggle expansion (expand the item) - click.call(this); - } - // left arrow, collapse the focused item - } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) { - event.preventDefault(); - event.stopPropagation(); - // if the item is expanded - if (select(this).classed('expanded')) { - // toggle expansion (collapse the item) - click.call(this); - } - } else { - itemKeydown.call(this); - } - }); - - var label = button - .append('div') - .attr('class', 'label') - .append('div') - .attr('class', 'label-inner'); - - label - .append('div') - .attr('class', 'namepart') - .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline')) - .append('span') - .html(function() { return preset.name() + '…'; }); - - box = selection.append('div') - .attr('class', 'subgrid') - .style('max-height', '0px') - .style('opacity', 0); - - box.append('div') - .attr('class', 'arrow'); - - sublist = box.append('div') - .attr('class', 'preset-list fillL3'); - } - - - item.choose = function() { - if (!box || !sublist) return; - - if (shown) { - shown = false; - box.transition() - .duration(200) - .style('opacity', '0') - .style('max-height', '0px') - .style('padding-bottom', '0px'); - } else { - shown = true; - var members = preset.members.matchAllGeometry(entityGeometries()); - sublist.call(drawList, members); - box.transition() - .duration(200) - .style('opacity', '1') - .style('max-height', 200 + members.collection.length * 190 + 'px') - .style('padding-bottom', '10px'); - } - }; - - item.preset = preset; - return item; - } - - - function PresetItem(preset) { - function item(selection) { - var wrap = selection.append('div') - .attr('class', 'preset-list-button-wrap'); - - var geometries = entityGeometries(); - - var button = wrap.append('button') - .attr('class', 'preset-list-button') - .call(uiPresetIcon() - .geometry(geometries.length === 1 && geometries[0]) - .preset(preset)) - .on('click', item.choose) - .on('keydown', itemKeydown); - - var label = button - .append('div') - .attr('class', 'label') - .append('div') - .attr('class', 'label-inner'); - - // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc) - label.selectAll('.namepart') - .data(preset.name().split(' – ')) - .enter() - .append('div') - .attr('class', 'namepart') - .text(function(d) { return d; }); - - wrap.call(item.reference.button); - selection.call(item.reference.body); - } - - item.choose = function() { - if (select(this).classed('disabled')) return; - if (!context.inIntro()) { - _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]); - } - context.perform( - function(graph) { - for (var i in _entityIDs) { - var entityID = _entityIDs[i]; - var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph); - graph = actionChangePreset(entityID, oldPreset, preset)(graph); - } - return graph; - }, - _t('operations.change_tags.annotation') - ); - - context.validator().validate(); // rerun validation - dispatch$1.call('choose', this, preset); - }; - - item.help = function() { - event.stopPropagation(); - item.reference.toggle(); - }; - - item.preset = preset; - item.reference = uiTagReference(preset.reference(entityGeometries()[0])); - - return item; - } - - - function updateForFeatureHiddenState() { - if (!_entityIDs.every(context.hasEntity)) return; - - var geometries = entityGeometries(); - var button = context.container().selectAll('.preset-list .preset-list-button'); - - // remove existing tooltips - button.call(uiTooltip().destroyAny); - - button.each(function(item, index) { - var hiddenPresetFeaturesId; - for (var i in geometries) { - hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]); - if (hiddenPresetFeaturesId) break; - } - var isHiddenPreset = !context.inIntro() && - !!hiddenPresetFeaturesId && - (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]); - - select(this) - .classed('disabled', isHiddenPreset); - - if (isHiddenPreset) { - var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId); - var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual'; - var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') }; - select(this).call(uiTooltip() - .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj)) - .placement(index < 2 ? 'bottom' : 'top') - ); - } - }); - } - - presetList.autofocus = function(val) { - if (!arguments.length) return _autofocus; - _autofocus = val; - return presetList; - }; - - presetList.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - if (_entityIDs && _entityIDs.length) { - var presets = _entityIDs.map(function(entityID) { - return _mainPresetIndex.match(context.entity(entityID), context.graph()); - }); - presetList.presets(presets); - } - return presetList; - }; - - presetList.presets = function(val) { - if (!arguments.length) return _currentPresets; - _currentPresets = val; - return presetList; - }; - - function entityGeometries() { - - var counts = {}; - - for (var i in _entityIDs) { - var entityID = _entityIDs[i]; - var entity = context.entity(entityID); - var geometry = entity.geometry(context.graph()); - - // Treat entities on addr:interpolation lines as points, not vertices (#3241) - if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) { - geometry = 'point'; - } - - if (!counts[geometry]) counts[geometry] = 0; - counts[geometry] += 1; - } - - return Object.keys(counts).sort(function(geom1, geom2) { - return counts[geom2] - counts[geom1]; - }); - } - - function combinedEntityExtent() { - return _entityIDs.reduce(function(extent, entityID) { - var entity = context.graph().entity(entityID); - return extent.extend(entity.extent(context.graph())); - }, geoExtent()); - } - - return utilRebind(presetList, dispatch$1, 'on'); - } - - function uiInspector(context) { - var presetList = uiPresetList(context); - var entityEditor = uiEntityEditor(context); - var wrap = select(null), - presetPane = select(null), - editorPane = select(null); - var _state = 'select'; - var _entityIDs; - var _newFeature = false; - - - function inspector(selection) { - presetList - .entityIDs(_entityIDs) - .autofocus(_newFeature) - .on('choose', inspector.setPreset) - .on('cancel', function() { - wrap.transition() - .styleTween('right', function() { return interpolate('-100%', '0%'); }); - editorPane.call(entityEditor); - }); - - entityEditor - .state(_state) - .entityIDs(_entityIDs) - .on('choose', inspector.showList); - - wrap = selection.selectAll('.panewrap') - .data([0]); - - var enter = wrap.enter() - .append('div') - .attr('class', 'panewrap'); - - enter - .append('div') - .attr('class', 'preset-list-pane pane'); - - enter - .append('div') - .attr('class', 'entity-editor-pane pane'); - - wrap = wrap.merge(enter); - presetPane = wrap.selectAll('.preset-list-pane'); - editorPane = wrap.selectAll('.entity-editor-pane'); - - function shouldDefaultToPresetList() { - // always show the inspector on hover - if (_state !== 'select') return false; - - // can only change preset on single selection - if (_entityIDs.length !== 1) return false; - - var entityID = _entityIDs[0]; - var entity = context.hasEntity(entityID); - if (!entity) return false; - - // default to inspector if there are already tags - if (entity.hasNonGeometryTags()) return false; - - // prompt to select preset if feature is new and untagged - if (_newFeature) return true; - - // all existing features except vertices should default to inspector - if (entity.geometry(context.graph()) !== 'vertex') return false; - - // show vertex relations if any - if (context.graph().parentRelations(entity).length) return false; - - // show vertex issues if there are any - if (context.validator().getEntityIssues(entityID).length) return false; - - // show turn retriction editor for junction vertices - if (entity.isHighwayIntersection(context.graph())) return false; - - // otherwise show preset list for uninteresting vertices - return true; - } - - if (shouldDefaultToPresetList()) { - wrap.style('right', '-100%'); - presetPane.call(presetList); - } else { - wrap.style('right', '0%'); - editorPane.call(entityEditor); - } - - var footer = selection.selectAll('.footer') - .data([0]); - - footer = footer.enter() - .append('div') - .attr('class', 'footer') - .merge(footer); - - footer - .call(uiViewOnOSM(context) - .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])) - ); - } - - inspector.showList = function(presets) { - - wrap.transition() - .styleTween('right', function() { return interpolate('0%', '-100%'); }); - - if (presets) { - presetList.presets(presets); - } - - presetPane - .call(presetList.autofocus(true)); - }; - - inspector.setPreset = function(preset) { - - // upon setting multipolygon, go to the area preset list instead of the editor - if (preset.id === 'type/multipolygon') { - presetPane - .call(presetList.autofocus(true)); - - } else { - wrap.transition() - .styleTween('right', function() { return interpolate('-100%', '0%'); }); - - editorPane - .call(entityEditor.presets([preset])); - } - - }; - - inspector.state = function(val) { - if (!arguments.length) return _state; - _state = val; - entityEditor.state(_state); - - // remove any old field help overlay that might have gotten attached to the inspector - context.container().selectAll('.field-help-body').remove(); - - return inspector; - }; - - - inspector.entityIDs = function(val) { - if (!arguments.length) return _entityIDs; - _entityIDs = val; - return inspector; - }; - - - inspector.newFeature = function(val) { - if (!arguments.length) return _newFeature; - _newFeature = val; - return inspector; - }; - - - return inspector; - } - - function uiSidebar(context) { - var inspector = uiInspector(context); - var dataEditor = uiDataEditor(context); - var noteEditor = uiNoteEditor(context); - var improveOsmEditor = uiImproveOsmEditor(context); - var keepRightEditor = uiKeepRightEditor(context); - var osmoseEditor = uiOsmoseEditor(context); - var _current; - var _wasData = false; - var _wasNote = false; - var _wasQaItem = false; - - // use pointer events on supported platforms; fallback to mouse events - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - - function sidebar(selection) { - var container = context.container(); - var minWidth = 240; - var sidebarWidth; - var containerWidth; - var dragOffset; - - // Set the initial width constraints - selection - .style('min-width', minWidth + 'px') - .style('max-width', '400px') - .style('width', '33.3333%'); - - var resizer = selection - .append('div') - .attr('class', 'sidebar-resizer') - .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown); - - var downPointerId, lastClientX, containerLocGetter; - - function pointerdown() { - if (downPointerId) return; - - if ('button' in event && event.button !== 0) return; - - downPointerId = event.pointerId || 'mouse'; - - lastClientX = event.clientX; - - containerLocGetter = utilFastMouse(container.node()); - - // offset from edge of sidebar-resizer - dragOffset = utilFastMouse(resizer.node())(event)[0] - 1; - - sidebarWidth = selection.node().getBoundingClientRect().width; - containerWidth = container.node().getBoundingClientRect().width; - var widthPct = (sidebarWidth / containerWidth) * 100; - selection - .style('width', widthPct + '%') // lock in current width - .style('max-width', '85%'); // but allow larger widths - - resizer.classed('dragging', true); - - select(window) - .on('touchmove.sidebar-resizer', function() { - // disable page scrolling while resizing on touch input - event.preventDefault(); - }, { passive: false }) - .on(_pointerPrefix + 'move.sidebar-resizer', pointermove) - .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup); - } - - function pointermove() { - - if (downPointerId !== (event.pointerId || 'mouse')) return; - - event.preventDefault(); - - var dx = event.clientX - lastClientX; - - lastClientX = event.clientX; - - var isRTL = (_mainLocalizer.textDirection() === 'rtl'); - var scaleX = isRTL ? 0 : 1; - var xMarginProperty = isRTL ? 'margin-right' : 'margin-left'; - - var x = containerLocGetter(event)[0] - dragOffset; - sidebarWidth = isRTL ? containerWidth - x : x; - - var isCollapsed = selection.classed('collapsed'); - var shouldCollapse = sidebarWidth < minWidth; - - selection.classed('collapsed', shouldCollapse); - - if (shouldCollapse) { - if (!isCollapsed) { - selection - .style(xMarginProperty, '-400px') - .style('width', '400px'); - - context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]); - } - - } else { - var widthPct = (sidebarWidth / containerWidth) * 100; - selection - .style(xMarginProperty, null) - .style('width', widthPct + '%'); - - if (isCollapsed) { - context.ui().onResize([-sidebarWidth * scaleX, 0]); - } else { - context.ui().onResize([-dx * scaleX, 0]); - } - } - } - - function pointerup() { - if (downPointerId !== (event.pointerId || 'mouse')) return; - - downPointerId = null; - - resizer.classed('dragging', false); - - select(window) - .on('touchmove.sidebar-resizer', null) - .on(_pointerPrefix + 'move.sidebar-resizer', null) - .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null); - } - - var featureListWrap = selection - .append('div') - .attr('class', 'feature-list-pane') - .call(uiFeatureList(context)); - - var inspectorWrap = selection - .append('div') - .attr('class', 'inspector-hidden inspector-wrap'); - - var hoverModeSelect = function(targets) { - context.container().selectAll('.feature-list-item').classed('hover', false); - - if (context.selectedIDs().length > 1 && - targets && targets.length) { - - var elements = context.container().selectAll('.feature-list-item') - .filter(function (node) { - return targets.indexOf(node) !== -1; - }); - - if (!elements.empty()) { - elements.classed('hover', true); - } - } - }; - - sidebar.hoverModeSelect = throttle(hoverModeSelect, 200); - - function hover(targets) { - var datum = targets && targets.length && targets[0]; - if (datum && datum.__featurehash__) { // hovering on data - _wasData = true; - sidebar - .show(dataEditor.datum(datum)); - - selection.selectAll('.sidebar-component') - .classed('inspector-hover', true); - - } else if (datum instanceof osmNote) { - if (context.mode().id === 'drag-note') return; - _wasNote = true; - - var osm = services.osm; - if (osm) { - datum = osm.getNote(datum.id); // marker may contain stale data - get latest - } - - sidebar - .show(noteEditor.note(datum)); - - selection.selectAll('.sidebar-component') - .classed('inspector-hover', true); - - } else if (datum instanceof QAItem) { - _wasQaItem = true; - - var errService = services[datum.service]; - if (errService) { - // marker may contain stale data - get latest - datum = errService.getError(datum.id); - } - - // Currently only three possible services - var errEditor; - if (datum.service === 'keepRight') { - errEditor = keepRightEditor; - } else if (datum.service === 'osmose') { - errEditor = osmoseEditor; - } else { - errEditor = improveOsmEditor; - } - - context.container().selectAll('.qaItem.' + datum.service) - .classed('hover', function(d) { return d.id === datum.id; }); - - sidebar - .show(errEditor.error(datum)); - - selection.selectAll('.sidebar-component') - .classed('inspector-hover', true); - - } else if (!_current && (datum instanceof osmEntity)) { - featureListWrap - .classed('inspector-hidden', true); - - inspectorWrap - .classed('inspector-hidden', false) - .classed('inspector-hover', true); - - if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') { - inspector - .state('hover') - .entityIDs([datum.id]) - .newFeature(false); - - inspectorWrap - .call(inspector); - } - - } else if (!_current) { - featureListWrap - .classed('inspector-hidden', false); - inspectorWrap - .classed('inspector-hidden', true); - inspector - .state('hide'); - - } else if (_wasData || _wasNote || _wasQaItem) { - _wasNote = false; - _wasData = false; - _wasQaItem = false; - context.container().selectAll('.note').classed('hover', false); - context.container().selectAll('.qaItem').classed('hover', false); - sidebar.hide(); - } - } - - sidebar.hover = throttle(hover, 200); - - - sidebar.intersects = function(extent) { - var rect = selection.node().getBoundingClientRect(); - return extent.intersects([ - context.projection.invert([0, rect.height]), - context.projection.invert([rect.width, 0]) - ]); - }; - - - sidebar.select = function(ids, newFeature) { - sidebar.hide(); - - if (ids && ids.length) { - - var entity = ids.length === 1 && context.entity(ids[0]); - if (entity && newFeature && selection.classed('collapsed')) { - // uncollapse the sidebar - var extent = entity.extent(context.graph()); - sidebar.expand(sidebar.intersects(extent)); - } - - featureListWrap - .classed('inspector-hidden', true); - - inspectorWrap - .classed('inspector-hidden', false) - .classed('inspector-hover', false); - - if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') { - inspector - .state('select') - .entityIDs(ids) - .newFeature(newFeature); - - inspectorWrap - .call(inspector); - } - - } else { - inspector - .state('hide'); - } - }; - - - sidebar.showPresetList = function() { - inspector.showList(); - }; - - - sidebar.show = function(component, element) { - featureListWrap - .classed('inspector-hidden', true); - inspectorWrap - .classed('inspector-hidden', true); - - if (_current) _current.remove(); - _current = selection - .append('div') - .attr('class', 'sidebar-component') - .call(component, element); - }; - - - sidebar.hide = function() { - featureListWrap - .classed('inspector-hidden', false); - inspectorWrap - .classed('inspector-hidden', true); - - if (_current) _current.remove(); - _current = null; - }; - - - sidebar.expand = function(moveMap) { - if (selection.classed('collapsed')) { - sidebar.toggle(moveMap); - } - }; - - - sidebar.collapse = function(moveMap) { - if (!selection.classed('collapsed')) { - sidebar.toggle(moveMap); - } - }; - - - sidebar.toggle = function(moveMap) { - var e = event; - if (e && e.sourceEvent) { - e.sourceEvent.preventDefault(); - } else if (e) { - e.preventDefault(); - } - - // Don't allow sidebar to toggle when the user is in the walkthrough. - if (context.inIntro()) return; - - var isCollapsed = selection.classed('collapsed'); - var isCollapsing = !isCollapsed; - var isRTL = (_mainLocalizer.textDirection() === 'rtl'); - var scaleX = isRTL ? 0 : 1; - var xMarginProperty = isRTL ? 'margin-right' : 'margin-left'; - - sidebarWidth = selection.node().getBoundingClientRect().width; - - // switch from % to px - selection.style('width', sidebarWidth + 'px'); - - var startMargin, endMargin, lastMargin; - if (isCollapsing) { - startMargin = lastMargin = 0; - endMargin = -sidebarWidth; - } else { - startMargin = lastMargin = -sidebarWidth; - endMargin = 0; - } - - selection.transition() - .style(xMarginProperty, endMargin + 'px') - .tween('panner', function() { - var i = d3_interpolateNumber(startMargin, endMargin); - return function(t) { - var dx = lastMargin - Math.round(i(t)); - lastMargin = lastMargin - dx; - context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]); - }; - }) - .on('end', function() { - selection.classed('collapsed', isCollapsing); - - // switch back from px to % - if (!isCollapsing) { - var containerWidth = container.node().getBoundingClientRect().width; - var widthPct = (sidebarWidth / containerWidth) * 100; - selection - .style(xMarginProperty, null) - .style('width', widthPct + '%'); - } - }); - }; - - // toggle the sidebar collapse when double-clicking the resizer - resizer.on('dblclick', sidebar.toggle); - - // ensure hover sidebar is closed when zooming out beyond editable zoom - context.map().on('crossEditableZoom.sidebar', function(within) { - if (!within && !selection.select('.inspector-hover').empty()) { - hover([]); - } - }); - } - - sidebar.showPresetList = function() {}; - sidebar.hover = function() {}; - sidebar.hover.cancel = function() {}; - sidebar.intersects = function() {}; - sidebar.select = function() {}; - sidebar.show = function() {}; - sidebar.hide = function() {}; - sidebar.expand = function() {}; - sidebar.collapse = function() {}; - sidebar.toggle = function() {}; - - return sidebar; - } - - function uiSourceSwitch(context) { - var keys; - - - function click() { - event.preventDefault(); - - var osm = context.connection(); - if (!osm) return; - - if (context.inIntro()) return; - - if (context.history().hasChanges() && - !window.confirm(_t('source_switch.lose_changes'))) return; - - var isLive = select(this) - .classed('live'); - - isLive = !isLive; - context.enter(modeBrowse(context)); - context.history().clearSaved(); // remove saved history - context.flush(); // remove stored data - - select(this) - .text(isLive ? _t('source_switch.live') : _t('source_switch.dev')) - .classed('live', isLive) - .classed('chip', isLive); - - osm.switch(isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event) - } - - var sourceSwitch = function(selection) { - selection - .append('a') - .attr('href', '#') - .text(_t('source_switch.live')) - .attr('class', 'live chip') - .on('click', click); - }; - - - sourceSwitch.keys = function(_) { - if (!arguments.length) return keys; - keys = _; - return sourceSwitch; - }; - - - return sourceSwitch; - } - - function uiSpinner(context) { - var osm = context.connection(); - - - return function(selection) { - var img = selection - .append('img') - .attr('src', context.imagePath('loader-black.gif')) - .style('opacity', 0); - - if (osm) { - osm - .on('loading.spinner', function() { - img.transition() - .style('opacity', 1); - }) - .on('loaded.spinner', function() { - img.transition() - .style('opacity', 0); - }); - } - }; - } - - function uiSplash(context) { - return (selection) => { - // Exception - if there are restorable changes, skip this splash screen. - // This is because we currently only support one `uiModal` at a time - // and we need to show them `uiRestore`` instead of this one. - if (context.history().hasRestorableChanges()) return; - - // If user has not seen this version of the privacy policy, show the splash again. - let updateMessage = ''; - const sawPrivacyVersion = corePreferences('sawPrivacyVersion'); - let showSplash = !corePreferences('sawSplash'); - if (sawPrivacyVersion !== context.privacyVersion) { - updateMessage = _t('splash.privacy_update'); - showSplash = true; - } - - if (!showSplash) return; - - corePreferences('sawSplash', true); - corePreferences('sawPrivacyVersion', context.privacyVersion); - - // fetch intro graph data now, while user is looking at the splash screen - _mainFileFetcher.get('intro_graph'); - - let modalSelection = uiModal(selection); - - modalSelection.select('.modal') - .attr('class', 'modal-splash modal'); - - let introModal = modalSelection.select('.content') - .append('div') - .attr('class', 'fillL'); - - introModal - .append('div') - .attr('class','modal-section') - .append('h3') - .text(_t('splash.welcome')); - - let modalSection = introModal - .append('div') - .attr('class','modal-section'); - - modalSection - .append('p') - .html(_t('splash.text', { - version: context.version, - website: 'ideditor.blog', - github: 'github.com' - })); - - modalSection - .append('p') - .html(_t('splash.privacy', { - updateMessage: updateMessage, - privacyLink: '' + - _t('splash.privacy_policy') + '' - })); - - let buttonWrap = introModal - .append('div') - .attr('class', 'modal-actions'); - - let walkthrough = buttonWrap - .append('button') - .attr('class', 'walkthrough') - .on('click', () => { - context.container().call(uiIntro(context)); - modalSelection.close(); - }); - - walkthrough - .append('svg') - .attr('class', 'logo logo-walkthrough') - .append('use') - .attr('xlink:href', '#iD-logo-walkthrough'); - - walkthrough - .append('div') - .text(_t('splash.walkthrough')); - - let startEditing = buttonWrap - .append('button') - .attr('class', 'start-editing') - .on('click', modalSelection.close); - - startEditing - .append('svg') - .attr('class', 'logo logo-features') - .append('use') - .attr('xlink:href', '#iD-logo-features'); - - startEditing - .append('div') - .text(_t('splash.start')); - - modalSelection.select('button.close') - .attr('class','hide'); - }; - } - - function uiStatus(context) { - var osm = context.connection(); - - - return function(selection) { - if (!osm) return; - - function update(err, apiStatus) { - selection.html(''); - - if (err) { - if (apiStatus === 'connectionSwitched') { - // if the connection was just switched, we can't rely on - // the status (we're getting the status of the previous api) - return; - - } else if (apiStatus === 'rateLimited') { - selection - .text(_t('osm_api_status.message.rateLimit')) - .append('a') - .attr('class', 'api-status-login') - .attr('target', '_blank') - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(_t('login')) - .on('click.login', function() { - event.preventDefault(); - osm.authenticate(); - }); - } else { - - // don't allow retrying too rapidly - var throttledRetry = throttle(function() { - // try loading the visible tiles - context.loadTiles(context.projection); - // manually reload the status too in case all visible tiles were already loaded - osm.reloadApiStatus(); - }, 2000); - - // eslint-disable-next-line no-warning-comments - // TODO: nice messages for different error types - selection - .text(_t('osm_api_status.message.error') + ' ') - .append('a') - // let the user manually retry their connection directly - .text(_t('osm_api_status.retry')) - .on('click.retry', function() { - event.preventDefault(); - throttledRetry(); - }); - } - - } else if (apiStatus === 'readonly') { - selection.text(_t('osm_api_status.message.readonly')); - } else if (apiStatus === 'offline') { - selection.text(_t('osm_api_status.message.offline')); - } - - selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus)); - } - - osm.on('apiStatusChange.uiStatus', update); - - // reload the status periodically regardless of other factors - window.setInterval(function() { - osm.reloadApiStatus(); - }, 90000); - - // load the initial status in case no OSM data was loaded yet - osm.reloadApiStatus(); - }; - } - - function modeDrawArea(context, wayID, startGraph, button) { - var mode = { - button: button, - id: 'draw-area' - }; - - var behavior = behaviorDrawWay(context, wayID, mode, startGraph) - .on('rejectedSelfIntersection.modeDrawArea', function() { - context.ui().flash - .text(_t('self_intersection.error.areas'))(); - }); - - mode.wayID = wayID; - - mode.enter = function() { - context.install(behavior); - }; - - mode.exit = function() { - context.uninstall(behavior); - }; - - mode.selectedIDs = function() { - return [wayID]; - }; - - mode.activeID = function() { - return (behavior && behavior.activeID()) || []; - }; - - return mode; - } - - function modeAddArea(context, mode) { - mode.id = 'add-area'; - - var behavior = behaviorAddWay(context) - .on('start', start) - .on('startFromWay', startFromWay) - .on('startFromNode', startFromNode); - - var defaultTags = { area: 'yes' }; - if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area'); - - - function actionClose(wayId) { - return function (graph) { - return graph.replace(graph.entity(wayId).close()); - }; - } - - - function start(loc) { - var startGraph = context.graph(); - var node = osmNode({ loc: loc }); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(node), - actionAddEntity(way), - actionAddVertex(way.id, node.id), - actionClose(way.id) - ); - - context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); - } - - - function startFromWay(loc, edge) { - var startGraph = context.graph(); - var node = osmNode({ loc: loc }); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(node), - actionAddEntity(way), - actionAddVertex(way.id, node.id), - actionClose(way.id), - actionAddMidpoint({ loc: loc, edge: edge }, node) - ); - - context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); - } - - - function startFromNode(node) { - var startGraph = context.graph(); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(way), - actionAddVertex(way.id, node.id), - actionClose(way.id) - ); - - context.enter(modeDrawArea(context, way.id, startGraph, mode.button)); - } - - - mode.enter = function() { - context.install(behavior); - }; - - - mode.exit = function() { - context.uninstall(behavior); - }; - - - return mode; - } - - function modeAddLine(context, mode) { - mode.id = 'add-line'; - - var behavior = behaviorAddWay(context) - .on('start', start) - .on('startFromWay', startFromWay) - .on('startFromNode', startFromNode); - - var defaultTags = {}; - if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line'); - - - function start(loc) { - var startGraph = context.graph(); - var node = osmNode({ loc: loc }); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(node), - actionAddEntity(way), - actionAddVertex(way.id, node.id) - ); - - context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); - } - - - function startFromWay(loc, edge) { - var startGraph = context.graph(); - var node = osmNode({ loc: loc }); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(node), - actionAddEntity(way), - actionAddVertex(way.id, node.id), - actionAddMidpoint({ loc: loc, edge: edge }, node) - ); - - context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); - } - - - function startFromNode(node) { - var startGraph = context.graph(); - var way = osmWay({ tags: defaultTags }); - - context.perform( - actionAddEntity(way), - actionAddVertex(way.id, node.id) - ); - - context.enter(modeDrawLine(context, way.id, startGraph, mode.button)); - } - - - mode.enter = function() { - context.install(behavior); - }; - - - mode.exit = function() { - context.uninstall(behavior); - }; - - return mode; - } - - function modeAddPoint(context, mode) { - - mode.id = 'add-point'; - - var behavior = behaviorDraw(context) - .on('click', add) - .on('clickWay', addWay) - .on('clickNode', addNode) - .on('cancel', cancel) - .on('finish', cancel); - - var defaultTags = {}; - if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point'); - - - function add(loc) { - var node = osmNode({ loc: loc, tags: defaultTags }); - - context.perform( - actionAddEntity(node), - _t('operations.add.annotation.point') - ); - - enterSelectMode(node); - } - - - function addWay(loc, edge) { - var node = osmNode({ tags: defaultTags }); - - context.perform( - actionAddMidpoint({loc: loc, edge: edge}, node), - _t('operations.add.annotation.vertex') - ); - - enterSelectMode(node); - } - - function enterSelectMode(node) { - context.enter( - modeSelect(context, [node.id]).newFeature(true) - ); - } - - - function addNode(node) { - if (Object.keys(defaultTags).length === 0) { - enterSelectMode(node); - return; - } - - var tags = Object.assign({}, node.tags); // shallow copy - for (var key in defaultTags) { - tags[key] = defaultTags[key]; - } - - context.perform( - actionChangeTags(node.id, tags), - _t('operations.add.annotation.point') - ); - - enterSelectMode(node); - } - - - function cancel() { - context.enter(modeBrowse(context)); - } - - - mode.enter = function() { - context.install(behavior); - }; - - - mode.exit = function() { - context.uninstall(behavior); - }; - - - return mode; - } - - function modeAddNote(context) { - var mode = { - id: 'add-note', - button: 'note', - title: _t('modes.add_note.title'), - description: _t('modes.add_note.description'), - key: _t('modes.add_note.key') - }; - - var behavior = behaviorDraw(context) - .on('click', add) - .on('cancel', cancel) - .on('finish', cancel); - - - function add(loc) { - var osm = services.osm; - if (!osm) return; - - var note = osmNote({ loc: loc, status: 'open', comments: [] }); - osm.replaceNote(note); - - // force a reraw (there is no history change that would otherwise do this) - context.map().pan([0,0]); - - context - .selectedNoteID(note.id) - .enter(modeSelectNote(context, note.id).newFeature(true)); - } - - - function cancel() { - context.enter(modeBrowse(context)); - } - - - mode.enter = function() { - context.install(behavior); - }; - - - mode.exit = function() { - context.uninstall(behavior); - }; - - - return mode; - } - - function uiConflicts(context) { - var dispatch$1 = dispatch('cancel', 'save'); - var keybinding = utilKeybinding('conflicts'); - var _origChanges; - var _conflictList; - var _shownConflictIndex; - - - function keybindingOn() { - select(document) - .call(keybinding.on('⎋', cancel, true)); - } - - function keybindingOff() { - select(document) - .call(keybinding.unbind); - } - - function tryAgain() { - keybindingOff(); - dispatch$1.call('save'); - } - - function cancel() { - keybindingOff(); - dispatch$1.call('cancel'); - } - - - function conflicts(selection) { - keybindingOn(); - - var headerEnter = selection.selectAll('.header') - .data([0]) - .enter() - .append('div') - .attr('class', 'header fillL'); - - headerEnter - .append('button') - .attr('class', 'fr') - .on('click', cancel) - .call(svgIcon('#iD-icon-close')); - - headerEnter - .append('h3') - .text(_t('save.conflict.header')); - - var bodyEnter = selection.selectAll('.body') - .data([0]) - .enter() - .append('div') - .attr('class', 'body fillL'); - - var conflictsHelpEnter = bodyEnter - .append('div') - .attr('class', 'conflicts-help') - .text(_t('save.conflict.help')); - - - // Download changes link - var detected = utilDetect(); - var changeset = new osmChangeset(); - - delete changeset.id; // Export without changeset_id - - var data = JXON.stringify(changeset.osmChangeJXON(_origChanges)); - var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' }); - var fileName = 'changes.osc'; - - var linkEnter = conflictsHelpEnter.selectAll('.download-changes') - .append('a') - .attr('class', 'download-changes'); - - if (detected.download) { // All except IE11 and Edge - linkEnter // download the data as a file - .attr('href', window.URL.createObjectURL(blob)) - .attr('download', fileName); - - } else { // IE11 and Edge - linkEnter // open data uri in a new tab - .attr('target', '_blank') - .on('click.download', function() { - navigator.msSaveBlob(blob, fileName); - }); - } - - linkEnter - .call(svgIcon('#iD-icon-load', 'inline')) - .append('span') - .text(_t('save.conflict.download_changes')); - - - bodyEnter - .append('div') - .attr('class', 'conflict-container fillL3') - .call(showConflict, 0); - - bodyEnter - .append('div') - .attr('class', 'conflicts-done') - .attr('opacity', 0) - .style('display', 'none') - .text(_t('save.conflict.done')); - - var buttonsEnter = bodyEnter - .append('div') - .attr('class','buttons col12 joined conflicts-buttons'); - - buttonsEnter - .append('button') - .attr('disabled', _conflictList.length > 1) - .attr('class', 'action conflicts-button col6') - .text(_t('save.title')) - .on('click.try_again', tryAgain); - - buttonsEnter - .append('button') - .attr('class', 'secondary-action conflicts-button col6') - .text(_t('confirm.cancel')) - .on('click.cancel', cancel); - } - - - function showConflict(selection, index) { - index = utilWrap(index, _conflictList.length); - _shownConflictIndex = index; - - var parent = select(selection.node().parentNode); - - // enable save button if this is the last conflict being reviewed.. - if (index === _conflictList.length - 1) { - window.setTimeout(function() { - parent.select('.conflicts-button') - .attr('disabled', null); - - parent.select('.conflicts-done') - .transition() - .attr('opacity', 1) - .style('display', 'block'); - }, 250); - } - - var conflict = selection - .selectAll('.conflict') - .data([_conflictList[index]]); - - conflict.exit() - .remove(); - - var conflictEnter = conflict.enter() - .append('div') - .attr('class', 'conflict'); - - conflictEnter - .append('h4') - .attr('class', 'conflict-count') - .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length })); - - conflictEnter - .append('a') - .attr('class', 'conflict-description') - .attr('href', '#') - .text(function(d) { return d.name; }) - .on('click', function(d) { - event.preventDefault(); - zoomToEntity(d.id); - }); - - var details = conflictEnter - .append('div') - .attr('class', 'conflict-detail-container'); - - details - .append('ul') - .attr('class', 'conflict-detail-list') - .selectAll('li') - .data(function(d) { return d.details || []; }) - .enter() - .append('li') - .attr('class', 'conflict-detail-item') - .html(function(d) { return d; }); - - details - .append('div') - .attr('class', 'conflict-choices') - .call(addChoices); - - details - .append('div') - .attr('class', 'conflict-nav-buttons joined cf') - .selectAll('button') - .data(['previous', 'next']) - .enter() - .append('button') - .text(function(d) { return _t('save.conflict.' + d); }) - .attr('class', 'conflict-nav-button action col6') - .attr('disabled', function(d, i) { - return (i === 0 && index === 0) || - (i === 1 && index === _conflictList.length - 1) || null; - }) - .on('click', function(d, i) { - event.preventDefault(); - - var container = parent.selectAll('.conflict-container'); - var sign = (i === 0 ? -1 : 1); - - container - .selectAll('.conflict') - .remove(); - - container - .call(showConflict, index + sign); - }); - - } - - - function addChoices(selection) { - var choices = selection - .append('ul') - .attr('class', 'layer-list') - .selectAll('li') - .data(function(d) { return d.choices || []; }); - - // enter - var choicesEnter = choices.enter() - .append('li') - .attr('class', 'layer'); - - var labelEnter = choicesEnter - .append('label'); - - labelEnter - .append('input') - .attr('type', 'radio') - .attr('name', function(d) { return d.id; }) - .on('change', function(d, i) { - var ul = this.parentNode.parentNode.parentNode; - ul.__data__.chosen = i; - choose(ul, d); - }); - - labelEnter - .append('span') - .text(function(d) { return d.text; }); - - // update - choicesEnter - .merge(choices) - .each(function(d, i) { - var ul = this.parentNode; - if (ul.__data__.chosen === i) { - choose(ul, d); - } - }); - } - - - function choose(ul, datum) { - if (event) event.preventDefault(); - - select(ul) - .selectAll('li') - .classed('active', function(d) { return d === datum; }) - .selectAll('input') - .property('checked', function(d) { return d === datum; }); - - var extent = geoExtent(); - var entity; - - entity = context.graph().hasEntity(datum.id); - if (entity) extent._extend(entity.extent(context.graph())); - - datum.action(); - - entity = context.graph().hasEntity(datum.id); - if (entity) extent._extend(entity.extent(context.graph())); - - zoomToEntity(datum.id, extent); - } - - - function zoomToEntity(id, extent) { - context.surface().selectAll('.hover') - .classed('hover', false); - - var entity = context.graph().hasEntity(id); - if (entity) { - if (extent) { - context.map().trimmedExtent(extent); - } else { - context.map().zoomToEase(entity); - } - context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())) - .classed('hover', true); - } - } - - - // The conflict list should be an array of objects like: - // { - // id: id, - // name: entityName(local), - // details: merge.conflicts(), - // chosen: 1, - // choices: [ - // choice(id, keepMine, forceLocal), - // choice(id, keepTheirs, forceRemote) - // ] - // } - conflicts.conflictList = function(_) { - if (!arguments.length) return _conflictList; - _conflictList = _; - return conflicts; - }; - - - conflicts.origChanges = function(_) { - if (!arguments.length) return _origChanges; - _origChanges = _; - return conflicts; - }; - - - conflicts.shownEntityIds = function() { - if (_conflictList && typeof _shownConflictIndex === 'number') { - return [_conflictList[_shownConflictIndex].id]; - } - return []; - }; - - - return utilRebind(conflicts, dispatch$1, 'on'); - } - - function uiConfirm(selection) { - var modalSelection = uiModal(selection); - - modalSelection.select('.modal') - .classed('modal-alert', true); - - var section = modalSelection.select('.content'); - - section.append('div') - .attr('class', 'modal-section header'); - - section.append('div') - .attr('class', 'modal-section message-text'); - - var buttons = section.append('div') - .attr('class', 'modal-section buttons cf'); - - - modalSelection.okButton = function() { - buttons - .append('button') - .attr('class', 'button ok-button action') - .on('click.confirm', function() { - modalSelection.remove(); - }) - .text(_t('confirm.okay')) - .node() - .focus(); - - return modalSelection; - }; - - - return modalSelection; - } - - function uiChangesetEditor(context) { - var dispatch$1 = dispatch('change'); - var formFields = uiFormFields(context); - var commentCombo = uiCombobox(context, 'comment').caseSensitive(true); - var _fieldsArr; - var _tags; - var _changesetID; - - - function changesetEditor(selection) { - render(selection); - } - - - function render(selection) { - var initial = false; - - if (!_fieldsArr) { - initial = true; - var presets = _mainPresetIndex; - - _fieldsArr = [ - uiField(context, presets.field('comment'), null, { show: true, revert: false }), - uiField(context, presets.field('source'), null, { show: false, revert: false }), - uiField(context, presets.field('hashtags'), null, { show: false, revert: false }), - ]; - - _fieldsArr.forEach(function(field) { - field - .on('change', function(t, onInput) { - dispatch$1.call('change', field, undefined, t, onInput); - }); - }); - } - - _fieldsArr.forEach(function(field) { - field - .tags(_tags); - }); - - - selection - .call(formFields.fieldsArr(_fieldsArr)); - - - if (initial) { - var commentField = selection.select('.form-field-comment textarea'); - var commentNode = commentField.node(); - - if (commentNode) { - commentNode.focus(); - commentNode.select(); - } - - // trigger a 'blur' event so that comment field can be cleaned - // and checked for hashtags, even if retrieved from localstorage - utilTriggerEvent(commentField, 'blur'); - - var osm = context.connection(); - if (osm) { - osm.userChangesets(function (err, changesets) { - if (err) return; - - var comments = changesets.map(function(changeset) { - var comment = changeset.tags.comment; - return comment ? { title: comment, value: comment } : null; - }).filter(Boolean); - - commentField - .call(commentCombo - .data(utilArrayUniqBy(comments, 'title')) - ); - }); - } - } - - // Add warning if comment mentions Google - var hasGoogle = _tags.comment.match(/google/i); - var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning') - .data(hasGoogle ? [0] : []); - - commentWarning.exit() - .transition() - .duration(200) - .style('opacity', 0) - .remove(); - - var commentEnter = commentWarning.enter() - .insert('div', '.tag-reference-body') - .attr('class', 'field-warning comment-warning') - .style('opacity', 0); - - commentEnter - .append('a') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-alert', 'inline')) - .attr('href', _t('commit.google_warning_link')) - .append('span') - .text(_t('commit.google_warning')); - - commentEnter - .transition() - .duration(200) - .style('opacity', 1); - } - - - changesetEditor.tags = function(_) { - if (!arguments.length) return _tags; - _tags = _; - // Don't reset _fieldsArr here. - return changesetEditor; - }; - - - changesetEditor.changesetID = function(_) { - if (!arguments.length) return _changesetID; - if (_changesetID === _) return changesetEditor; - _changesetID = _; - _fieldsArr = null; - return changesetEditor; - }; - - - return utilRebind(changesetEditor, dispatch$1, 'on'); - } - - function uiSectionChanges(context) { - var detected = utilDetect(); - - var _discardTags = {}; - _mainFileFetcher.get('discarded') - .then(function(d) { _discardTags = d; }) - .catch(function() { /* ignore */ }); - - var section = uiSection('changes-list', context) - .title(function() { - var history = context.history(); - var summary = history.difference().summary(); - return _t('commit.changes', { count: summary.length }); - }) - .disclosureContent(renderDisclosureContent); - - function renderDisclosureContent(selection) { - var history = context.history(); - var summary = history.difference().summary(); - - var container = selection.selectAll('.commit-section') - .data([0]); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'commit-section'); - - containerEnter - .append('ul') - .attr('class', 'changeset-list'); - - container = containerEnter - .merge(container); - - - var items = container.select('ul').selectAll('li') - .data(summary); - - var itemsEnter = items.enter() - .append('li') - .attr('class', 'change-item'); - - itemsEnter - .each(function(d) { - select(this) - .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType)); - }); - - itemsEnter - .append('span') - .attr('class', 'change-type') - .text(function(d) { return _t('commit.' + d.changeType) + ' '; }); - - itemsEnter - .append('strong') - .attr('class', 'entity-type') - .text(function(d) { - var matched = _mainPresetIndex.match(d.entity, d.graph); - return (matched && matched.name()) || utilDisplayType(d.entity.id); - }); - - itemsEnter - .append('span') - .attr('class', 'entity-name') - .text(function(d) { - var name = utilDisplayName(d.entity) || '', - string = ''; - if (name !== '') { - string += ':'; - } - return string += ' ' + name; - }); - - itemsEnter - .style('opacity', 0) - .transition() - .style('opacity', 1); - - items = itemsEnter - .merge(items); - - items - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .on('click', click); - - - // Download changeset link - var changeset = new osmChangeset().update({ id: undefined }); - var changes = history.changes(actionDiscardTags(history.difference(), _discardTags)); - - delete changeset.id; // Export without chnageset_id - - var data = JXON.stringify(changeset.osmChangeJXON(changes)); - var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'}); - var fileName = 'changes.osc'; - - var linkEnter = container.selectAll('.download-changes') - .data([0]) - .enter() - .append('a') - .attr('class', 'download-changes'); - - if (detected.download) { // All except IE11 and Edge - linkEnter // download the data as a file - .attr('href', window.URL.createObjectURL(blob)) - .attr('download', fileName); - - } else { // IE11 and Edge - linkEnter // open data uri in a new tab - .attr('target', '_blank') - .on('click.download', function() { - navigator.msSaveBlob(blob, fileName); - }); - } - - linkEnter - .call(svgIcon('#iD-icon-load', 'inline')) - .append('span') - .text(_t('commit.download_changes')); - - - function mouseover(d) { - if (d.entity) { - context.surface().selectAll( - utilEntityOrMemberSelector([d.entity.id], context.graph()) - ).classed('hover', true); - } - } - - - function mouseout() { - context.surface().selectAll('.hover') - .classed('hover', false); - } - - - function click(change) { - if (change.changeType !== 'deleted') { - var entity = change.entity; - context.map().zoomToEase(entity); - context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())) - .classed('hover', true); - } - } - } - - return section; - } - - function uiCommitWarnings(context) { - - function commitWarnings(selection) { - var issuesBySeverity = context.validator() - .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true }); - - for (var severity in issuesBySeverity) { - var issues = issuesBySeverity[severity]; - var section = severity + '-section'; - var issueItem = severity + '-item'; - - var container = selection.selectAll('.' + section) - .data(issues.length ? [0] : []); - - container.exit() - .remove(); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'modal-section ' + section + ' fillL2'); - - containerEnter - .append('h3') - .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors')); - - containerEnter - .append('ul') - .attr('class', 'changeset-list'); - - container = containerEnter - .merge(container); - - - var items = container.select('ul').selectAll('li') - .data(issues, function(d) { return d.id; }); - - items.exit() - .remove(); - - var itemsEnter = items.enter() - .append('li') - .attr('class', issueItem); - - itemsEnter - .call(svgIcon('#iD-icon-alert', 'pre-text')); - - itemsEnter - .append('strong') - .attr('class', 'issue-message'); - - itemsEnter.filter(function(d) { return d.tooltip; }) - .call(uiTooltip() - .title(function(d) { return d.tooltip; }) - .placement('top') - ); - - items = itemsEnter - .merge(items); - - items.selectAll('.issue-message') - .text(function(d) { - return d.message(context); - }); - - items - .on('mouseover', function(d) { - if (d.entityIds) { - context.surface().selectAll( - utilEntityOrMemberSelector( - d.entityIds, - context.graph() - ) - ).classed('hover', true); - } - }) - .on('mouseout', function() { - context.surface().selectAll('.hover') - .classed('hover', false); - }) - .on('click', function(d) { - context.validator().focusIssue(d); - }); - } - } - - - return commitWarnings; - } - - var readOnlyTags = [ - /^changesets_count$/, - /^created_by$/, - /^ideditor:/, - /^imagery_used$/, - /^host$/, - /^locale$/, - /^warnings:/, - /^resolved:/, - /^closed:note$/, - /^closed:keepright$/, - /^closed:improveosm:/, - /^closed:osmose:/ - ]; - - // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398 - // from https://stackoverflow.com/a/25575009 - var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g; - - - function uiCommit(context) { - var dispatch$1 = dispatch('cancel'); - var _userDetails; - var _selection; - - var changesetEditor = uiChangesetEditor(context) - .on('change', changeTags); - var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context) - .on('change', changeTags) - .readOnlyTags(readOnlyTags); - var commitChanges = uiSectionChanges(context); - var commitWarnings = uiCommitWarnings(context); - - - function commit(selection) { - _selection = selection; - - // Initialize changeset if one does not exist yet. - if (!context.changeset) initChangeset(); - - loadDerivedChangesetTags(); - - selection.call(render); - } - - function initChangeset() { - - // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899 - var commentDate = +corePreferences('commentDate') || 0; - var currDate = Date.now(); - var cutoff = 2 * 86400 * 1000; // 2 days - if (commentDate > currDate || currDate - commentDate > cutoff) { - corePreferences('comment', null); - corePreferences('hashtags', null); - corePreferences('source', null); - } - - // load in explicitly-set values, if any - if (context.defaultChangesetComment()) { - corePreferences('comment', context.defaultChangesetComment()); - corePreferences('commentDate', Date.now()); - } - if (context.defaultChangesetSource()) { - corePreferences('source', context.defaultChangesetSource()); - corePreferences('commentDate', Date.now()); - } - if (context.defaultChangesetHashtags()) { - corePreferences('hashtags', context.defaultChangesetHashtags()); - corePreferences('commentDate', Date.now()); - } - - var detected = utilDetect(); - var tags = { - comment: corePreferences('comment') || '', - created_by: context.cleanTagValue('iD ' + context.version), - host: context.cleanTagValue(detected.host), - locale: context.cleanTagValue(_mainLocalizer.localeCode()) - }; - - // call findHashtags initially - this will remove stored - // hashtags if any hashtags are found in the comment - #4304 - findHashtags(tags, true); - - var hashtags = corePreferences('hashtags'); - if (hashtags) { - tags.hashtags = hashtags; - } - - var source = corePreferences('source'); - if (source) { - tags.source = source; - } - var photoOverlaysUsed = context.history().photoOverlaysUsed(); - if (photoOverlaysUsed.length) { - var sources = (tags.source || '').split(';'); - - // include this tag for any photo layer - if (sources.indexOf('streetlevel imagery') === -1) { - sources.push('streetlevel imagery'); - } - - // add the photo overlays used during editing as sources - photoOverlaysUsed.forEach(function(photoOverlay) { - if (sources.indexOf(photoOverlay) === -1) { - sources.push(photoOverlay); - } - }); - - tags.source = context.cleanTagValue(sources.join(';')); - } - - context.changeset = new osmChangeset({ tags: tags }); - } - - // Calculates read-only metadata tags based on the user's editing session and applies - // them to the changeset. - function loadDerivedChangesetTags() { - - var osm = context.connection(); - if (!osm) return; - - var tags = Object.assign({}, context.changeset.tags); // shallow copy - - // assign tags for imagery used - var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';')); - tags.imagery_used = imageryUsed || 'None'; - - // assign tags for closed issues and notes - var osmClosed = osm.getClosedIDs(); - var itemType; - if (osmClosed.length) { - tags['closed:note'] = context.cleanTagValue(osmClosed.join(';')); - } - if (services.keepRight) { - var krClosed = services.keepRight.getClosedIDs(); - if (krClosed.length) { - tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';')); - } - } - if (services.improveOSM) { - var iOsmClosed = services.improveOSM.getClosedCounts(); - for (itemType in iOsmClosed) { - tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString()); - } - } - if (services.osmose) { - var osmoseClosed = services.osmose.getClosedCounts(); - for (itemType in osmoseClosed) { - tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString()); - } - } - - // remove existing issue counts - for (var key in tags) { - if (key.match(/(^warnings:)|(^resolved:)/)) { - delete tags[key]; - } - } - - function addIssueCounts(issues, prefix) { - var issuesByType = utilArrayGroupBy(issues, 'type'); - for (var issueType in issuesByType) { - var issuesOfType = issuesByType[issueType]; - if (issuesOfType[0].subtype) { - var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype'); - for (var issueSubtype in issuesBySubtype) { - var issuesOfSubtype = issuesBySubtype[issueSubtype]; - tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString()); - } - } else { - tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString()); - } - } - } - - // add counts of warnings generated by the user's edits - var warnings = context.validator() - .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning; - addIssueCounts(warnings, 'warnings'); - - // add counts of issues resolved by the user's edits - var resolvedIssues = context.validator().getResolvedIssues(); - addIssueCounts(resolvedIssues, 'resolved'); - - context.changeset = context.changeset.update({ tags: tags }); - } - - function render(selection) { - - var osm = context.connection(); - if (!osm) return; - - var header = selection.selectAll('.header') - .data([0]); - - var headerTitle = header.enter() - .append('div') - .attr('class', 'header fillL header-container'); - - headerTitle - .append('div') - .attr('class', 'header-block header-block-outer'); - - headerTitle - .append('div') - .attr('class', 'header-block') - .append('h3') - .text(_t('commit.title')); - - headerTitle - .append('div') - .attr('class', 'header-block header-block-outer header-block-close') - .append('button') - .attr('class', 'close') - .on('click', function() { - dispatch$1.call('cancel', this); - }) - .call(svgIcon('#iD-icon-close')); - - var body = selection.selectAll('.body') - .data([0]); - - body = body.enter() - .append('div') - .attr('class', 'body') - .merge(body); - - - // Changeset Section - var changesetSection = body.selectAll('.changeset-editor') - .data([0]); - - changesetSection = changesetSection.enter() - .append('div') - .attr('class', 'modal-section changeset-editor') - .merge(changesetSection); - - changesetSection - .call(changesetEditor - .changesetID(context.changeset.id) - .tags(context.changeset.tags) - ); - - - // Warnings - body.call(commitWarnings); - - - // Upload Explanation - var saveSection = body.selectAll('.save-section') - .data([0]); - - saveSection = saveSection.enter() - .append('div') - .attr('class','modal-section save-section fillL') - .merge(saveSection); - - var prose = saveSection.selectAll('.commit-info') - .data([0]); - - if (prose.enter().size()) { // first time, make sure to update user details in prose - _userDetails = null; - } - - prose = prose.enter() - .append('p') - .attr('class', 'commit-info') - .text(_t('commit.upload_explanation')) - .merge(prose); - - // always check if this has changed, but only update prose.html() - // if needed, because it can trigger a style recalculation - osm.userDetails(function(err, user) { - if (err) return; - - if (_userDetails === user) return; // no change - _userDetails = user; - - var userLink = select(document.createElement('div')); - - if (user.image_url) { - userLink - .append('img') - .attr('src', user.image_url) - .attr('class', 'icon pre-text user-icon'); - } - - userLink - .append('a') - .attr('class', 'user-info') - .text(user.display_name) - .attr('href', osm.userURL(user.display_name)) - .attr('target', '_blank'); - - prose - .html(_t('commit.upload_explanation_with_user', { user: userLink.html() })); - }); - - - // Request Review - var requestReview = saveSection.selectAll('.request-review') - .data([0]); - - // Enter - var requestReviewEnter = requestReview.enter() - .append('div') - .attr('class', 'request-review'); - - var requestReviewDomId = utilUniqueDomId('commit-input-request-review'); - - var labelEnter = requestReviewEnter - .append('label') - .attr('for', requestReviewDomId); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .attr('id', requestReviewDomId); - - labelEnter - .append('span') - .text(_t('commit.request_review')); - - // Update - requestReview = requestReview - .merge(requestReviewEnter); - - var requestReviewInput = requestReview.selectAll('input') - .property('checked', isReviewRequested(context.changeset.tags)) - .on('change', toggleRequestReview); - - - // Buttons - var buttonSection = saveSection.selectAll('.buttons') - .data([0]); - - // enter - var buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'buttons fillL'); - - buttonEnter - .append('button') - .attr('class', 'secondary-action button cancel-button') - .append('span') - .attr('class', 'label') - .text(_t('commit.cancel')); - - var uploadButton = buttonEnter - .append('button') - .attr('class', 'action button save-button'); - - uploadButton.append('span') - .attr('class', 'label') - .text(_t('commit.save')); - - var uploadBlockerTooltipText = getUploadBlockerMessage(); - - // update - buttonSection = buttonSection - .merge(buttonEnter); - - buttonSection.selectAll('.cancel-button') - .on('click.cancel', function() { - dispatch$1.call('cancel', this); - }); - - buttonSection.selectAll('.save-button') - .classed('disabled', uploadBlockerTooltipText !== null) - .on('click.save', function() { - if (!select(this).classed('disabled')) { - this.blur(); // avoid keeping focus on the button - #4641 - context.uploader().save(context.changeset); - } - }); - - // remove any existing tooltip - uiTooltip().destroyAny(buttonSection.selectAll('.save-button')); - - if (uploadBlockerTooltipText) { - buttonSection.selectAll('.save-button') - .call(uiTooltip().title(uploadBlockerTooltipText).placement('top')); - } - - // Raw Tag Editor - var tagSection = body.selectAll('.tag-section.raw-tag-editor') - .data([0]); - - tagSection = tagSection.enter() - .append('div') - .attr('class', 'modal-section tag-section raw-tag-editor') - .merge(tagSection); - - tagSection - .call(rawTagEditor - .tags(Object.assign({}, context.changeset.tags)) // shallow copy - .render - ); - - var changesSection = body.selectAll('.commit-changes-section') - .data([0]); - - changesSection = changesSection.enter() - .append('div') - .attr('class', 'modal-section commit-changes-section') - .merge(changesSection); - - // Change summary - changesSection.call(commitChanges.render); - - - function toggleRequestReview() { - var rr = requestReviewInput.property('checked'); - updateChangeset({ review_requested: (rr ? 'yes' : undefined) }); - - tagSection - .call(rawTagEditor - .tags(Object.assign({}, context.changeset.tags)) // shallow copy - .render - ); - } - } - - - function getUploadBlockerMessage() { - var errors = context.validator() - .getIssuesBySeverity({ what: 'edited', where: 'all' }).error; - - if (errors.length) { - return _t('commit.outstanding_errors_message', { count: errors.length }); - - } else { - var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length; - if (!hasChangesetComment) { - return _t('commit.comment_needed_message'); - } - } - return null; - } - - - function changeTags(_, changed, onInput) { - if (changed.hasOwnProperty('comment')) { - if (changed.comment === undefined) { - changed.comment = ''; - } - if (!onInput) { - corePreferences('comment', changed.comment); - corePreferences('commentDate', Date.now()); - } - } - if (changed.hasOwnProperty('source')) { - if (changed.source === undefined) { - corePreferences('source', null); - } else if (!onInput) { - corePreferences('source', changed.source); - corePreferences('commentDate', Date.now()); - } - } - // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset` - - updateChangeset(changed, onInput); - - if (_selection) { - _selection.call(render); - } - } - - - function findHashtags(tags, commentOnly) { - var detectedHashtags = commentHashtags(); - - if (detectedHashtags.length) { - // always remove stored hashtags if there are hashtags in the comment - #4304 - corePreferences('hashtags', null); - } - if (!detectedHashtags.length || !commentOnly) { - detectedHashtags = detectedHashtags.concat(hashtagHashtags()); - } - - var allLowerCase = new Set(); - return detectedHashtags.filter(function(hashtag) { - // Compare tags as lowercase strings, but keep original case tags - var lowerCase = hashtag.toLowerCase(); - if (!allLowerCase.has(lowerCase)) { - allLowerCase.add(lowerCase); - return true; - } - return false; - }); - - // Extract hashtags from `comment` - function commentHashtags() { - var matches = (tags.comment || '') - .replace(/http\S*/g, '') // drop anything that looks like a URL - #4289 - .match(hashtagRegex); - - return matches || []; - } - - // Extract and clean hashtags from `hashtags` - function hashtagHashtags() { - var matches = (tags.hashtags || '') - .split(/[,;\s]+/) - .map(function (s) { - if (s[0] !== '#') { s = '#' + s; } // prepend '#' - var matched = s.match(hashtagRegex); - return matched && matched[0]; - }).filter(Boolean); // exclude falsy - - return matches || []; - } - } - - - function isReviewRequested(tags) { - var rr = tags.review_requested; - if (rr === undefined) return false; - rr = rr.trim().toLowerCase(); - return !(rr === '' || rr === 'no'); - } - - - function updateChangeset(changed, onInput) { - var tags = Object.assign({}, context.changeset.tags); // shallow copy - - Object.keys(changed).forEach(function(k) { - var v = changed[k]; - k = context.cleanTagKey(k); - if (readOnlyTags.indexOf(k) !== -1) return; - - if (k !== '' && v !== undefined) { - if (onInput) { - tags[k] = v; - } else { - tags[k] = context.cleanTagValue(v); - } - } else { - delete tags[k]; - } - }); - - if (!onInput) { - // when changing the comment, override hashtags with any found in comment. - var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== ''); - var arr = findHashtags(tags, commentOnly); - if (arr.length) { - tags.hashtags = context.cleanTagValue(arr.join(';')); - corePreferences('hashtags', tags.hashtags); - } else { - delete tags.hashtags; - corePreferences('hashtags', null); - } - } - - // always update userdetails, just in case user reauthenticates as someone else - if (_userDetails && _userDetails.changesets_count !== undefined) { - var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283 - tags.changesets_count = String(changesetsCount); - - // first 100 edits - new user - if (changesetsCount <= 100) { - var s; - s = corePreferences('walkthrough_completed'); - if (s) { - tags['ideditor:walkthrough_completed'] = s; - } - - s = corePreferences('walkthrough_progress'); - if (s) { - tags['ideditor:walkthrough_progress'] = s; - } - - s = corePreferences('walkthrough_started'); - if (s) { - tags['ideditor:walkthrough_started'] = s; - } - } - } else { - delete tags.changesets_count; - } - - if (!fastDeepEqual(context.changeset.tags, tags)) { - context.changeset = context.changeset.update({ tags: tags }); - } - } - - - commit.reset = function() { - context.changeset = null; - }; - - - return utilRebind(commit, dispatch$1, 'on'); - } - - var RADIUS = 6378137; - var FLATTENING = 1/298.257223563; - var POLAR_RADIUS$1 = 6356752.3142; - - var wgs84 = { - RADIUS: RADIUS, - FLATTENING: FLATTENING, - POLAR_RADIUS: POLAR_RADIUS$1 - }; - - var geometry_1 = geometry; - var ring = ringArea; - - function geometry(_) { - var area = 0, i; - switch (_.type) { - case 'Polygon': - return polygonArea(_.coordinates); - case 'MultiPolygon': - for (i = 0; i < _.coordinates.length; i++) { - area += polygonArea(_.coordinates[i]); - } - return area; - case 'Point': - case 'MultiPoint': - case 'LineString': - case 'MultiLineString': - return 0; - case 'GeometryCollection': - for (i = 0; i < _.geometries.length; i++) { - area += geometry(_.geometries[i]); - } - return area; - } - } - - function polygonArea(coords) { - var area = 0; - if (coords && coords.length > 0) { - area += Math.abs(ringArea(coords[0])); - for (var i = 1; i < coords.length; i++) { - area -= Math.abs(ringArea(coords[i])); - } - } - return area; - } - - /** - * Calculate the approximate area of the polygon were it projected onto - * the earth. Note that this area will be positive if ring is oriented - * clockwise, otherwise it will be negative. - * - * Reference: - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for - * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 - * - * Returns: - * {float} The approximate signed geodesic area of the polygon in square - * meters. - */ - - function ringArea(coords) { - var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i, - area = 0, - coordsLength = coords.length; - - if (coordsLength > 2) { - for (i = 0; i < coordsLength; i++) { - if (i === coordsLength - 2) {// i = N-2 - lowerIndex = coordsLength - 2; - middleIndex = coordsLength -1; - upperIndex = 0; - } else if (i === coordsLength - 1) {// i = N-1 - lowerIndex = coordsLength - 1; - middleIndex = 0; - upperIndex = 1; - } else { // i = 0 to N-3 - lowerIndex = i; - middleIndex = i+1; - upperIndex = i+2; - } - p1 = coords[lowerIndex]; - p2 = coords[middleIndex]; - p3 = coords[upperIndex]; - area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1])); - } - - area = area * wgs84.RADIUS * wgs84.RADIUS / 2; - } - - return area; - } - - function rad(_) { - return _ * Math.PI / 180; - } - - var geojsonArea = { - geometry: geometry_1, - ring: ring - }; - - function toRadians(angleInDegrees) { - return (angleInDegrees * Math.PI) / 180; - } - - function toDegrees(angleInRadians) { - return (angleInRadians * 180) / Math.PI; - } - - function offset(c1, distance, bearing) { - var lat1 = toRadians(c1[1]); - var lon1 = toRadians(c1[0]); - var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84 - var lat = Math.asin( - Math.sin(lat1) * Math.cos(dByR) + - Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing) - ); - var lon = - lon1 + - Math.atan2( - Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), - Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat) - ); - return [toDegrees(lon), toDegrees(lat)]; - } - - function validateCenter(center) { - const validCenterLengths = [2, 3]; - if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) { - throw new Error("ERROR! Center has to be an array of length two or three"); - } - const [lng, lat] = center; - if (typeof lng !== "number" || typeof lat !== "number") { - throw new Error( - `ERROR! Longitude and Latitude has to be numbers but where ${typeof lng} and ${typeof lat}` - ); - } - if (lng > 180 || lng < -180) { - throw new Error( - `ERROR! Longitude has to be between -180 and 180 but was ${lng}` - ); - } - - if (lat > 90 || lat < -90) { - throw new Error( - `ERROR! Latitude has to be between -90 and 90 but was ${lat}` - ); - } - } - - function validateRadius(radius) { - if (typeof radius !== "number") { - throw new Error( - `ERROR! Radius has to be a positive number but was: ${typeof radius}` - ); - } - - if (radius <= 0) { - throw new Error( - `ERROR! Radius has to be a positive number but was: ${radius}` - ); - } - } - - function validateNumberOfSegments(numberOfSegments) { - if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) { - throw new Error( - `ERROR! Number of segments has to be a number but was: ${typeof numberOfSegments}` - ); - } - - if (numberOfSegments < 3) { - throw new Error( - `ERROR! Number of segments has to be at least 3 but was: ${numberOfSegments}` - ); - } - } - - function validateInput({ center, radius, numberOfSegments }) { - validateCenter(center); - validateRadius(radius); - validateNumberOfSegments(numberOfSegments); - } - - var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) { - var n = numberOfSegments ? numberOfSegments : 32; - - // validateInput() throws error on invalid input and do nothing on valid input - validateInput({ center, radius, numberOfSegments }); - - var coordinates = []; - for (var i = 0; i < n; ++i) { - coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n)); - } - coordinates.push(coordinates[0]); - - return { - type: "Polygon", - coordinates: [coordinates] - }; - }; - - var geojsonPrecision = createCommonjsModule(function (module) { - (function() { - - function parse(t, coordinatePrecision, extrasPrecision) { - - function point(p) { - return p.map(function(e, index) { - if (index < 2) { - return 1 * e.toFixed(coordinatePrecision); - } else { - return 1 * e.toFixed(extrasPrecision); - } - }); - } - - function multi(l) { - return l.map(point); - } - - function poly(p) { - return p.map(multi); - } - - function multiPoly(m) { - return m.map(poly); - } - - function geometry(obj) { - if (!obj) { - return {}; - } - - switch (obj.type) { - case "Point": - obj.coordinates = point(obj.coordinates); - return obj; - case "LineString": - case "MultiPoint": - obj.coordinates = multi(obj.coordinates); - return obj; - case "Polygon": - case "MultiLineString": - obj.coordinates = poly(obj.coordinates); - return obj; - case "MultiPolygon": - obj.coordinates = multiPoly(obj.coordinates); - return obj; - case "GeometryCollection": - obj.geometries = obj.geometries.map(geometry); - return obj; - default : - return {}; - } - } - - function feature(obj) { - obj.geometry = geometry(obj.geometry); - return obj - } - - function featureCollection(f) { - f.features = f.features.map(feature); - return f; - } - - function geometryCollection(g) { - g.geometries = g.geometries.map(geometry); - return g; - } - - if (!t) { - return t; - } - - switch (t.type) { - case "Feature": - return feature(t); - case "GeometryCollection" : - return geometryCollection(t); - case "FeatureCollection" : - return featureCollection(t); - case "Point": - case "LineString": - case "Polygon": - case "MultiPoint": - case "MultiPolygon": - case "MultiLineString": - return geometry(t); - default : - return t; - } - - } - - module.exports = parse; - module.exports.parse = parse; - - }()); - }); - - /* Polyfill service v3.13.0 - * For detailed credits and licence information see http://github.com/financial-times/polyfill-service - * - * - Array.prototype.fill, License: CC0 */ - - if (!('fill' in Array.prototype)) { - Object.defineProperty(Array.prototype, 'fill', { - configurable: true, - value: function fill (value) { - if (this === undefined || this === null) { - throw new TypeError(this + ' is not an object') - } - - var arrayLike = Object(this); - - var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0; - - var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0; - - relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length); - - var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length; - - relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length); - - while (relativeStart < relativeEnd) { - arrayLike[relativeStart] = value; - - ++relativeStart; - } - - return arrayLike - }, - writable: true - }); - } - - /** - * Polyfill for IE support - */ - Number.isFinite = Number.isFinite || function (value) { - return typeof value === 'number' && isFinite(value) - }; - - Number.isInteger = Number.isInteger || function (val) { - return typeof val === 'number' && - isFinite(val) && - Math.floor(val) === val - }; - - Number.parseFloat = Number.parseFloat || parseFloat; - - Number.isNaN = Number.isNaN || function (value) { - return value !== value // eslint-disable-line - }; - - /** - * Polyfill for IE support - */ - Math.trunc = Math.trunc || function (x) { - return x < 0 ? Math.ceil(x) : Math.floor(x) - }; - - var NumberUtil = function NumberUtil () {}; - - NumberUtil.prototype.interfaces_ = function interfaces_ () { - return [] - }; - NumberUtil.prototype.getClass = function getClass () { - return NumberUtil - }; - NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) { - return Math.abs(x1 - x2) <= tolerance - }; - - var IllegalArgumentException = (function (Error) { - function IllegalArgumentException (message) { - Error.call(this, message); - this.name = 'IllegalArgumentException'; - this.message = message; - this.stack = (new Error()).stack; - } - - if ( Error ) IllegalArgumentException.__proto__ = Error; - IllegalArgumentException.prototype = Object.create( Error && Error.prototype ); - IllegalArgumentException.prototype.constructor = IllegalArgumentException; - - return IllegalArgumentException; - }(Error)); - - var Double = function Double () {}; - - var staticAccessors$1 = { MAX_VALUE: { configurable: true } }; - - Double.isNaN = function isNaN (n) { return Number.isNaN(n) }; - Double.doubleToLongBits = function doubleToLongBits (n) { return n }; - Double.longBitsToDouble = function longBitsToDouble (n) { return n }; - Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) }; - staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE }; - - Object.defineProperties( Double, staticAccessors$1 ); - - var Comparable = function Comparable () {}; - - var Clonable = function Clonable () {}; - - var Comparator = function Comparator () {}; - - function Serializable () {} - - // import Assert from '../util/Assert' - - var Coordinate = function Coordinate () { - this.x = null; - this.y = null; - this.z = null; - if (arguments.length === 0) { - this.x = 0.0; - this.y = 0.0; - this.z = Coordinate.NULL_ORDINATE; - } else if (arguments.length === 1) { - var c = arguments[0]; - this.x = c.x; - this.y = c.y; - this.z = c.z; - } else if (arguments.length === 2) { - this.x = arguments[0]; - this.y = arguments[1]; - this.z = Coordinate.NULL_ORDINATE; - } else if (arguments.length === 3) { - this.x = arguments[0]; - this.y = arguments[1]; - this.z = arguments[2]; - } - }; - - var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } }; - Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) { - switch (ordinateIndex) { - case Coordinate.X: - this.x = value; - break - case Coordinate.Y: - this.y = value; - break - case Coordinate.Z: - this.z = value; - break - default: - throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex) - } - }; - Coordinate.prototype.equals2D = function equals2D () { - if (arguments.length === 1) { - var other = arguments[0]; - if (this.x !== other.x) { - return false - } - if (this.y !== other.y) { - return false - } - return true - } else if (arguments.length === 2) { - var c = arguments[0]; - var tolerance = arguments[1]; - if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) { - return false - } - if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) { - return false - } - return true - } - }; - Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) { - switch (ordinateIndex) { - case Coordinate.X: - return this.x - case Coordinate.Y: - return this.y - case Coordinate.Z: - return this.z - } - throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex) - }; - Coordinate.prototype.equals3D = function equals3D (other) { - return this.x === other.x && - this.y === other.y && - ((this.z === other.z || Double.isNaN(this.z)) && - Double.isNaN(other.z)) - }; - Coordinate.prototype.equals = function equals (other) { - if (!(other instanceof Coordinate)) { - return false - } - return this.equals2D(other) - }; - Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) { - return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance) - }; - Coordinate.prototype.compareTo = function compareTo (o) { - var other = o; - if (this.x < other.x) { return -1 } - if (this.x > other.x) { return 1 } - if (this.y < other.y) { return -1 } - if (this.y > other.y) { return 1 } - return 0 - }; - Coordinate.prototype.clone = function clone () { - // try { - // var coord = null - // return coord - // } catch (e) { - // if (e instanceof CloneNotSupportedException) { - // Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable") - // return null - // } else throw e - // } finally {} - }; - Coordinate.prototype.copy = function copy () { - return new Coordinate(this) - }; - Coordinate.prototype.toString = function toString () { - return '(' + this.x + ', ' + this.y + ', ' + this.z + ')' - }; - Coordinate.prototype.distance3D = function distance3D (c) { - var dx = this.x - c.x; - var dy = this.y - c.y; - var dz = this.z - c.z; - return Math.sqrt(dx * dx + dy * dy + dz * dz) - }; - Coordinate.prototype.distance = function distance (c) { - var dx = this.x - c.x; - var dy = this.y - c.y; - return Math.sqrt(dx * dx + dy * dy) - }; - Coordinate.prototype.hashCode = function hashCode () { - var result = 17; - result = 37 * result + Coordinate.hashCode(this.x); - result = 37 * result + Coordinate.hashCode(this.y); - return result - }; - Coordinate.prototype.setCoordinate = function setCoordinate (other) { - this.x = other.x; - this.y = other.y; - this.z = other.z; - }; - Coordinate.prototype.interfaces_ = function interfaces_ () { - return [Comparable, Clonable, Serializable] - }; - Coordinate.prototype.getClass = function getClass () { - return Coordinate - }; - Coordinate.hashCode = function hashCode () { - if (arguments.length === 1) { - var x = arguments[0]; - var f = Double.doubleToLongBits(x); - return Math.trunc((f ^ f) >>> 32) - } - }; - staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator }; - staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 }; - staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN }; - staticAccessors.X.get = function () { return 0 }; - staticAccessors.Y.get = function () { return 1 }; - staticAccessors.Z.get = function () { return 2 }; - - Object.defineProperties( Coordinate, staticAccessors ); - - var DimensionalComparator = function DimensionalComparator (dimensionsToTest) { - this._dimensionsToTest = 2; - if (arguments.length === 0) ; else if (arguments.length === 1) { - var dimensionsToTest$1 = arguments[0]; - if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') } - this._dimensionsToTest = dimensionsToTest$1; - } - }; - DimensionalComparator.prototype.compare = function compare (o1, o2) { - var c1 = o1; - var c2 = o2; - var compX = DimensionalComparator.compare(c1.x, c2.x); - if (compX !== 0) { return compX } - var compY = DimensionalComparator.compare(c1.y, c2.y); - if (compY !== 0) { return compY } - if (this._dimensionsToTest <= 2) { return 0 } - var compZ = DimensionalComparator.compare(c1.z, c2.z); - return compZ - }; - DimensionalComparator.prototype.interfaces_ = function interfaces_ () { - return [Comparator] - }; - DimensionalComparator.prototype.getClass = function getClass () { - return DimensionalComparator - }; - DimensionalComparator.compare = function compare (a, b) { - if (a < b) { return -1 } - if (a > b) { return 1 } - if (Double.isNaN(a)) { - if (Double.isNaN(b)) { return 0 } - return -1 - } - if (Double.isNaN(b)) { return 1 } - return 0 - }; - - // import hasInterface from '../../../../hasInterface' - // import CoordinateSequence from './CoordinateSequence' - - var CoordinateSequenceFactory = function CoordinateSequenceFactory () {}; - - CoordinateSequenceFactory.prototype.create = function create () { - // if (arguments.length === 1) { - // if (arguments[0] instanceof Array) { - // let coordinates = arguments[0] - // } else if (hasInterface(arguments[0], CoordinateSequence)) { - // let coordSeq = arguments[0] - // } - // } else if (arguments.length === 2) { - // let size = arguments[0] - // let dimension = arguments[1] - // } - }; - CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateSequenceFactory.prototype.getClass = function getClass () { - return CoordinateSequenceFactory - }; - - var Location = function Location () {}; - - var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } }; - - Location.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Location.prototype.getClass = function getClass () { - return Location - }; - Location.toLocationSymbol = function toLocationSymbol (locationValue) { - switch (locationValue) { - case Location.EXTERIOR: - return 'e' - case Location.BOUNDARY: - return 'b' - case Location.INTERIOR: - return 'i' - case Location.NONE: - return '-' - } - throw new IllegalArgumentException('Unknown location value: ' + locationValue) - }; - staticAccessors$4.INTERIOR.get = function () { return 0 }; - staticAccessors$4.BOUNDARY.get = function () { return 1 }; - staticAccessors$4.EXTERIOR.get = function () { return 2 }; - staticAccessors$4.NONE.get = function () { return -1 }; - - Object.defineProperties( Location, staticAccessors$4 ); - - var hasInterface = function (o, i) { - return o.interfaces_ && o.interfaces_().indexOf(i) > -1 - }; - - var MathUtil = function MathUtil () {}; - - var staticAccessors$5 = { LOG_10: { configurable: true } }; - - MathUtil.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MathUtil.prototype.getClass = function getClass () { - return MathUtil - }; - MathUtil.log10 = function log10 (x) { - var ln = Math.log(x); - if (Double.isInfinite(ln)) { return ln } - if (Double.isNaN(ln)) { return ln } - return ln / MathUtil.LOG_10 - }; - MathUtil.min = function min (v1, v2, v3, v4) { - var min = v1; - if (v2 < min) { min = v2; } - if (v3 < min) { min = v3; } - if (v4 < min) { min = v4; } - return min - }; - MathUtil.clamp = function clamp () { - if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) { - var x = arguments[0]; - var min = arguments[1]; - var max = arguments[2]; - if (x < min) { return min } - if (x > max) { return max } - return x - } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) { - var x$1 = arguments[0]; - var min$1 = arguments[1]; - var max$1 = arguments[2]; - if (x$1 < min$1) { return min$1 } - if (x$1 > max$1) { return max$1 } - return x$1 - } - }; - MathUtil.wrap = function wrap (index, max) { - if (index < 0) { - return max - -index % max - } - return index % max - }; - MathUtil.max = function max () { - if (arguments.length === 3) { - var v1 = arguments[0]; - var v2 = arguments[1]; - var v3 = arguments[2]; - var max = v1; - if (v2 > max) { max = v2; } - if (v3 > max) { max = v3; } - return max - } else if (arguments.length === 4) { - var v1$1 = arguments[0]; - var v2$1 = arguments[1]; - var v3$1 = arguments[2]; - var v4 = arguments[3]; - var max$1 = v1$1; - if (v2$1 > max$1) { max$1 = v2$1; } - if (v3$1 > max$1) { max$1 = v3$1; } - if (v4 > max$1) { max$1 = v4; } - return max$1 - } - }; - MathUtil.average = function average (x1, x2) { - return (x1 + x2) / 2.0 - }; - staticAccessors$5.LOG_10.get = function () { return Math.log(10) }; - - Object.defineProperties( MathUtil, staticAccessors$5 ); - - var StringBuffer = function StringBuffer (str) { - this.str = str; - }; - StringBuffer.prototype.append = function append (e) { - this.str += e; - }; - - StringBuffer.prototype.setCharAt = function setCharAt (i, c) { - this.str = this.str.substr(0, i) + c + this.str.substr(i + 1); - }; - - StringBuffer.prototype.toString = function toString (e) { - return this.str - }; - - var Integer = function Integer (value) { - this.value = value; - }; - Integer.prototype.intValue = function intValue () { - return this.value - }; - Integer.prototype.compareTo = function compareTo (o) { - if (this.value < o) { return -1 } - if (this.value > o) { return 1 } - return 0 - }; - Integer.isNaN = function isNaN (n) { return Number.isNaN(n) }; - - var Character = function Character () {}; - - Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) }; - Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() }; - - var DD = function DD () { - this._hi = 0.0; - this._lo = 0.0; - if (arguments.length === 0) { - this.init(0.0); - } else if (arguments.length === 1) { - if (typeof arguments[0] === 'number') { - var x = arguments[0]; - this.init(x); - } else if (arguments[0] instanceof DD) { - var dd = arguments[0]; - this.init(dd); - } else if (typeof arguments[0] === 'string') { - var str = arguments[0]; - DD.call(this, DD.parse(str)); - } - } else if (arguments.length === 2) { - var hi = arguments[0]; - var lo = arguments[1]; - this.init(hi, lo); - } - }; - - var staticAccessors$7 = { PI: { configurable: true },TWO_PI: { configurable: true },PI_2: { configurable: true },E: { configurable: true },NaN: { configurable: true },EPS: { configurable: true },SPLIT: { configurable: true },MAX_PRINT_DIGITS: { configurable: true },TEN: { configurable: true },ONE: { configurable: true },SCI_NOT_EXPONENT_CHAR: { configurable: true },SCI_NOT_ZERO: { configurable: true } }; - DD.prototype.le = function le (y) { - return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo - }; - DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) { - var y = this.abs(); - var mag = DD.magnitude(y._hi); - var scale = DD.TEN.pow(mag); - y = y.divide(scale); - if (y.gt(DD.TEN)) { - y = y.divide(DD.TEN); - mag += 1; - } else if (y.lt(DD.ONE)) { - y = y.multiply(DD.TEN); - mag -= 1; - } - var decimalPointPos = mag + 1; - var buf = new StringBuffer(); - var numDigits = DD.MAX_PRINT_DIGITS - 1; - for (var i = 0; i <= numDigits; i++) { - if (insertDecimalPoint && i === decimalPointPos) { - buf.append('.'); - } - var digit = Math.trunc(y._hi); - if (digit < 0) { - break - } - var rebiasBy10 = false; - var digitChar = 0; - if (digit > 9) { - rebiasBy10 = true; - digitChar = '9'; - } else { - digitChar = '0' + digit; - } - buf.append(digitChar); - y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN); - if (rebiasBy10) { y.selfAdd(DD.TEN); } - var continueExtractingDigits = true; - var remMag = DD.magnitude(y._hi); - if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; } - if (!continueExtractingDigits) { break } - } - magnitude[0] = mag; - return buf.toString() - }; - DD.prototype.sqr = function sqr () { - return this.multiply(this) - }; - DD.prototype.doubleValue = function doubleValue () { - return this._hi + this._lo - }; - DD.prototype.subtract = function subtract () { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - return this.add(y.negate()) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - return this.add(-y$1) - } - }; - DD.prototype.equals = function equals () { - if (arguments.length === 1) { - var y = arguments[0]; - return this._hi === y._hi && this._lo === y._lo - } - }; - DD.prototype.isZero = function isZero () { - return this._hi === 0.0 && this._lo === 0.0 - }; - DD.prototype.selfSubtract = function selfSubtract () { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - if (this.isNaN()) { return this } - return this.selfAdd(-y._hi, -y._lo) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - if (this.isNaN()) { return this } - return this.selfAdd(-y$1, 0.0) - } - }; - DD.prototype.getSpecialNumberString = function getSpecialNumberString () { - if (this.isZero()) { return '0.0' } - if (this.isNaN()) { return 'NaN ' } - return null - }; - DD.prototype.min = function min (x) { - if (this.le(x)) { - return this - } else { - return x - } - }; - DD.prototype.selfDivide = function selfDivide () { - if (arguments.length === 1) { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - return this.selfDivide(y._hi, y._lo) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - return this.selfDivide(y$1, 0.0) - } - } else if (arguments.length === 2) { - var yhi = arguments[0]; - var ylo = arguments[1]; - var hc = null; - var tc = null; - var hy = null; - var ty = null; - var C = null; - var c = null; - var U = null; - var u = null; - C = this._hi / yhi; - c = DD.SPLIT * C; - hc = c - C; - u = DD.SPLIT * yhi; - hc = c - hc; - tc = C - hc; - hy = u - yhi; - U = C * yhi; - hy = u - hy; - ty = yhi - hy; - u = hc * hy - U + hc * ty + tc * hy + tc * ty; - c = (this._hi - U - u + this._lo - C * ylo) / yhi; - u = C + c; - this._hi = u; - this._lo = C - u + c; - return this - } - }; - DD.prototype.dump = function dump () { - return 'DD<' + this._hi + ', ' + this._lo + '>' - }; - DD.prototype.divide = function divide () { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - var hc = null; - var tc = null; - var hy = null; - var ty = null; - var C = null; - var c = null; - var U = null; - var u = null; - C = this._hi / y._hi; - c = DD.SPLIT * C; - hc = c - C; - u = DD.SPLIT * y._hi; - hc = c - hc; - tc = C - hc; - hy = u - y._hi; - U = C * y._hi; - hy = u - hy; - ty = y._hi - hy; - u = hc * hy - U + hc * ty + tc * hy + tc * ty; - c = (this._hi - U - u + this._lo - C * y._lo) / y._hi; - u = C + c; - var zhi = u; - var zlo = C - u + c; - return new DD(zhi, zlo) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - if (Double.isNaN(y$1)) { return DD.createNaN() } - return DD.copy(this).selfDivide(y$1, 0.0) - } - }; - DD.prototype.ge = function ge (y) { - return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo - }; - DD.prototype.pow = function pow (exp) { - if (exp === 0.0) { return DD.valueOf(1.0) } - var r = new DD(this); - var s = DD.valueOf(1.0); - var n = Math.abs(exp); - if (n > 1) { - while (n > 0) { - if (n % 2 === 1) { - s.selfMultiply(r); - } - n /= 2; - if (n > 0) { r = r.sqr(); } - } - } else { - s = r; - } - if (exp < 0) { return s.reciprocal() } - return s - }; - DD.prototype.ceil = function ceil () { - if (this.isNaN()) { return DD.NaN } - var fhi = Math.ceil(this._hi); - var flo = 0.0; - if (fhi === this._hi) { - flo = Math.ceil(this._lo); - } - return new DD(fhi, flo) - }; - DD.prototype.compareTo = function compareTo (o) { - var other = o; - if (this._hi < other._hi) { return -1 } - if (this._hi > other._hi) { return 1 } - if (this._lo < other._lo) { return -1 } - if (this._lo > other._lo) { return 1 } - return 0 - }; - DD.prototype.rint = function rint () { - if (this.isNaN()) { return this } - var plus5 = this.add(0.5); - return plus5.floor() - }; - DD.prototype.setValue = function setValue () { - if (arguments[0] instanceof DD) { - var value = arguments[0]; - this.init(value); - return this - } else if (typeof arguments[0] === 'number') { - var value$1 = arguments[0]; - this.init(value$1); - return this - } - }; - DD.prototype.max = function max (x) { - if (this.ge(x)) { - return this - } else { - return x - } - }; - DD.prototype.sqrt = function sqrt () { - if (this.isZero()) { return DD.valueOf(0.0) } - if (this.isNegative()) { - return DD.NaN - } - var x = 1.0 / Math.sqrt(this._hi); - var ax = this._hi * x; - var axdd = DD.valueOf(ax); - var diffSq = this.subtract(axdd.sqr()); - var d2 = diffSq._hi * (x * 0.5); - return axdd.add(d2) - }; - DD.prototype.selfAdd = function selfAdd () { - if (arguments.length === 1) { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - return this.selfAdd(y._hi, y._lo) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - var H = null; - var h = null; - var S = null; - var s = null; - var e = null; - var f = null; - S = this._hi + y$1; - e = S - this._hi; - s = S - e; - s = y$1 - e + (this._hi - s); - f = s + this._lo; - H = S + f; - h = f + (S - H); - this._hi = H + h; - this._lo = h + (H - this._hi); - return this - } - } else if (arguments.length === 2) { - var yhi = arguments[0]; - var ylo = arguments[1]; - var H$1 = null; - var h$1 = null; - var T = null; - var t = null; - var S$1 = null; - var s$1 = null; - var e$1 = null; - var f$1 = null; - S$1 = this._hi + yhi; - T = this._lo + ylo; - e$1 = S$1 - this._hi; - f$1 = T - this._lo; - s$1 = S$1 - e$1; - t = T - f$1; - s$1 = yhi - e$1 + (this._hi - s$1); - t = ylo - f$1 + (this._lo - t); - e$1 = s$1 + T; - H$1 = S$1 + e$1; - h$1 = e$1 + (S$1 - H$1); - e$1 = t + h$1; - var zhi = H$1 + e$1; - var zlo = e$1 + (H$1 - zhi); - this._hi = zhi; - this._lo = zlo; - return this - } - }; - DD.prototype.selfMultiply = function selfMultiply () { - if (arguments.length === 1) { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - return this.selfMultiply(y._hi, y._lo) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - return this.selfMultiply(y$1, 0.0) - } - } else if (arguments.length === 2) { - var yhi = arguments[0]; - var ylo = arguments[1]; - var hx = null; - var tx = null; - var hy = null; - var ty = null; - var C = null; - var c = null; - C = DD.SPLIT * this._hi; - hx = C - this._hi; - c = DD.SPLIT * yhi; - hx = C - hx; - tx = this._hi - hx; - hy = c - yhi; - C = this._hi * yhi; - hy = c - hy; - ty = yhi - hy; - c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi); - var zhi = C + c; - hx = C - zhi; - var zlo = c + hx; - this._hi = zhi; - this._lo = zlo; - return this - } - }; - DD.prototype.selfSqr = function selfSqr () { - return this.selfMultiply(this) - }; - DD.prototype.floor = function floor () { - if (this.isNaN()) { return DD.NaN } - var fhi = Math.floor(this._hi); - var flo = 0.0; - if (fhi === this._hi) { - flo = Math.floor(this._lo); - } - return new DD(fhi, flo) - }; - DD.prototype.negate = function negate () { - if (this.isNaN()) { return this } - return new DD(-this._hi, -this._lo) - }; - DD.prototype.clone = function clone () { - // try { - // return null - // } catch (ex) { - // if (ex instanceof CloneNotSupportedException) { - // return null - // } else throw ex - // } finally {} - }; - DD.prototype.multiply = function multiply () { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - if (y.isNaN()) { return DD.createNaN() } - return DD.copy(this).selfMultiply(y) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - if (Double.isNaN(y$1)) { return DD.createNaN() } - return DD.copy(this).selfMultiply(y$1, 0.0) - } - }; - DD.prototype.isNaN = function isNaN () { - return Double.isNaN(this._hi) - }; - DD.prototype.intValue = function intValue () { - return Math.trunc(this._hi) - }; - DD.prototype.toString = function toString () { - var mag = DD.magnitude(this._hi); - if (mag >= -3 && mag <= 20) { return this.toStandardNotation() } - return this.toSciNotation() - }; - DD.prototype.toStandardNotation = function toStandardNotation () { - var specialStr = this.getSpecialNumberString(); - if (specialStr !== null) { return specialStr } - var magnitude = new Array(1).fill(null); - var sigDigits = this.extractSignificantDigits(true, magnitude); - var decimalPointPos = magnitude[0] + 1; - var num = sigDigits; - if (sigDigits.charAt(0) === '.') { - num = '0' + sigDigits; - } else if (decimalPointPos < 0) { - num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits; - } else if (sigDigits.indexOf('.') === -1) { - var numZeroes = decimalPointPos - sigDigits.length; - var zeroes = DD.stringOfChar('0', numZeroes); - num = sigDigits + zeroes + '.0'; - } - if (this.isNegative()) { return '-' + num } - return num - }; - DD.prototype.reciprocal = function reciprocal () { - var hc = null; - var tc = null; - var hy = null; - var ty = null; - var C = null; - var c = null; - var U = null; - var u = null; - C = 1.0 / this._hi; - c = DD.SPLIT * C; - hc = c - C; - u = DD.SPLIT * this._hi; - hc = c - hc; - tc = C - hc; - hy = u - this._hi; - U = C * this._hi; - hy = u - hy; - ty = this._hi - hy; - u = hc * hy - U + hc * ty + tc * hy + tc * ty; - c = (1.0 - U - u - C * this._lo) / this._hi; - var zhi = C + c; - var zlo = C - zhi + c; - return new DD(zhi, zlo) - }; - DD.prototype.toSciNotation = function toSciNotation () { - if (this.isZero()) { return DD.SCI_NOT_ZERO } - var specialStr = this.getSpecialNumberString(); - if (specialStr !== null) { return specialStr } - var magnitude = new Array(1).fill(null); - var digits = this.extractSignificantDigits(false, magnitude); - var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0]; - if (digits.charAt(0) === '0') { - throw new Error('Found leading zero: ' + digits) - } - var trailingDigits = ''; - if (digits.length > 1) { trailingDigits = digits.substring(1); } - var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits; - if (this.isNegative()) { return '-' + digitsWithDecimal + expStr } - return digitsWithDecimal + expStr - }; - DD.prototype.abs = function abs () { - if (this.isNaN()) { return DD.NaN } - if (this.isNegative()) { return this.negate() } - return new DD(this) - }; - DD.prototype.isPositive = function isPositive () { - return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0 - }; - DD.prototype.lt = function lt (y) { - return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo - }; - DD.prototype.add = function add () { - if (arguments[0] instanceof DD) { - var y = arguments[0]; - return DD.copy(this).selfAdd(y) - } else if (typeof arguments[0] === 'number') { - var y$1 = arguments[0]; - return DD.copy(this).selfAdd(y$1) - } - }; - DD.prototype.init = function init () { - if (arguments.length === 1) { - if (typeof arguments[0] === 'number') { - var x = arguments[0]; - this._hi = x; - this._lo = 0.0; - } else if (arguments[0] instanceof DD) { - var dd = arguments[0]; - this._hi = dd._hi; - this._lo = dd._lo; - } - } else if (arguments.length === 2) { - var hi = arguments[0]; - var lo = arguments[1]; - this._hi = hi; - this._lo = lo; - } - }; - DD.prototype.gt = function gt (y) { - return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo - }; - DD.prototype.isNegative = function isNegative () { - return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0 - }; - DD.prototype.trunc = function trunc () { - if (this.isNaN()) { return DD.NaN } - if (this.isPositive()) { return this.floor(); } else { return this.ceil() } - }; - DD.prototype.signum = function signum () { - if (this._hi > 0) { return 1 } - if (this._hi < 0) { return -1 } - if (this._lo > 0) { return 1 } - if (this._lo < 0) { return -1 } - return 0 - }; - DD.prototype.interfaces_ = function interfaces_ () { - return [Serializable, Comparable, Clonable] - }; - DD.prototype.getClass = function getClass () { - return DD - }; - DD.sqr = function sqr (x) { - return DD.valueOf(x).selfMultiply(x) - }; - DD.valueOf = function valueOf () { - if (typeof arguments[0] === 'string') { - var str = arguments[0]; - return DD.parse(str) - } else if (typeof arguments[0] === 'number') { - var x = arguments[0]; - return new DD(x) - } - }; - DD.sqrt = function sqrt (x) { - return DD.valueOf(x).sqrt() - }; - DD.parse = function parse (str) { - var i = 0; - var strlen = str.length; - while (Character.isWhitespace(str.charAt(i))) { i++; } - var isNegative = false; - if (i < strlen) { - var signCh = str.charAt(i); - if (signCh === '-' || signCh === '+') { - i++; - if (signCh === '-') { isNegative = true; } - } - } - var val = new DD(); - var numDigits = 0; - var numBeforeDec = 0; - var exp = 0; - while (true) { - if (i >= strlen) { break } - var ch = str.charAt(i); - i++; - if (Character.isDigit(ch)) { - var d = ch - '0'; - val.selfMultiply(DD.TEN); - val.selfAdd(d); - numDigits++; - continue - } - if (ch === '.') { - numBeforeDec = numDigits; - continue - } - if (ch === 'e' || ch === 'E') { - var expStr = str.substring(i); - try { - exp = Integer.parseInt(expStr); - } catch (ex) { - if (ex instanceof Error) { - throw new Error('Invalid exponent ' + expStr + ' in string ' + str) - } else { throw ex } - } finally {} - break - } - throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str) - } - var val2 = val; - var numDecPlaces = numDigits - numBeforeDec - exp; - if (numDecPlaces === 0) { - val2 = val; - } else if (numDecPlaces > 0) { - var scale = DD.TEN.pow(numDecPlaces); - val2 = val.divide(scale); - } else if (numDecPlaces < 0) { - var scale$1 = DD.TEN.pow(-numDecPlaces); - val2 = val.multiply(scale$1); - } - if (isNegative) { - return val2.negate() - } - return val2 - }; - DD.createNaN = function createNaN () { - return new DD(Double.NaN, Double.NaN) - }; - DD.copy = function copy (dd) { - return new DD(dd) - }; - DD.magnitude = function magnitude (x) { - var xAbs = Math.abs(x); - var xLog10 = Math.log(xAbs) / Math.log(10); - var xMag = Math.trunc(Math.floor(xLog10)); - var xApprox = Math.pow(10, xMag); - if (xApprox * 10 <= xAbs) { xMag += 1; } - return xMag - }; - DD.stringOfChar = function stringOfChar (ch, len) { - var buf = new StringBuffer(); - for (var i = 0; i < len; i++) { - buf.append(ch); - } - return buf.toString() - }; - staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) }; - staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) }; - staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) }; - staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) }; - staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) }; - staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 }; - staticAccessors$7.SPLIT.get = function () { return 134217729.0 }; - staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 }; - staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) }; - staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) }; - staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' }; - staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' }; - - Object.defineProperties( DD, staticAccessors$7 ); - - var CGAlgorithmsDD = function CGAlgorithmsDD () {}; - - var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } }; - - CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CGAlgorithmsDD.prototype.getClass = function getClass () { - return CGAlgorithmsDD - }; - CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) { - var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q); - if (index <= 1) { return index } - var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x); - var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y); - var dx2 = DD.valueOf(q.x).selfAdd(-p2.x); - var dy2 = DD.valueOf(q.y).selfAdd(-p2.y); - return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum() - }; - CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) { - var det = x1.multiply(y2).selfSubtract(y1.multiply(x2)); - return det.signum() - }; - CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) { - var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x)); - var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y)); - var denom = denom1.subtract(denom2); - var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y)); - var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x)); - var numx = numx1.subtract(numx2); - var fracP = numx.selfDivide(denom).doubleValue(); - var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue(); - var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y)); - var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x)); - var numy = numy1.subtract(numy2); - var fracQ = numy.selfDivide(denom).doubleValue(); - var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue(); - return new Coordinate(x, y) - }; - CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) { - var detsum = null; - var detleft = (pa.x - pc.x) * (pb.y - pc.y); - var detright = (pa.y - pc.y) * (pb.x - pc.x); - var det = detleft - detright; - if (detleft > 0.0) { - if (detright <= 0.0) { - return CGAlgorithmsDD.signum(det) - } else { - detsum = detleft + detright; - } - } else if (detleft < 0.0) { - if (detright >= 0.0) { - return CGAlgorithmsDD.signum(det) - } else { - detsum = -detleft - detright; - } - } else { - return CGAlgorithmsDD.signum(det) - } - var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum; - if (det >= errbound || -det >= errbound) { - return CGAlgorithmsDD.signum(det) - } - return 2 - }; - CGAlgorithmsDD.signum = function signum (x) { - if (x > 0) { return 1 } - if (x < 0) { return -1 } - return 0 - }; - staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 }; - - Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 ); - - var CoordinateSequence = function CoordinateSequence () {}; - - var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } }; - - staticAccessors$8.X.get = function () { return 0 }; - staticAccessors$8.Y.get = function () { return 1 }; - staticAccessors$8.Z.get = function () { return 2 }; - staticAccessors$8.M.get = function () { return 3 }; - CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {}; - CoordinateSequence.prototype.size = function size () {}; - CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {}; - CoordinateSequence.prototype.getCoordinate = function getCoordinate () {}; - CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {}; - CoordinateSequence.prototype.getDimension = function getDimension () {}; - CoordinateSequence.prototype.getX = function getX (index) {}; - CoordinateSequence.prototype.clone = function clone () {}; - CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {}; - CoordinateSequence.prototype.copy = function copy () {}; - CoordinateSequence.prototype.getY = function getY (index) {}; - CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {}; - CoordinateSequence.prototype.interfaces_ = function interfaces_ () { - return [Clonable] - }; - CoordinateSequence.prototype.getClass = function getClass () { - return CoordinateSequence - }; - - Object.defineProperties( CoordinateSequence, staticAccessors$8 ); - - var Exception = function Exception () {}; - - var NotRepresentableException = (function (Exception$$1) { - function NotRepresentableException () { - Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.'); - } - - if ( Exception$$1 ) NotRepresentableException.__proto__ = Exception$$1; - NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype ); - NotRepresentableException.prototype.constructor = NotRepresentableException; - NotRepresentableException.prototype.interfaces_ = function interfaces_ () { - return [] - }; - NotRepresentableException.prototype.getClass = function getClass () { - return NotRepresentableException - }; - - return NotRepresentableException; - }(Exception)); - - var System = function System () {}; - - System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) { - var c = 0; - for (var i = srcPos; i < srcPos + len; i++) { - dest[destPos + c] = src[i]; - c++; - } - }; - - System.getProperty = function getProperty (name) { - return { - 'line.separator': '\n' - }[name] - }; - - var HCoordinate = function HCoordinate () { - this.x = null; - this.y = null; - this.w = null; - if (arguments.length === 0) { - this.x = 0.0; - this.y = 0.0; - this.w = 1.0; - } else if (arguments.length === 1) { - var p = arguments[0]; - this.x = p.x; - this.y = p.y; - this.w = 1.0; - } else if (arguments.length === 2) { - if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { - var _x = arguments[0]; - var _y = arguments[1]; - this.x = _x; - this.y = _y; - this.w = 1.0; - } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) { - var p1 = arguments[0]; - var p2 = arguments[1]; - this.x = p1.y * p2.w - p2.y * p1.w; - this.y = p2.x * p1.w - p1.x * p2.w; - this.w = p1.x * p2.y - p2.x * p1.y; - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) { - var p1$1 = arguments[0]; - var p2$1 = arguments[1]; - this.x = p1$1.y - p2$1.y; - this.y = p2$1.x - p1$1.x; - this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y; - } - } else if (arguments.length === 3) { - var _x$1 = arguments[0]; - var _y$1 = arguments[1]; - var _w = arguments[2]; - this.x = _x$1; - this.y = _y$1; - this.w = _w; - } else if (arguments.length === 4) { - var p1$2 = arguments[0]; - var p2$2 = arguments[1]; - var q1 = arguments[2]; - var q2 = arguments[3]; - var px = p1$2.y - p2$2.y; - var py = p2$2.x - p1$2.x; - var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y; - var qx = q1.y - q2.y; - var qy = q2.x - q1.x; - var qw = q1.x * q2.y - q2.x * q1.y; - this.x = py * qw - qy * pw; - this.y = qx * pw - px * qw; - this.w = px * qy - qx * py; - } - }; - HCoordinate.prototype.getY = function getY () { - var a = this.y / this.w; - if (Double.isNaN(a) || Double.isInfinite(a)) { - throw new NotRepresentableException() - } - return a - }; - HCoordinate.prototype.getX = function getX () { - var a = this.x / this.w; - if (Double.isNaN(a) || Double.isInfinite(a)) { - throw new NotRepresentableException() - } - return a - }; - HCoordinate.prototype.getCoordinate = function getCoordinate () { - var p = new Coordinate(); - p.x = this.getX(); - p.y = this.getY(); - return p - }; - HCoordinate.prototype.interfaces_ = function interfaces_ () { - return [] - }; - HCoordinate.prototype.getClass = function getClass () { - return HCoordinate - }; - HCoordinate.intersection = function intersection (p1, p2, q1, q2) { - var px = p1.y - p2.y; - var py = p2.x - p1.x; - var pw = p1.x * p2.y - p2.x * p1.y; - var qx = q1.y - q2.y; - var qy = q2.x - q1.x; - var qw = q1.x * q2.y - q2.x * q1.y; - var x = py * qw - qy * pw; - var y = qx * pw - px * qw; - var w = px * qy - qx * py; - var xInt = x / w; - var yInt = y / w; - if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) { - throw new NotRepresentableException() - } - return new Coordinate(xInt, yInt) - }; - - var Envelope = function Envelope () { - this._minx = null; - this._maxx = null; - this._miny = null; - this._maxy = null; - if (arguments.length === 0) { - this.init(); - } else if (arguments.length === 1) { - if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - this.init(p.x, p.x, p.y, p.y); - } else if (arguments[0] instanceof Envelope) { - var env = arguments[0]; - this.init(env); - } - } else if (arguments.length === 2) { - var p1 = arguments[0]; - var p2 = arguments[1]; - this.init(p1.x, p2.x, p1.y, p2.y); - } else if (arguments.length === 4) { - var x1 = arguments[0]; - var x2 = arguments[1]; - var y1 = arguments[2]; - var y2 = arguments[3]; - this.init(x1, x2, y1, y2); - } - }; - - var staticAccessors$9 = { serialVersionUID: { configurable: true } }; - Envelope.prototype.getArea = function getArea () { - return this.getWidth() * this.getHeight() - }; - Envelope.prototype.equals = function equals (other) { - if (!(other instanceof Envelope)) { - return false - } - var otherEnvelope = other; - if (this.isNull()) { - return otherEnvelope.isNull() - } - return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY() - }; - Envelope.prototype.intersection = function intersection (env) { - if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() } - var intMinX = this._minx > env._minx ? this._minx : env._minx; - var intMinY = this._miny > env._miny ? this._miny : env._miny; - var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx; - var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy; - return new Envelope(intMinX, intMaxX, intMinY, intMaxY) - }; - Envelope.prototype.isNull = function isNull () { - return this._maxx < this._minx - }; - Envelope.prototype.getMaxX = function getMaxX () { - return this._maxx - }; - Envelope.prototype.covers = function covers () { - if (arguments.length === 1) { - if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - return this.covers(p.x, p.y) - } else if (arguments[0] instanceof Envelope) { - var other = arguments[0]; - if (this.isNull() || other.isNull()) { - return false - } - return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy - } - } else if (arguments.length === 2) { - var x = arguments[0]; - var y = arguments[1]; - if (this.isNull()) { return false } - return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy - } - }; - Envelope.prototype.intersects = function intersects () { - if (arguments.length === 1) { - if (arguments[0] instanceof Envelope) { - var other = arguments[0]; - if (this.isNull() || other.isNull()) { - return false - } - return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny) - } else if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - return this.intersects(p.x, p.y) - } - } else if (arguments.length === 2) { - var x = arguments[0]; - var y = arguments[1]; - if (this.isNull()) { return false } - return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny) - } - }; - Envelope.prototype.getMinY = function getMinY () { - return this._miny - }; - Envelope.prototype.getMinX = function getMinX () { - return this._minx - }; - Envelope.prototype.expandToInclude = function expandToInclude () { - if (arguments.length === 1) { - if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - this.expandToInclude(p.x, p.y); - } else if (arguments[0] instanceof Envelope) { - var other = arguments[0]; - if (other.isNull()) { - return null - } - if (this.isNull()) { - this._minx = other.getMinX(); - this._maxx = other.getMaxX(); - this._miny = other.getMinY(); - this._maxy = other.getMaxY(); - } else { - if (other._minx < this._minx) { - this._minx = other._minx; - } - if (other._maxx > this._maxx) { - this._maxx = other._maxx; - } - if (other._miny < this._miny) { - this._miny = other._miny; - } - if (other._maxy > this._maxy) { - this._maxy = other._maxy; - } - } - } - } else if (arguments.length === 2) { - var x = arguments[0]; - var y = arguments[1]; - if (this.isNull()) { - this._minx = x; - this._maxx = x; - this._miny = y; - this._maxy = y; - } else { - if (x < this._minx) { - this._minx = x; - } - if (x > this._maxx) { - this._maxx = x; - } - if (y < this._miny) { - this._miny = y; - } - if (y > this._maxy) { - this._maxy = y; - } - } - } - }; - Envelope.prototype.minExtent = function minExtent () { - if (this.isNull()) { return 0.0 } - var w = this.getWidth(); - var h = this.getHeight(); - if (w < h) { return w } - return h - }; - Envelope.prototype.getWidth = function getWidth () { - if (this.isNull()) { - return 0 - } - return this._maxx - this._minx - }; - Envelope.prototype.compareTo = function compareTo (o) { - var env = o; - if (this.isNull()) { - if (env.isNull()) { return 0 } - return -1 - } else { - if (env.isNull()) { return 1 } - } - if (this._minx < env._minx) { return -1 } - if (this._minx > env._minx) { return 1 } - if (this._miny < env._miny) { return -1 } - if (this._miny > env._miny) { return 1 } - if (this._maxx < env._maxx) { return -1 } - if (this._maxx > env._maxx) { return 1 } - if (this._maxy < env._maxy) { return -1 } - if (this._maxy > env._maxy) { return 1 } - return 0 - }; - Envelope.prototype.translate = function translate (transX, transY) { - if (this.isNull()) { - return null - } - this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY); - }; - Envelope.prototype.toString = function toString () { - return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']' - }; - Envelope.prototype.setToNull = function setToNull () { - this._minx = 0; - this._maxx = -1; - this._miny = 0; - this._maxy = -1; - }; - Envelope.prototype.getHeight = function getHeight () { - if (this.isNull()) { - return 0 - } - return this._maxy - this._miny - }; - Envelope.prototype.maxExtent = function maxExtent () { - if (this.isNull()) { return 0.0 } - var w = this.getWidth(); - var h = this.getHeight(); - if (w > h) { return w } - return h - }; - Envelope.prototype.expandBy = function expandBy () { - if (arguments.length === 1) { - var distance = arguments[0]; - this.expandBy(distance, distance); - } else if (arguments.length === 2) { - var deltaX = arguments[0]; - var deltaY = arguments[1]; - if (this.isNull()) { return null } - this._minx -= deltaX; - this._maxx += deltaX; - this._miny -= deltaY; - this._maxy += deltaY; - if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); } - } - }; - Envelope.prototype.contains = function contains () { - if (arguments.length === 1) { - if (arguments[0] instanceof Envelope) { - var other = arguments[0]; - return this.covers(other) - } else if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - return this.covers(p) - } - } else if (arguments.length === 2) { - var x = arguments[0]; - var y = arguments[1]; - return this.covers(x, y) - } - }; - Envelope.prototype.centre = function centre () { - if (this.isNull()) { return null } - return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0) - }; - Envelope.prototype.init = function init () { - if (arguments.length === 0) { - this.setToNull(); - } else if (arguments.length === 1) { - if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - this.init(p.x, p.x, p.y, p.y); - } else if (arguments[0] instanceof Envelope) { - var env = arguments[0]; - this._minx = env._minx; - this._maxx = env._maxx; - this._miny = env._miny; - this._maxy = env._maxy; - } - } else if (arguments.length === 2) { - var p1 = arguments[0]; - var p2 = arguments[1]; - this.init(p1.x, p2.x, p1.y, p2.y); - } else if (arguments.length === 4) { - var x1 = arguments[0]; - var x2 = arguments[1]; - var y1 = arguments[2]; - var y2 = arguments[3]; - if (x1 < x2) { - this._minx = x1; - this._maxx = x2; - } else { - this._minx = x2; - this._maxx = x1; - } - if (y1 < y2) { - this._miny = y1; - this._maxy = y2; - } else { - this._miny = y2; - this._maxy = y1; - } - } - }; - Envelope.prototype.getMaxY = function getMaxY () { - return this._maxy - }; - Envelope.prototype.distance = function distance (env) { - if (this.intersects(env)) { return 0 } - var dx = 0.0; - if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; } - var dy = 0.0; - if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; } - if (dx === 0.0) { return dy } - if (dy === 0.0) { return dx } - return Math.sqrt(dx * dx + dy * dy) - }; - Envelope.prototype.hashCode = function hashCode () { - var result = 17; - result = 37 * result + Coordinate.hashCode(this._minx); - result = 37 * result + Coordinate.hashCode(this._maxx); - result = 37 * result + Coordinate.hashCode(this._miny); - result = 37 * result + Coordinate.hashCode(this._maxy); - return result - }; - Envelope.prototype.interfaces_ = function interfaces_ () { - return [Comparable, Serializable] - }; - Envelope.prototype.getClass = function getClass () { - return Envelope - }; - Envelope.intersects = function intersects () { - if (arguments.length === 3) { - var p1 = arguments[0]; - var p2 = arguments[1]; - var q = arguments[2]; - if (q.x >= (p1.x < p2.x ? p1.x : p2.x) && q.x <= (p1.x > p2.x ? p1.x : p2.x) && (q.y >= (p1.y < p2.y ? p1.y : p2.y) && q.y <= (p1.y > p2.y ? p1.y : p2.y))) { - return true - } - return false - } else if (arguments.length === 4) { - var p1$1 = arguments[0]; - var p2$1 = arguments[1]; - var q1 = arguments[2]; - var q2 = arguments[3]; - var minq = Math.min(q1.x, q2.x); - var maxq = Math.max(q1.x, q2.x); - var minp = Math.min(p1$1.x, p2$1.x); - var maxp = Math.max(p1$1.x, p2$1.x); - if (minp > maxq) { return false } - if (maxp < minq) { return false } - minq = Math.min(q1.y, q2.y); - maxq = Math.max(q1.y, q2.y); - minp = Math.min(p1$1.y, p2$1.y); - maxp = Math.max(p1$1.y, p2$1.y); - if (minp > maxq) { return false } - if (maxp < minq) { return false } - return true - } - }; - staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 }; - - Object.defineProperties( Envelope, staticAccessors$9 ); - - var regExes = { - 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/, - 'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/, - 'spaces': /\s+/, - 'parenComma': /\)\s*,\s*\(/, - 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here - 'trimParens': /^\s*\(?(.*?)\)?\s*$/ - }; - - /** - * Class for reading and writing Well-Known Text. - * - * NOTE: Adapted from OpenLayers 2.11 implementation. - */ - - /** Create a new parser for WKT - * - * @param {GeometryFactory} geometryFactory - * @return An instance of WKTParser. - * @constructor - * @private - */ - var WKTParser = function WKTParser (geometryFactory) { - this.geometryFactory = geometryFactory || new GeometryFactory(); - }; - /** - * Deserialize a WKT string and return a geometry. Supports WKT for POINT, - * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, - * and GEOMETRYCOLLECTION. - * - * @param {String} wkt A WKT string. - * @return {Geometry} A geometry instance. - * @private - */ - WKTParser.prototype.read = function read (wkt) { - var geometry, type, str; - wkt = wkt.replace(/[\n\r]/g, ' '); - var matches = regExes.typeStr.exec(wkt); - if (wkt.search('EMPTY') !== -1) { - matches = regExes.emptyTypeStr.exec(wkt); - matches[2] = undefined; - } - if (matches) { - type = matches[1].toLowerCase(); - str = matches[2]; - if (parse$1[type]) { - geometry = parse$1[type].apply(this, [str]); - } - } - - if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) } - - return geometry - }; - - /** - * Serialize a geometry into a WKT string. - * - * @param {Geometry} geometry A feature or array of features. - * @return {String} The WKT string representation of the input geometries. - * @private - */ - WKTParser.prototype.write = function write (geometry) { - return this.extractGeometry(geometry) - }; - - /** - * Entry point to construct the WKT for a single Geometry object. - * - * @param {Geometry} geometry - * @return {String} A WKT string of representing the geometry. - * @private - */ - WKTParser.prototype.extractGeometry = function extractGeometry (geometry) { - var type = geometry.getGeometryType().toLowerCase(); - if (!extract$1[type]) { - return null - } - var wktType = type.toUpperCase(); - var data; - if (geometry.isEmpty()) { - data = wktType + ' EMPTY'; - } else { - data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')'; - } - return data - }; - - /** - * Object with properties corresponding to the geometry types. Property values - * are functions that do the actual data extraction. - * @private - */ - var extract$1 = { - coordinate: function coordinate (coordinate$1) { - return coordinate$1.x + ' ' + coordinate$1.y - }, - - /** - * Return a space delimited string of point coordinates. - * - * @param {Point} - * point - * @return {String} A string of coordinates representing the point. - */ - point: function point (point$1) { - return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0]) - }, - - /** - * Return a comma delimited string of point coordinates from a multipoint. - * - * @param {MultiPoint} - * multipoint - * @return {String} A string of point coordinate strings representing the - * multipoint. - */ - multipoint: function multipoint (multipoint$1) { - var this$1 = this; - - var array = []; - for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) { - array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')'); - } - return array.join(',') - }, - - /** - * Return a comma delimited string of point coordinates from a line. - * - * @param {LineString} linestring - * @return {String} A string of point coordinate strings representing the linestring. - */ - linestring: function linestring (linestring$1) { - var this$1 = this; - - var array = []; - for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) { - array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]])); - } - return array.join(',') - }, - - linearring: function linearring (linearring$1) { - var this$1 = this; - - var array = []; - for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) { - array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]])); - } - return array.join(',') - }, - - /** - * Return a comma delimited string of linestring strings from a - * multilinestring. - * - * @param {MultiLineString} multilinestring - * @return {String} A string of of linestring strings representing the multilinestring. - */ - multilinestring: function multilinestring (multilinestring$1) { - var this$1 = this; - - var array = []; - for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) { - array.push('(' + - extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) + - ')'); - } - return array.join(',') - }, - - /** - * Return a comma delimited string of linear ring arrays from a polygon. - * - * @param {Polygon} polygon - * @return {String} An array of linear ring arrays representing the polygon. - */ - polygon: function polygon (polygon$1) { - var this$1 = this; - - var array = []; - array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')'); - for (var i = 0, len = polygon$1._holes.length; i < len; ++i) { - array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')'); - } - return array.join(',') - }, - - /** - * Return an array of polygon arrays from a multipolygon. - * - * @param {MultiPolygon} multipolygon - * @return {String} An array of polygon arrays representing the multipolygon. - */ - multipolygon: function multipolygon (multipolygon$1) { - var this$1 = this; - - var array = []; - for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) { - array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')'); - } - return array.join(',') - }, - - /** - * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an - * geometrycollection. - * - * @param {GeometryCollection} collection - * @return {String} internal WKT representation of the collection. - */ - geometrycollection: function geometrycollection (collection) { - var this$1 = this; - - var array = []; - for (var i = 0, len = collection._geometries.length; i < len; ++i) { - array.push(this$1.extractGeometry(collection._geometries[i])); - } - return array.join(',') - } - }; - - /** - * Object with properties corresponding to the geometry types. Property values - * are functions that do the actual parsing. - * @private - */ - var parse$1 = { - /** - * Return point geometry given a point WKT fragment. - * - * @param {String} str A WKT fragment representing the point. - * @return {Point} A point geometry. - * @private - */ - point: function point (str) { - if (str === undefined) { - return this.geometryFactory.createPoint() - } - - var coords = str.trim().split(regExes.spaces); - return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]), - Number.parseFloat(coords[1]))) - }, - - /** - * Return a multipoint geometry given a multipoint WKT fragment. - * - * @param {String} str A WKT fragment representing the multipoint. - * @return {Point} A multipoint feature. - * @private - */ - multipoint: function multipoint (str) { - var this$1 = this; - - if (str === undefined) { - return this.geometryFactory.createMultiPoint() - } - - var point; - var points = str.trim().split(','); - var components = []; - for (var i = 0, len = points.length; i < len; ++i) { - point = points[i].replace(regExes.trimParens, '$1'); - components.push(parse$1.point.apply(this$1, [point])); - } - return this.geometryFactory.createMultiPoint(components) - }, - - /** - * Return a linestring geometry given a linestring WKT fragment. - * - * @param {String} str A WKT fragment representing the linestring. - * @return {LineString} A linestring geometry. - * @private - */ - linestring: function linestring (str) { - if (str === undefined) { - return this.geometryFactory.createLineString() - } - - var points = str.trim().split(','); - var components = []; - var coords; - for (var i = 0, len = points.length; i < len; ++i) { - coords = points[i].trim().split(regExes.spaces); - components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1]))); - } - return this.geometryFactory.createLineString(components) - }, - - /** - * Return a linearring geometry given a linearring WKT fragment. - * - * @param {String} str A WKT fragment representing the linearring. - * @return {LinearRing} A linearring geometry. - * @private - */ - linearring: function linearring (str) { - if (str === undefined) { - return this.geometryFactory.createLinearRing() - } - - var points = str.trim().split(','); - var components = []; - var coords; - for (var i = 0, len = points.length; i < len; ++i) { - coords = points[i].trim().split(regExes.spaces); - components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1]))); - } - return this.geometryFactory.createLinearRing(components) - }, - - /** - * Return a multilinestring geometry given a multilinestring WKT fragment. - * - * @param {String} str A WKT fragment representing the multilinestring. - * @return {MultiLineString} A multilinestring geometry. - * @private - */ - multilinestring: function multilinestring (str) { - var this$1 = this; - - if (str === undefined) { - return this.geometryFactory.createMultiLineString() - } - - var line; - var lines = str.trim().split(regExes.parenComma); - var components = []; - for (var i = 0, len = lines.length; i < len; ++i) { - line = lines[i].replace(regExes.trimParens, '$1'); - components.push(parse$1.linestring.apply(this$1, [line])); - } - return this.geometryFactory.createMultiLineString(components) - }, - - /** - * Return a polygon geometry given a polygon WKT fragment. - * - * @param {String} str A WKT fragment representing the polygon. - * @return {Polygon} A polygon geometry. - * @private - */ - polygon: function polygon (str) { - var this$1 = this; - - if (str === undefined) { - return this.geometryFactory.createPolygon() - } - - var ring, linestring, linearring; - var rings = str.trim().split(regExes.parenComma); - var shell; - var holes = []; - for (var i = 0, len = rings.length; i < len; ++i) { - ring = rings[i].replace(regExes.trimParens, '$1'); - linestring = parse$1.linestring.apply(this$1, [ring]); - linearring = this$1.geometryFactory.createLinearRing(linestring._points); - if (i === 0) { - shell = linearring; - } else { - holes.push(linearring); - } - } - return this.geometryFactory.createPolygon(shell, holes) - }, - - /** - * Return a multipolygon geometry given a multipolygon WKT fragment. - * - * @param {String} str A WKT fragment representing the multipolygon. - * @return {MultiPolygon} A multipolygon geometry. - * @private - */ - multipolygon: function multipolygon (str) { - var this$1 = this; - - if (str === undefined) { - return this.geometryFactory.createMultiPolygon() - } - - var polygon; - var polygons = str.trim().split(regExes.doubleParenComma); - var components = []; - for (var i = 0, len = polygons.length; i < len; ++i) { - polygon = polygons[i].replace(regExes.trimParens, '$1'); - components.push(parse$1.polygon.apply(this$1, [polygon])); - } - return this.geometryFactory.createMultiPolygon(components) - }, - - /** - * Return a geometrycollection given a geometrycollection WKT fragment. - * - * @param {String} str A WKT fragment representing the geometrycollection. - * @return {GeometryCollection} - * @private - */ - geometrycollection: function geometrycollection (str) { - var this$1 = this; - - if (str === undefined) { - return this.geometryFactory.createGeometryCollection() - } - - // separate components of the collection with | - str = str.replace(/,\s*([A-Za-z])/g, '|$1'); - var wktArray = str.trim().split('|'); - var components = []; - for (var i = 0, len = wktArray.length; i < len; ++i) { - components.push(this$1.read(wktArray[i])); - } - return this.geometryFactory.createGeometryCollection(components) - } - }; - - /** - * Writes the Well-Known Text representation of a {@link Geometry}. The - * Well-Known Text format is defined in the OGC Simple Features - * Specification for SQL. - *

    - * The WKTWriter outputs coordinates rounded to the precision - * model. Only the maximum number of decimal places necessary to represent the - * ordinates to the required precision will be output. - *

    - * The SFS WKT spec does not define a special tag for {@link LinearRing}s. - * Under the spec, rings are output as LINESTRINGs. - */ - - /** - * @param {GeometryFactory} geometryFactory - * @constructor - */ - var WKTWriter = function WKTWriter (geometryFactory) { - this.parser = new WKTParser(geometryFactory); - }; - - /** - * Converts a Geometry to its Well-known Text representation. - * - * @param {Geometry} geometry a Geometry to process. - * @return {string} a string (see the OpenGIS Simple - * Features Specification). - * @memberof WKTWriter - */ - WKTWriter.prototype.write = function write (geometry) { - return this.parser.write(geometry) - }; - /** - * Generates the WKT for a LINESTRING specified by two - * {@link Coordinate}s. - * - * @param p0 the first coordinate. - * @param p1 the second coordinate. - * - * @return the WKT. - * @private - */ - WKTWriter.toLineString = function toLineString (p0, p1) { - if (arguments.length !== 2) { - throw new Error('Not implemented') - } - return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )' - }; - - var RuntimeException = (function (Error) { - function RuntimeException (message) { - Error.call(this, message); - this.name = 'RuntimeException'; - this.message = message; - this.stack = (new Error()).stack; - } - - if ( Error ) RuntimeException.__proto__ = Error; - RuntimeException.prototype = Object.create( Error && Error.prototype ); - RuntimeException.prototype.constructor = RuntimeException; - - return RuntimeException; - }(Error)); - - var AssertionFailedException = (function (RuntimeException$$1) { - function AssertionFailedException () { - RuntimeException$$1.call(this); - if (arguments.length === 0) { - RuntimeException$$1.call(this); - } else if (arguments.length === 1) { - var message = arguments[0]; - RuntimeException$$1.call(this, message); - } - } - - if ( RuntimeException$$1 ) AssertionFailedException.__proto__ = RuntimeException$$1; - AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype ); - AssertionFailedException.prototype.constructor = AssertionFailedException; - AssertionFailedException.prototype.interfaces_ = function interfaces_ () { - return [] - }; - AssertionFailedException.prototype.getClass = function getClass () { - return AssertionFailedException - }; - - return AssertionFailedException; - }(RuntimeException)); - - var Assert = function Assert () {}; - - Assert.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Assert.prototype.getClass = function getClass () { - return Assert - }; - Assert.shouldNeverReachHere = function shouldNeverReachHere () { - if (arguments.length === 0) { - Assert.shouldNeverReachHere(null); - } else if (arguments.length === 1) { - var message = arguments[0]; - throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : '')) - } - }; - Assert.isTrue = function isTrue () { - var assertion; - var message; - if (arguments.length === 1) { - assertion = arguments[0]; - Assert.isTrue(assertion, null); - } else if (arguments.length === 2) { - assertion = arguments[0]; - message = arguments[1]; - if (!assertion) { - if (message === null) { - throw new AssertionFailedException() - } else { - throw new AssertionFailedException(message) - } - } - } - }; - Assert.equals = function equals () { - var expectedValue; - var actualValue; - var message; - if (arguments.length === 2) { - expectedValue = arguments[0]; - actualValue = arguments[1]; - Assert.equals(expectedValue, actualValue, null); - } else if (arguments.length === 3) { - expectedValue = arguments[0]; - actualValue = arguments[1]; - message = arguments[2]; - if (!actualValue.equals(expectedValue)) { - throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : '')) - } - } - }; - - var LineIntersector = function LineIntersector () { - this._result = null; - this._inputLines = Array(2).fill().map(function () { return Array(2); }); - this._intPt = new Array(2).fill(null); - this._intLineIndex = null; - this._isProper = null; - this._pa = null; - this._pb = null; - this._precisionModel = null; - this._intPt[0] = new Coordinate(); - this._intPt[1] = new Coordinate(); - this._pa = this._intPt[0]; - this._pb = this._intPt[1]; - this._result = 0; - }; - - var staticAccessors$10 = { DONT_INTERSECT: { configurable: true },DO_INTERSECT: { configurable: true },COLLINEAR: { configurable: true },NO_INTERSECTION: { configurable: true },POINT_INTERSECTION: { configurable: true },COLLINEAR_INTERSECTION: { configurable: true } }; - LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) { - this.computeIntLineIndex(); - return this._intLineIndex[segmentIndex][intIndex] - }; - LineIntersector.prototype.getTopologySummary = function getTopologySummary () { - var catBuf = new StringBuffer(); - if (this.isEndPoint()) { catBuf.append(' endpoint'); } - if (this._isProper) { catBuf.append(' proper'); } - if (this.isCollinear()) { catBuf.append(' collinear'); } - return catBuf.toString() - }; - LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) { - this._inputLines[0][0] = p1; - this._inputLines[0][1] = p2; - this._inputLines[1][0] = p3; - this._inputLines[1][1] = p4; - this._result = this.computeIntersect(p1, p2, p3, p4); - }; - LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () { - return this._result - }; - LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () { - if (arguments.length === 0) { - if (this._intLineIndex === null) { - this._intLineIndex = Array(2).fill().map(function () { return Array(2); }); - this.computeIntLineIndex(0); - this.computeIntLineIndex(1); - } - } else if (arguments.length === 1) { - var segmentIndex = arguments[0]; - var dist0 = this.getEdgeDistance(segmentIndex, 0); - var dist1 = this.getEdgeDistance(segmentIndex, 1); - if (dist0 > dist1) { - this._intLineIndex[segmentIndex][0] = 0; - this._intLineIndex[segmentIndex][1] = 1; - } else { - this._intLineIndex[segmentIndex][0] = 1; - this._intLineIndex[segmentIndex][1] = 0; - } - } - }; - LineIntersector.prototype.isProper = function isProper () { - return this.hasIntersection() && this._isProper - }; - LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) { - this._precisionModel = precisionModel; - }; - LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () { - var this$1 = this; - - if (arguments.length === 0) { - if (this.isInteriorIntersection(0)) { return true } - if (this.isInteriorIntersection(1)) { return true } - return false - } else if (arguments.length === 1) { - var inputLineIndex = arguments[0]; - for (var i = 0; i < this._result; i++) { - if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) { - return true - } - } - return false - } - }; - LineIntersector.prototype.getIntersection = function getIntersection (intIndex) { - return this._intPt[intIndex] - }; - LineIntersector.prototype.isEndPoint = function isEndPoint () { - return this.hasIntersection() && !this._isProper - }; - LineIntersector.prototype.hasIntersection = function hasIntersection () { - return this._result !== LineIntersector.NO_INTERSECTION - }; - LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) { - var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]); - return dist - }; - LineIntersector.prototype.isCollinear = function isCollinear () { - return this._result === LineIntersector.COLLINEAR_INTERSECTION - }; - LineIntersector.prototype.toString = function toString () { - return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary() - }; - LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) { - return this._inputLines[segmentIndex][ptIndex] - }; - LineIntersector.prototype.isIntersection = function isIntersection (pt) { - var this$1 = this; - - for (var i = 0; i < this._result; i++) { - if (this$1._intPt[i].equals2D(pt)) { - return true - } - } - return false - }; - LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) { - this.computeIntLineIndex(); - return this._intPt[this._intLineIndex[segmentIndex][intIndex]] - }; - LineIntersector.prototype.interfaces_ = function interfaces_ () { - return [] - }; - LineIntersector.prototype.getClass = function getClass () { - return LineIntersector - }; - LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) { - var dx = Math.abs(p1.x - p0.x); - var dy = Math.abs(p1.y - p0.y); - var dist = -1.0; - if (p.equals(p0)) { - dist = 0.0; - } else if (p.equals(p1)) { - if (dx > dy) { dist = dx; } else { dist = dy; } - } else { - var pdx = Math.abs(p.x - p0.x); - var pdy = Math.abs(p.y - p0.y); - if (dx > dy) { dist = pdx; } else { dist = pdy; } - if (dist === 0.0 && !p.equals(p0)) { - dist = Math.max(pdx, pdy); - } - } - Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation'); - return dist - }; - LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) { - var dx = p.x - p1.x; - var dy = p.y - p1.y; - var dist = Math.sqrt(dx * dx + dy * dy); - Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation'); - return dist - }; - staticAccessors$10.DONT_INTERSECT.get = function () { return 0 }; - staticAccessors$10.DO_INTERSECT.get = function () { return 1 }; - staticAccessors$10.COLLINEAR.get = function () { return 2 }; - staticAccessors$10.NO_INTERSECTION.get = function () { return 0 }; - staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 }; - staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 }; - - Object.defineProperties( LineIntersector, staticAccessors$10 ); - - var RobustLineIntersector = (function (LineIntersector$$1) { - function RobustLineIntersector () { - LineIntersector$$1.apply(this, arguments); - } - - if ( LineIntersector$$1 ) RobustLineIntersector.__proto__ = LineIntersector$$1; - RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype ); - RobustLineIntersector.prototype.constructor = RobustLineIntersector; - - RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) { - var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]); - var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]); - return env0.contains(intPt) && env1.contains(intPt) - }; - RobustLineIntersector.prototype.computeIntersection = function computeIntersection () { - if (arguments.length === 3) { - var p = arguments[0]; - var p1 = arguments[1]; - var p2 = arguments[2]; - this._isProper = false; - if (Envelope.intersects(p1, p2, p)) { - if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) { - this._isProper = true; - if (p.equals(p1) || p.equals(p2)) { - this._isProper = false; - } - this._result = LineIntersector$$1.POINT_INTERSECTION; - return null - } - } - this._result = LineIntersector$$1.NO_INTERSECTION; - } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) } - }; - RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) { - normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x); - normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y); - n1.x -= normPt.x; - n1.y -= normPt.y; - n2.x -= normPt.x; - n2.y -= normPt.y; - n3.x -= normPt.x; - n3.y -= normPt.y; - n4.x -= normPt.x; - n4.y -= normPt.y; - }; - RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) { - var intPt = null; - try { - intPt = HCoordinate.intersection(p1, p2, q1, q2); - } catch (e) { - if (e instanceof NotRepresentableException) { - intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2); - } else { throw e } - } finally {} - return intPt - }; - RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) { - var intPt = this.intersectionWithNormalization(p1, p2, q1, q2); - if (!this.isInSegmentEnvelopes(intPt)) { - intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2)); - } - if (this._precisionModel !== null) { - this._precisionModel.makePrecise(intPt); - } - return intPt - }; - RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) { - var x = x1; - var xabs = Math.abs(x); - if (Math.abs(x2) < xabs) { - x = x2; - xabs = Math.abs(x2); - } - if (Math.abs(x3) < xabs) { - x = x3; - xabs = Math.abs(x3); - } - if (Math.abs(x4) < xabs) { - x = x4; - } - return x - }; - RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) { - var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2); - var isIn = this.isInSegmentEnvelopes(intPtDD); - System.out.println('DD in env = ' + isIn + ' --------------------- ' + intPtDD); - if (intPt.distance(intPtDD) > 0.0001) { - System.out.println('Distance = ' + intPt.distance(intPtDD)); - } - }; - RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) { - var n1 = new Coordinate(p1); - var n2 = new Coordinate(p2); - var n3 = new Coordinate(q1); - var n4 = new Coordinate(q2); - var normPt = new Coordinate(); - this.normalizeToEnvCentre(n1, n2, n3, n4, normPt); - var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4); - intPt.x += normPt.x; - intPt.y += normPt.y; - return intPt - }; - RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) { - var p1q1p2 = Envelope.intersects(p1, p2, q1); - var p1q2p2 = Envelope.intersects(p1, p2, q2); - var q1p1q2 = Envelope.intersects(q1, q2, p1); - var q1p2q2 = Envelope.intersects(q1, q2, p2); - if (p1q1p2 && p1q2p2) { - this._intPt[0] = q1; - this._intPt[1] = q2; - return LineIntersector$$1.COLLINEAR_INTERSECTION - } - if (q1p1q2 && q1p2q2) { - this._intPt[0] = p1; - this._intPt[1] = p2; - return LineIntersector$$1.COLLINEAR_INTERSECTION - } - if (p1q1p2 && q1p1q2) { - this._intPt[0] = q1; - this._intPt[1] = p1; - return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION - } - if (p1q1p2 && q1p2q2) { - this._intPt[0] = q1; - this._intPt[1] = p2; - return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION - } - if (p1q2p2 && q1p1q2) { - this._intPt[0] = q2; - this._intPt[1] = p1; - return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION - } - if (p1q2p2 && q1p2q2) { - this._intPt[0] = q2; - this._intPt[1] = p2; - return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION - } - return LineIntersector$$1.NO_INTERSECTION - }; - RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) { - var minX0 = n00.x < n01.x ? n00.x : n01.x; - var minY0 = n00.y < n01.y ? n00.y : n01.y; - var maxX0 = n00.x > n01.x ? n00.x : n01.x; - var maxY0 = n00.y > n01.y ? n00.y : n01.y; - var minX1 = n10.x < n11.x ? n10.x : n11.x; - var minY1 = n10.y < n11.y ? n10.y : n11.y; - var maxX1 = n10.x > n11.x ? n10.x : n11.x; - var maxY1 = n10.y > n11.y ? n10.y : n11.y; - var intMinX = minX0 > minX1 ? minX0 : minX1; - var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1; - var intMinY = minY0 > minY1 ? minY0 : minY1; - var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1; - var intMidX = (intMinX + intMaxX) / 2.0; - var intMidY = (intMinY + intMaxY) / 2.0; - normPt.x = intMidX; - normPt.y = intMidY; - n00.x -= normPt.x; - n00.y -= normPt.y; - n01.x -= normPt.x; - n01.y -= normPt.y; - n10.x -= normPt.x; - n10.y -= normPt.y; - n11.x -= normPt.x; - n11.y -= normPt.y; - }; - RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) { - this._isProper = false; - if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION } - var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1); - var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2); - if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) { - return LineIntersector$$1.NO_INTERSECTION - } - var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1); - var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2); - if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) { - return LineIntersector$$1.NO_INTERSECTION - } - var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0; - if (collinear) { - return this.computeCollinearIntersection(p1, p2, q1, q2) - } - if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) { - this._isProper = false; - if (p1.equals2D(q1) || p1.equals2D(q2)) { - this._intPt[0] = p1; - } else if (p2.equals2D(q1) || p2.equals2D(q2)) { - this._intPt[0] = p2; - } else if (Pq1 === 0) { - this._intPt[0] = new Coordinate(q1); - } else if (Pq2 === 0) { - this._intPt[0] = new Coordinate(q2); - } else if (Qp1 === 0) { - this._intPt[0] = new Coordinate(p1); - } else if (Qp2 === 0) { - this._intPt[0] = new Coordinate(p2); - } - } else { - this._isProper = true; - this._intPt[0] = this.intersection(p1, p2, q1, q2); - } - return LineIntersector$$1.POINT_INTERSECTION - }; - RobustLineIntersector.prototype.interfaces_ = function interfaces_ () { - return [] - }; - RobustLineIntersector.prototype.getClass = function getClass () { - return RobustLineIntersector - }; - RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) { - var nearestPt = p1; - var minDist = CGAlgorithms.distancePointLine(p1, q1, q2); - var dist = CGAlgorithms.distancePointLine(p2, q1, q2); - if (dist < minDist) { - minDist = dist; - nearestPt = p2; - } - dist = CGAlgorithms.distancePointLine(q1, p1, p2); - if (dist < minDist) { - minDist = dist; - nearestPt = q1; - } - dist = CGAlgorithms.distancePointLine(q2, p1, p2); - if (dist < minDist) { - minDist = dist; - nearestPt = q2; - } - return nearestPt - }; - - return RobustLineIntersector; - }(LineIntersector)); - - var RobustDeterminant = function RobustDeterminant () {}; - - RobustDeterminant.prototype.interfaces_ = function interfaces_ () { - return [] - }; - RobustDeterminant.prototype.getClass = function getClass () { - return RobustDeterminant - }; - RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) { - var dx1 = p2.x - p1.x; - var dy1 = p2.y - p1.y; - var dx2 = q.x - p2.x; - var dy2 = q.y - p2.y; - return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2) - }; - RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) { - var sign = null; - var swap = null; - var k = null; - sign = 1; - if (x1 === 0.0 || y2 === 0.0) { - if (y1 === 0.0 || x2 === 0.0) { - return 0 - } else if (y1 > 0) { - if (x2 > 0) { - return -sign - } else { - return sign - } - } else { - if (x2 > 0) { - return sign - } else { - return -sign - } - } - } - if (y1 === 0.0 || x2 === 0.0) { - if (y2 > 0) { - if (x1 > 0) { - return sign - } else { - return -sign - } - } else { - if (x1 > 0) { - return -sign - } else { - return sign - } - } - } - if (y1 > 0.0) { - if (y2 > 0.0) { - if (y1 <= y2) ; else { - sign = -sign; - swap = x1; - x1 = x2; - x2 = swap; - swap = y1; - y1 = y2; - y2 = swap; - } - } else { - if (y1 <= -y2) { - sign = -sign; - x2 = -x2; - y2 = -y2; - } else { - swap = x1; - x1 = -x2; - x2 = swap; - swap = y1; - y1 = -y2; - y2 = swap; - } - } - } else { - if (y2 > 0.0) { - if (-y1 <= y2) { - sign = -sign; - x1 = -x1; - y1 = -y1; - } else { - swap = -x1; - x1 = x2; - x2 = swap; - swap = -y1; - y1 = y2; - y2 = swap; - } - } else { - if (y1 >= y2) { - x1 = -x1; - y1 = -y1; - x2 = -x2; - y2 = -y2; - } else { - sign = -sign; - swap = -x1; - x1 = -x2; - x2 = swap; - swap = -y1; - y1 = -y2; - y2 = swap; - } - } - } - if (x1 > 0.0) { - if (x2 > 0.0) { - if (x1 <= x2) ; else { - return sign - } - } else { - return sign - } - } else { - if (x2 > 0.0) { - return -sign - } else { - if (x1 >= x2) { - sign = -sign; - x1 = -x1; - x2 = -x2; - } else { - return -sign - } - } - } - while (true) { - k = Math.floor(x2 / x1); - x2 = x2 - k * x1; - y2 = y2 - k * y1; - if (y2 < 0.0) { - return -sign - } - if (y2 > y1) { - return sign - } - if (x1 > x2 + x2) { - if (y1 < y2 + y2) { - return sign - } - } else { - if (y1 > y2 + y2) { - return -sign - } else { - x2 = x1 - x2; - y2 = y1 - y2; - sign = -sign; - } - } - if (y2 === 0.0) { - if (x2 === 0.0) { - return 0 - } else { - return -sign - } - } - if (x2 === 0.0) { - return sign - } - k = Math.floor(x1 / x2); - x1 = x1 - k * x2; - y1 = y1 - k * y2; - if (y1 < 0.0) { - return sign - } - if (y1 > y2) { - return -sign - } - if (x2 > x1 + x1) { - if (y2 < y1 + y1) { - return -sign - } - } else { - if (y2 > y1 + y1) { - return sign - } else { - x1 = x2 - x1; - y1 = y2 - y1; - sign = -sign; - } - } - if (y1 === 0.0) { - if (x1 === 0.0) { - return 0 - } else { - return sign - } - } - if (x1 === 0.0) { - return -sign - } - } - }; - - var RayCrossingCounter = function RayCrossingCounter () { - this._p = null; - this._crossingCount = 0; - this._isPointOnSegment = false; - var p = arguments[0]; - this._p = p; - }; - RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) { - if (p1.x < this._p.x && p2.x < this._p.x) { return null } - if (this._p.x === p2.x && this._p.y === p2.y) { - this._isPointOnSegment = true; - return null - } - if (p1.y === this._p.y && p2.y === this._p.y) { - var minx = p1.x; - var maxx = p2.x; - if (minx > maxx) { - minx = p2.x; - maxx = p1.x; - } - if (this._p.x >= minx && this._p.x <= maxx) { - this._isPointOnSegment = true; - } - return null - } - if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) { - var x1 = p1.x - this._p.x; - var y1 = p1.y - this._p.y; - var x2 = p2.x - this._p.x; - var y2 = p2.y - this._p.y; - var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2); - if (xIntSign === 0.0) { - this._isPointOnSegment = true; - return null - } - if (y2 < y1) { xIntSign = -xIntSign; } - if (xIntSign > 0.0) { - this._crossingCount++; - } - } - }; - RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () { - return this.getLocation() !== Location.EXTERIOR - }; - RayCrossingCounter.prototype.getLocation = function getLocation () { - if (this._isPointOnSegment) { return Location.BOUNDARY } - if (this._crossingCount % 2 === 1) { - return Location.INTERIOR - } - return Location.EXTERIOR - }; - RayCrossingCounter.prototype.isOnSegment = function isOnSegment () { - return this._isPointOnSegment - }; - RayCrossingCounter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - RayCrossingCounter.prototype.getClass = function getClass () { - return RayCrossingCounter - }; - RayCrossingCounter.locatePointInRing = function locatePointInRing () { - if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) { - var p = arguments[0]; - var ring = arguments[1]; - var counter = new RayCrossingCounter(p); - var p1 = new Coordinate(); - var p2 = new Coordinate(); - for (var i = 1; i < ring.size(); i++) { - ring.getCoordinate(i, p1); - ring.getCoordinate(i - 1, p2); - counter.countSegment(p1, p2); - if (counter.isOnSegment()) { return counter.getLocation() } - } - return counter.getLocation() - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) { - var p$1 = arguments[0]; - var ring$1 = arguments[1]; - var counter$1 = new RayCrossingCounter(p$1); - for (var i$1 = 1; i$1 < ring$1.length; i$1++) { - var p1$1 = ring$1[i$1]; - var p2$1 = ring$1[i$1 - 1]; - counter$1.countSegment(p1$1, p2$1); - if (counter$1.isOnSegment()) { return counter$1.getLocation() } - } - return counter$1.getLocation() - } - }; - - var CGAlgorithms = function CGAlgorithms () {}; - - var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } }; - - CGAlgorithms.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CGAlgorithms.prototype.getClass = function getClass () { - return CGAlgorithms - }; - CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) { - return CGAlgorithmsDD.orientationIndex(p1, p2, q) - }; - CGAlgorithms.signedArea = function signedArea () { - if (arguments[0] instanceof Array) { - var ring = arguments[0]; - if (ring.length < 3) { return 0.0 } - var sum = 0.0; - var x0 = ring[0].x; - for (var i = 1; i < ring.length - 1; i++) { - var x = ring[i].x - x0; - var y1 = ring[i + 1].y; - var y2 = ring[i - 1].y; - sum += x * (y2 - y1); - } - return sum / 2.0 - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var ring$1 = arguments[0]; - var n = ring$1.size(); - if (n < 3) { return 0.0 } - var p0 = new Coordinate(); - var p1 = new Coordinate(); - var p2 = new Coordinate(); - ring$1.getCoordinate(0, p1); - ring$1.getCoordinate(1, p2); - var x0$1 = p1.x; - p2.x -= x0$1; - var sum$1 = 0.0; - for (var i$1 = 1; i$1 < n - 1; i$1++) { - p0.y = p1.y; - p1.x = p2.x; - p1.y = p2.y; - ring$1.getCoordinate(i$1 + 1, p2); - p2.x -= x0$1; - sum$1 += p1.x * (p0.y - p2.y); - } - return sum$1 / 2.0 - } - }; - CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) { - if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) } - if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) } - var noIntersection = false; - if (!Envelope.intersects(A, B, C, D)) { - noIntersection = true; - } else { - var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x); - if (denom === 0) { - noIntersection = true; - } else { - var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y); - var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y); - var s = sNum / denom; - var r = rNumb / denom; - if (r < 0 || r > 1 || s < 0 || s > 1) { - noIntersection = true; - } - } - } - if (noIntersection) { - return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B)) - } - return 0.0 - }; - CGAlgorithms.isPointInRing = function isPointInRing (p, ring) { - return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR - }; - CGAlgorithms.computeLength = function computeLength (pts) { - var n = pts.size(); - if (n <= 1) { return 0.0 } - var len = 0.0; - var p = new Coordinate(); - pts.getCoordinate(0, p); - var x0 = p.x; - var y0 = p.y; - for (var i = 1; i < n; i++) { - pts.getCoordinate(i, p); - var x1 = p.x; - var y1 = p.y; - var dx = x1 - x0; - var dy = y1 - y0; - len += Math.sqrt(dx * dx + dy * dy); - x0 = x1; - y0 = y1; - } - return len - }; - CGAlgorithms.isCCW = function isCCW (ring) { - var nPts = ring.length - 1; - if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') } - var hiPt = ring[0]; - var hiIndex = 0; - for (var i = 1; i <= nPts; i++) { - var p = ring[i]; - if (p.y > hiPt.y) { - hiPt = p; - hiIndex = i; - } - } - var iPrev = hiIndex; - do { - iPrev = iPrev - 1; - if (iPrev < 0) { iPrev = nPts; } - } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex) - var iNext = hiIndex; - do { - iNext = (iNext + 1) % nPts; - } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex) - var prev = ring[iPrev]; - var next = ring[iNext]; - if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false } - var disc = CGAlgorithms.computeOrientation(prev, hiPt, next); - var isCCW = false; - if (disc === 0) { - isCCW = prev.x > next.x; - } else { - isCCW = disc > 0; - } - return isCCW - }; - CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) { - return RayCrossingCounter.locatePointInRing(p, ring) - }; - CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) { - var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y); - var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2; - return Math.abs(s) * Math.sqrt(len2) - }; - CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) { - return CGAlgorithms.orientationIndex(p1, p2, q) - }; - CGAlgorithms.distancePointLine = function distancePointLine () { - if (arguments.length === 2) { - var p = arguments[0]; - var line = arguments[1]; - if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') } - var minDistance = p.distance(line[0]); - for (var i = 0; i < line.length - 1; i++) { - var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]); - if (dist < minDistance) { - minDistance = dist; - } - } - return minDistance - } else if (arguments.length === 3) { - var p$1 = arguments[0]; - var A = arguments[1]; - var B = arguments[2]; - if (A.x === B.x && A.y === B.y) { return p$1.distance(A) } - var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y); - var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2; - if (r <= 0.0) { return p$1.distance(A) } - if (r >= 1.0) { return p$1.distance(B) } - var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2; - return Math.abs(s) * Math.sqrt(len2) - } - }; - CGAlgorithms.isOnLine = function isOnLine (p, pt) { - var lineIntersector = new RobustLineIntersector(); - for (var i = 1; i < pt.length; i++) { - var p0 = pt[i - 1]; - var p1 = pt[i]; - lineIntersector.computeIntersection(p, p0, p1); - if (lineIntersector.hasIntersection()) { - return true - } - } - return false - }; - staticAccessors$3.CLOCKWISE.get = function () { return -1 }; - staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE }; - staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 }; - staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE }; - staticAccessors$3.COLLINEAR.get = function () { return 0 }; - staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR }; - - Object.defineProperties( CGAlgorithms, staticAccessors$3 ); - - var GeometryComponentFilter = function GeometryComponentFilter () {}; - - GeometryComponentFilter.prototype.filter = function filter (geom) {}; - GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryComponentFilter.prototype.getClass = function getClass () { - return GeometryComponentFilter - }; - - var Geometry = function Geometry () { - var factory = arguments[0]; - - this._envelope = null; - this._factory = null; - this._SRID = null; - this._userData = null; - this._factory = factory; - this._SRID = factory.getSRID(); - }; - - var staticAccessors$11 = { serialVersionUID: { configurable: true },SORTINDEX_POINT: { configurable: true },SORTINDEX_MULTIPOINT: { configurable: true },SORTINDEX_LINESTRING: { configurable: true },SORTINDEX_LINEARRING: { configurable: true },SORTINDEX_MULTILINESTRING: { configurable: true },SORTINDEX_POLYGON: { configurable: true },SORTINDEX_MULTIPOLYGON: { configurable: true },SORTINDEX_GEOMETRYCOLLECTION: { configurable: true },geometryChangedFilter: { configurable: true } }; - Geometry.prototype.isGeometryCollection = function isGeometryCollection () { - return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION - }; - Geometry.prototype.getFactory = function getFactory () { - return this._factory - }; - Geometry.prototype.getGeometryN = function getGeometryN (n) { - return this - }; - Geometry.prototype.getArea = function getArea () { - return 0.0 - }; - Geometry.prototype.isRectangle = function isRectangle () { - return false - }; - Geometry.prototype.equals = function equals () { - if (arguments[0] instanceof Geometry) { - var g$1 = arguments[0]; - if (g$1 === null) { return false } - return this.equalsTopo(g$1) - } else if (arguments[0] instanceof Object) { - var o = arguments[0]; - if (!(o instanceof Geometry)) { return false } - var g = o; - return this.equalsExact(g) - } - }; - Geometry.prototype.equalsExact = function equalsExact (other) { - return this === other || this.equalsExact(other, 0) - }; - Geometry.prototype.geometryChanged = function geometryChanged () { - this.apply(Geometry.geometryChangedFilter); - }; - Geometry.prototype.geometryChangedAction = function geometryChangedAction () { - this._envelope = null; - }; - Geometry.prototype.equalsNorm = function equalsNorm (g) { - if (g === null) { return false } - return this.norm().equalsExact(g.norm()) - }; - Geometry.prototype.getLength = function getLength () { - return 0.0 - }; - Geometry.prototype.getNumGeometries = function getNumGeometries () { - return 1 - }; - Geometry.prototype.compareTo = function compareTo () { - if (arguments.length === 1) { - var o = arguments[0]; - var other = o; - if (this.getSortIndex() !== other.getSortIndex()) { - return this.getSortIndex() - other.getSortIndex() - } - if (this.isEmpty() && other.isEmpty()) { - return 0 - } - if (this.isEmpty()) { - return -1 - } - if (other.isEmpty()) { - return 1 - } - return this.compareToSameClass(o) - } else if (arguments.length === 2) { - var other$1 = arguments[0]; - var comp = arguments[1]; - if (this.getSortIndex() !== other$1.getSortIndex()) { - return this.getSortIndex() - other$1.getSortIndex() - } - if (this.isEmpty() && other$1.isEmpty()) { - return 0 - } - if (this.isEmpty()) { - return -1 - } - if (other$1.isEmpty()) { - return 1 - } - return this.compareToSameClass(other$1, comp) - } - }; - Geometry.prototype.getUserData = function getUserData () { - return this._userData - }; - Geometry.prototype.getSRID = function getSRID () { - return this._SRID - }; - Geometry.prototype.getEnvelope = function getEnvelope () { - return this.getFactory().toGeometry(this.getEnvelopeInternal()) - }; - Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) { - if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) { - throw new IllegalArgumentException('This method does not support GeometryCollection arguments') - } - }; - Geometry.prototype.equal = function equal (a, b, tolerance) { - if (tolerance === 0) { - return a.equals(b) - } - return a.distance(b) <= tolerance - }; - Geometry.prototype.norm = function norm () { - var copy = this.copy(); - copy.normalize(); - return copy - }; - Geometry.prototype.getPrecisionModel = function getPrecisionModel () { - return this._factory.getPrecisionModel() - }; - Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () { - if (this._envelope === null) { - this._envelope = this.computeEnvelopeInternal(); - } - return new Envelope(this._envelope) - }; - Geometry.prototype.setSRID = function setSRID (SRID) { - this._SRID = SRID; - }; - Geometry.prototype.setUserData = function setUserData (userData) { - this._userData = userData; - }; - Geometry.prototype.compare = function compare (a, b) { - var i = a.iterator(); - var j = b.iterator(); - while (i.hasNext() && j.hasNext()) { - var aElement = i.next(); - var bElement = j.next(); - var comparison = aElement.compareTo(bElement); - if (comparison !== 0) { - return comparison - } - } - if (i.hasNext()) { - return 1 - } - if (j.hasNext()) { - return -1 - } - return 0 - }; - Geometry.prototype.hashCode = function hashCode () { - return this.getEnvelopeInternal().hashCode() - }; - Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () { - if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) { - return true - } - return false - }; - Geometry.prototype.interfaces_ = function interfaces_ () { - return [Clonable, Comparable, Serializable] - }; - Geometry.prototype.getClass = function getClass () { - return Geometry - }; - Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) { - for (var i = 0; i < geometries.length; i++) { - if (!geometries[i].isEmpty()) { - return true - } - } - return false - }; - Geometry.hasNullElements = function hasNullElements (array) { - for (var i = 0; i < array.length; i++) { - if (array[i] === null) { - return true - } - } - return false - }; - staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 }; - staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 }; - staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 }; - staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 }; - staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 }; - staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 }; - staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 }; - staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 }; - staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 }; - staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter }; - - Object.defineProperties( Geometry, staticAccessors$11 ); - - var geometryChangedFilter = function geometryChangedFilter () {}; - - geometryChangedFilter.interfaces_ = function interfaces_ () { - return [GeometryComponentFilter] - }; - geometryChangedFilter.filter = function filter (geom) { - geom.geometryChangedAction(); - }; - - var CoordinateFilter = function CoordinateFilter () {}; - - CoordinateFilter.prototype.filter = function filter (coord) {}; - CoordinateFilter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateFilter.prototype.getClass = function getClass () { - return CoordinateFilter - }; - - var BoundaryNodeRule = function BoundaryNodeRule () {}; - - var staticAccessors$12 = { Mod2BoundaryNodeRule: { configurable: true },EndPointBoundaryNodeRule: { configurable: true },MultiValentEndPointBoundaryNodeRule: { configurable: true },MonoValentEndPointBoundaryNodeRule: { configurable: true },MOD2_BOUNDARY_RULE: { configurable: true },ENDPOINT_BOUNDARY_RULE: { configurable: true },MULTIVALENT_ENDPOINT_BOUNDARY_RULE: { configurable: true },MONOVALENT_ENDPOINT_BOUNDARY_RULE: { configurable: true },OGC_SFS_BOUNDARY_RULE: { configurable: true } }; - - BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {}; - BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BoundaryNodeRule.prototype.getClass = function getClass () { - return BoundaryNodeRule - }; - staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule }; - staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule }; - staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule }; - staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule }; - staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() }; - staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() }; - staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() }; - staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() }; - staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE }; - - Object.defineProperties( BoundaryNodeRule, staticAccessors$12 ); - - var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {}; - - Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) { - return boundaryCount % 2 === 1 - }; - Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () { - return [BoundaryNodeRule] - }; - Mod2BoundaryNodeRule.prototype.getClass = function getClass () { - return Mod2BoundaryNodeRule - }; - - var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {}; - - EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) { - return boundaryCount > 0 - }; - EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () { - return [BoundaryNodeRule] - }; - EndPointBoundaryNodeRule.prototype.getClass = function getClass () { - return EndPointBoundaryNodeRule - }; - - var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {}; - - MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) { - return boundaryCount > 1 - }; - MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () { - return [BoundaryNodeRule] - }; - MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () { - return MultiValentEndPointBoundaryNodeRule - }; - - var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {}; - - MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) { - return boundaryCount === 1 - }; - MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () { - return [BoundaryNodeRule] - }; - MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () { - return MonoValentEndPointBoundaryNodeRule - }; - - // import Iterator from './Iterator' - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html - * - * @constructor - * @private - */ - var Collection = function Collection () {}; - - Collection.prototype.add = function add () {}; - - /** - * Appends all of the elements in the specified collection to the end of this - * list, in the order that they are returned by the specified collection's - * iterator (optional operation). - * @param {javascript.util.Collection} c - * @return {boolean} - */ - Collection.prototype.addAll = function addAll () {}; - - /** - * Returns true if this collection contains no elements. - * @return {boolean} - */ - Collection.prototype.isEmpty = function isEmpty () {}; - - /** - * Returns an iterator over the elements in this collection. - * @return {javascript.util.Iterator} - */ - Collection.prototype.iterator = function iterator () {}; - - /** - * Returns an iterator over the elements in this collection. - * @return {number} - */ - Collection.prototype.size = function size () {}; - - /** - * Returns an array containing all of the elements in this collection. - * @return {Array} - */ - Collection.prototype.toArray = function toArray () {}; - - /** - * Removes a single instance of the specified element from this collection if it - * is present. (optional) - * @param {Object} e - * @return {boolean} - */ - Collection.prototype.remove = function remove () {}; - - /** - * @param {string=} message Optional message - * @extends {Error} - * @constructor - * @private - */ - function IndexOutOfBoundsException (message) { - this.message = message || ''; - } - IndexOutOfBoundsException.prototype = new Error(); - - /** - * @type {string} - */ - IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException'; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html - * @constructor - * @private - */ - var Iterator$1 = function Iterator () {}; - - Iterator$1.prototype.hasNext = function hasNext () {}; - - /** - * Returns the next element in the iteration. - * @return {Object} - */ - Iterator$1.prototype.next = function next () {}; - - /** - * Removes from the underlying collection the last element returned by the - * iterator (optional operation). - */ - Iterator$1.prototype.remove = function remove () {}; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html - * - * @extends {javascript.util.Collection} - * @constructor - * @private - */ - var List = (function (Collection$$1) { - function List () { - Collection$$1.apply(this, arguments); - } - - if ( Collection$$1 ) List.__proto__ = Collection$$1; - List.prototype = Object.create( Collection$$1 && Collection$$1.prototype ); - List.prototype.constructor = List; - - List.prototype.get = function get () { }; - - /** - * Replaces the element at the specified position in this list with the - * specified element (optional operation). - * @param {number} index - * @param {Object} e - * @return {Object} - */ - List.prototype.set = function set () { }; - - /** - * Returns true if this collection contains no elements. - * @return {boolean} - */ - List.prototype.isEmpty = function isEmpty () { }; - - return List; - }(Collection)); - - /** - * @param {string=} message Optional message - * @extends {Error} - * @constructor - * @private - */ - function NoSuchElementException (message) { - this.message = message || ''; - } - NoSuchElementException.prototype = new Error(); - - /** - * @type {string} - */ - NoSuchElementException.prototype.name = 'NoSuchElementException'; - - // import OperationNotSupported from './OperationNotSupported' - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html - * - * @extends List - * @private - */ - var ArrayList = (function (List$$1) { - function ArrayList () { - List$$1.call(this); - this.array_ = []; - - if (arguments[0] instanceof Collection) { - this.addAll(arguments[0]); - } - } - - if ( List$$1 ) ArrayList.__proto__ = List$$1; - ArrayList.prototype = Object.create( List$$1 && List$$1.prototype ); - ArrayList.prototype.constructor = ArrayList; - - ArrayList.prototype.ensureCapacity = function ensureCapacity () {}; - ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] }; - - /** - * @override - */ - ArrayList.prototype.add = function add (e) { - if (arguments.length === 1) { - this.array_.push(e); - } else { - this.array_.splice(arguments[0], arguments[1]); - } - return true - }; - - ArrayList.prototype.clear = function clear () { - this.array_ = []; - }; - - /** - * @override - */ - ArrayList.prototype.addAll = function addAll (c) { - var this$1 = this; - - for (var i = c.iterator(); i.hasNext();) { - this$1.add(i.next()); - } - return true - }; - - /** - * @override - */ - ArrayList.prototype.set = function set (index, element) { - var oldElement = this.array_[index]; - this.array_[index] = element; - return oldElement - }; - - /** - * @override - */ - ArrayList.prototype.iterator = function iterator () { - return new Iterator_(this) - }; - - /** - * @override - */ - ArrayList.prototype.get = function get (index) { - if (index < 0 || index >= this.size()) { - throw new IndexOutOfBoundsException() - } - - return this.array_[index] - }; - - /** - * @override - */ - ArrayList.prototype.isEmpty = function isEmpty () { - return this.array_.length === 0 - }; - - /** - * @override - */ - ArrayList.prototype.size = function size () { - return this.array_.length - }; - - /** - * @override - */ - ArrayList.prototype.toArray = function toArray () { - var this$1 = this; - - var array = []; - - for (var i = 0, len = this.array_.length; i < len; i++) { - array.push(this$1.array_[i]); - } - - return array - }; - - /** - * @override - */ - ArrayList.prototype.remove = function remove (o) { - var this$1 = this; - - var found = false; - - for (var i = 0, len = this.array_.length; i < len; i++) { - if (this$1.array_[i] === o) { - this$1.array_.splice(i, 1); - found = true; - break - } - } - - return found - }; - - return ArrayList; - }(List)); - - /** - * @extends {Iterator} - * @param {ArrayList} arrayList - * @constructor - * @private - */ - var Iterator_ = (function (Iterator$$1) { - function Iterator_ (arrayList) { - Iterator$$1.call(this); - /** - * @type {ArrayList} - * @private - */ - this.arrayList_ = arrayList; - /** - * @type {number} - * @private - */ - this.position_ = 0; - } - - if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1; - Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype ); - Iterator_.prototype.constructor = Iterator_; - - /** - * @override - */ - Iterator_.prototype.next = function next () { - if (this.position_ === this.arrayList_.size()) { - throw new NoSuchElementException() - } - return this.arrayList_.get(this.position_++) - }; - - /** - * @override - */ - Iterator_.prototype.hasNext = function hasNext () { - if (this.position_ < this.arrayList_.size()) { - return true - } else { - return false - } - }; - - /** - * TODO: should be in ListIterator - * @override - */ - Iterator_.prototype.set = function set (element) { - return this.arrayList_.set(this.position_ - 1, element) - }; - - /** - * @override - */ - Iterator_.prototype.remove = function remove () { - this.arrayList_.remove(this.arrayList_.get(this.position_)); - }; - - return Iterator_; - }(Iterator$1)); - - var CoordinateList = (function (ArrayList$$1) { - function CoordinateList () { - ArrayList$$1.call(this); - if (arguments.length === 0) ; else if (arguments.length === 1) { - var coord = arguments[0]; - this.ensureCapacity(coord.length); - this.add(coord, true); - } else if (arguments.length === 2) { - var coord$1 = arguments[0]; - var allowRepeated = arguments[1]; - this.ensureCapacity(coord$1.length); - this.add(coord$1, allowRepeated); - } - } - - if ( ArrayList$$1 ) CoordinateList.__proto__ = ArrayList$$1; - CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype ); - CoordinateList.prototype.constructor = CoordinateList; - - var staticAccessors = { coordArrayType: { configurable: true } }; - staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) }; - CoordinateList.prototype.getCoordinate = function getCoordinate (i) { - return this.get(i) - }; - CoordinateList.prototype.addAll = function addAll () { - var this$1 = this; - - if (arguments.length === 2) { - var coll = arguments[0]; - var allowRepeated = arguments[1]; - var isChanged = false; - for (var i = coll.iterator(); i.hasNext();) { - this$1.add(i.next(), allowRepeated); - isChanged = true; - } - return isChanged - } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) } - }; - CoordinateList.prototype.clone = function clone () { - var this$1 = this; - - var clone = ArrayList$$1.prototype.clone.call(this); - for (var i = 0; i < this.size(); i++) { - clone.add(i, this$1.get(i).copy()); - } - return clone - }; - CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () { - return this.toArray(CoordinateList.coordArrayType) - }; - CoordinateList.prototype.add = function add () { - var this$1 = this; - - if (arguments.length === 1) { - var coord = arguments[0]; - ArrayList$$1.prototype.add.call(this, coord); - } else if (arguments.length === 2) { - if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') { - var coord$1 = arguments[0]; - var allowRepeated = arguments[1]; - this.add(coord$1, allowRepeated, true); - return true - } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') { - var coord$2 = arguments[0]; - var allowRepeated$1 = arguments[1]; - if (!allowRepeated$1) { - if (this.size() >= 1) { - var last = this.get(this.size() - 1); - if (last.equals2D(coord$2)) { return null } - } - } - ArrayList$$1.prototype.add.call(this, coord$2); - } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') { - var obj = arguments[0]; - var allowRepeated$2 = arguments[1]; - this.add(obj, allowRepeated$2); - return true - } - } else if (arguments.length === 3) { - if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) { - var coord$3 = arguments[0]; - var allowRepeated$3 = arguments[1]; - var direction = arguments[2]; - if (direction) { - for (var i$1 = 0; i$1 < coord$3.length; i$1++) { - this$1.add(coord$3[i$1], allowRepeated$3); - } - } else { - for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) { - this$1.add(coord$3[i$2], allowRepeated$3); - } - } - return true - } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) { - var i$3 = arguments[0]; - var coord$4 = arguments[1]; - var allowRepeated$4 = arguments[2]; - if (!allowRepeated$4) { - var size = this.size(); - if (size > 0) { - if (i$3 > 0) { - var prev = this.get(i$3 - 1); - if (prev.equals2D(coord$4)) { return null } - } - if (i$3 < size) { - var next = this.get(i$3); - if (next.equals2D(coord$4)) { return null } - } - } - } - ArrayList$$1.prototype.add.call(this, i$3, coord$4); - } - } else if (arguments.length === 4) { - var coord$5 = arguments[0]; - var allowRepeated$5 = arguments[1]; - var start = arguments[2]; - var end = arguments[3]; - var inc = 1; - if (start > end) { inc = -1; } - for (var i = start; i !== end; i += inc) { - this$1.add(coord$5[i], allowRepeated$5); - } - return true - } - }; - CoordinateList.prototype.closeRing = function closeRing () { - if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); } - }; - CoordinateList.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateList.prototype.getClass = function getClass () { - return CoordinateList - }; - - Object.defineProperties( CoordinateList, staticAccessors ); - - return CoordinateList; - }(ArrayList)); - - var CoordinateArrays = function CoordinateArrays () {}; - - var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } }; - - staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator }; - staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator }; - staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) }; - - CoordinateArrays.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateArrays.prototype.getClass = function getClass () { - return CoordinateArrays - }; - CoordinateArrays.isRing = function isRing (pts) { - if (pts.length < 4) { return false } - if (!pts[0].equals2D(pts[pts.length - 1])) { return false } - return true - }; - CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) { - for (var i = 0; i < testPts.length; i++) { - var testPt = testPts[i]; - if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt } - } - return null - }; - CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) { - var i = CoordinateArrays.indexOf(firstCoordinate, coordinates); - if (i < 0) { return null } - var newCoordinates = new Array(coordinates.length).fill(null); - System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i); - System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i); - System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length); - }; - CoordinateArrays.equals = function equals () { - if (arguments.length === 2) { - var coord1 = arguments[0]; - var coord2 = arguments[1]; - if (coord1 === coord2) { return true } - if (coord1 === null || coord2 === null) { return false } - if (coord1.length !== coord2.length) { return false } - for (var i = 0; i < coord1.length; i++) { - if (!coord1[i].equals(coord2[i])) { return false } - } - return true - } else if (arguments.length === 3) { - var coord1$1 = arguments[0]; - var coord2$1 = arguments[1]; - var coordinateComparator = arguments[2]; - if (coord1$1 === coord2$1) { return true } - if (coord1$1 === null || coord2$1 === null) { return false } - if (coord1$1.length !== coord2$1.length) { return false } - for (var i$1 = 0; i$1 < coord1$1.length; i$1++) { - if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false } - } - return true - } - }; - CoordinateArrays.intersection = function intersection (coordinates, env) { - var coordList = new CoordinateList(); - for (var i = 0; i < coordinates.length; i++) { - if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); } - } - return coordList.toCoordinateArray() - }; - CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) { - for (var i = 1; i < coord.length; i++) { - if (coord[i - 1].equals(coord[i])) { - return true - } - } - return false - }; - CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) { - if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord } - var coordList = new CoordinateList(coord, false); - return coordList.toCoordinateArray() - }; - CoordinateArrays.reverse = function reverse (coord) { - var last = coord.length - 1; - var mid = Math.trunc(last / 2); - for (var i = 0; i <= mid; i++) { - var tmp = coord[i]; - coord[i] = coord[last - i]; - coord[last - i] = tmp; - } - }; - CoordinateArrays.removeNull = function removeNull (coord) { - var nonNull = 0; - for (var i = 0; i < coord.length; i++) { - if (coord[i] !== null) { nonNull++; } - } - var newCoord = new Array(nonNull).fill(null); - if (nonNull === 0) { return newCoord } - var j = 0; - for (var i$1 = 0; i$1 < coord.length; i$1++) { - if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; } - } - return newCoord - }; - CoordinateArrays.copyDeep = function copyDeep () { - if (arguments.length === 1) { - var coordinates = arguments[0]; - var copy = new Array(coordinates.length).fill(null); - for (var i = 0; i < coordinates.length; i++) { - copy[i] = new Coordinate(coordinates[i]); - } - return copy - } else if (arguments.length === 5) { - var src = arguments[0]; - var srcStart = arguments[1]; - var dest = arguments[2]; - var destStart = arguments[3]; - var length = arguments[4]; - for (var i$1 = 0; i$1 < length; i$1++) { - dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]); - } - } - }; - CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) { - for (var i = 0; i < pts1.length; i++) { - var p1 = pts1[i]; - var p2 = pts2[pts1.length - i - 1]; - if (p1.compareTo(p2) !== 0) { return false } - } - return true - }; - CoordinateArrays.envelope = function envelope (coordinates) { - var env = new Envelope(); - for (var i = 0; i < coordinates.length; i++) { - env.expandToInclude(coordinates[i]); - } - return env - }; - CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) { - return coordList.toArray(CoordinateArrays.coordArrayType) - }; - CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) { - return c.length >= n ? c : [] - }; - CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) { - for (var i = 0; i < coordinates.length; i++) { - if (coordinate.equals(coordinates[i])) { - return i - } - } - return -1 - }; - CoordinateArrays.increasingDirection = function increasingDirection (pts) { - for (var i = 0; i < Math.trunc(pts.length / 2); i++) { - var j = pts.length - 1 - i; - var comp = pts[i].compareTo(pts[j]); - if (comp !== 0) { return comp } - } - return 1 - }; - CoordinateArrays.compare = function compare (pts1, pts2) { - var i = 0; - while (i < pts1.length && i < pts2.length) { - var compare = pts1[i].compareTo(pts2[i]); - if (compare !== 0) { return compare } - i++; - } - if (i < pts2.length) { return -1 } - if (i < pts1.length) { return 1 } - return 0 - }; - CoordinateArrays.minCoordinate = function minCoordinate (coordinates) { - var minCoord = null; - for (var i = 0; i < coordinates.length; i++) { - if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) { - minCoord = coordinates[i]; - } - } - return minCoord - }; - CoordinateArrays.extract = function extract (pts, start, end) { - start = MathUtil.clamp(start, 0, pts.length); - end = MathUtil.clamp(end, -1, pts.length); - var npts = end - start + 1; - if (end < 0) { npts = 0; } - if (start >= pts.length) { npts = 0; } - if (end < start) { npts = 0; } - var extractPts = new Array(npts).fill(null); - if (npts === 0) { return extractPts } - var iPts = 0; - for (var i = start; i <= end; i++) { - extractPts[iPts++] = pts[i]; - } - return extractPts - }; - - Object.defineProperties( CoordinateArrays, staticAccessors$13 ); - - var ForwardComparator = function ForwardComparator () {}; - - ForwardComparator.prototype.compare = function compare (o1, o2) { - var pts1 = o1; - var pts2 = o2; - return CoordinateArrays.compare(pts1, pts2) - }; - ForwardComparator.prototype.interfaces_ = function interfaces_ () { - return [Comparator] - }; - ForwardComparator.prototype.getClass = function getClass () { - return ForwardComparator - }; - - var BidirectionalComparator = function BidirectionalComparator () {}; - - BidirectionalComparator.prototype.compare = function compare (o1, o2) { - var pts1 = o1; - var pts2 = o2; - if (pts1.length < pts2.length) { return -1 } - if (pts1.length > pts2.length) { return 1 } - if (pts1.length === 0) { return 0 } - var forwardComp = CoordinateArrays.compare(pts1, pts2); - var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2); - if (isEqualRev) { return 0 } - return forwardComp - }; - BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) { - var pts1 = o1; - var pts2 = o2; - if (pts1.length < pts2.length) { return -1 } - if (pts1.length > pts2.length) { return 1 } - if (pts1.length === 0) { return 0 } - var dir1 = CoordinateArrays.increasingDirection(pts1); - var dir2 = CoordinateArrays.increasingDirection(pts2); - var i1 = dir1 > 0 ? 0 : pts1.length - 1; - var i2 = dir2 > 0 ? 0 : pts1.length - 1; - for (var i = 0; i < pts1.length; i++) { - var comparePt = pts1[i1].compareTo(pts2[i2]); - if (comparePt !== 0) { return comparePt } - i1 += dir1; - i2 += dir2; - } - return 0 - }; - BidirectionalComparator.prototype.interfaces_ = function interfaces_ () { - return [Comparator] - }; - BidirectionalComparator.prototype.getClass = function getClass () { - return BidirectionalComparator - }; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html - * - * @constructor - * @private - */ - var Map$1$1 = function Map () {}; - - Map$1$1.prototype.get = function get () {}; - /** - * Associates the specified value with the specified key in this map (optional - * operation). - * @param {Object} key - * @param {Object} value - * @return {Object} - */ - Map$1$1.prototype.put = function put () {}; - - /** - * Returns the number of key-value mappings in this map. - * @return {number} - */ - Map$1$1.prototype.size = function size () {}; - - /** - * Returns a Collection view of the values contained in this map. - * @return {javascript.util.Collection} - */ - Map$1$1.prototype.values = function values () {}; - - /** - * Returns a {@link Set} view of the mappings contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa.If the map is modified - * while an iteration over the set is in progress (except through - * the iterator's own remove operation, or through the - * setValue operation on a map entry returned by the - * iterator) the results of the iteration are undefined.The set - * supports element removal, which removes the corresponding - * mapping from the map, via the Iterator.remove, - * Set.remove, removeAll, retainAll and - * clear operations.It does not support the - * add or addAll operations. - * - * @return {Set} a set view of the mappings contained in this map - */ - Map$1$1.prototype.entrySet = function entrySet () {}; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html - * - * @extends {Map} - * @constructor - * @private - */ - var SortedMap = (function (Map) { - function SortedMap () { - Map.apply(this, arguments); - }if ( Map ) SortedMap.__proto__ = Map; - SortedMap.prototype = Object.create( Map && Map.prototype ); - SortedMap.prototype.constructor = SortedMap; - - - - return SortedMap; - }(Map$1$1)); - - /** - * @param {string=} message Optional message - * @extends {Error} - * @constructor - * @private - */ - function OperationNotSupported (message) { - this.message = message || ''; - } - OperationNotSupported.prototype = new Error(); - - /** - * @type {string} - */ - OperationNotSupported.prototype.name = 'OperationNotSupported'; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html - * - * @extends {Collection} - * @constructor - * @private - */ - function Set$2() {} - Set$2.prototype = new Collection(); - - - /** - * Returns true if this set contains the specified element. More formally, - * returns true if and only if this set contains an element e such that (o==null ? - * e==null : o.equals(e)). - * @param {Object} e - * @return {boolean} - */ - Set$2.prototype.contains = function() {}; - - /** - * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html - * - * @extends {javascript.util.Set} - * @constructor - * @private - */ - var HashSet = (function (Set$$1) { - function HashSet () { - Set$$1.call(this); - this.array_ = []; - - if (arguments[0] instanceof Collection) { - this.addAll(arguments[0]); - } - } - - if ( Set$$1 ) HashSet.__proto__ = Set$$1; - HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype ); - HashSet.prototype.constructor = HashSet; - - /** - * @override - */ - HashSet.prototype.contains = function contains (o) { - var this$1 = this; - - for (var i = 0, len = this.array_.length; i < len; i++) { - var e = this$1.array_[i]; - if (e === o) { - return true - } - } - return false - }; - - /** - * @override - */ - HashSet.prototype.add = function add (o) { - if (this.contains(o)) { - return false - } - - this.array_.push(o); - - return true - }; - - /** - * @override - */ - HashSet.prototype.addAll = function addAll (c) { - var this$1 = this; - - for (var i = c.iterator(); i.hasNext();) { - this$1.add(i.next()); - } - return true - }; - - /** - * @override - */ - HashSet.prototype.remove = function remove (o) { - // throw new javascript.util.OperationNotSupported() - throw new Error() - }; - - /** - * @override - */ - HashSet.prototype.size = function size () { - return this.array_.length - }; - - /** - * @override - */ - HashSet.prototype.isEmpty = function isEmpty () { - return this.array_.length === 0 - }; - - /** - * @override - */ - HashSet.prototype.toArray = function toArray () { - var this$1 = this; - - var array = []; - - for (var i = 0, len = this.array_.length; i < len; i++) { - array.push(this$1.array_[i]); - } - - return array - }; - - /** - * @override - */ - HashSet.prototype.iterator = function iterator () { - return new Iterator_$1(this) - }; - - return HashSet; - }(Set$2)); - - /** - * @extends {Iterator} - * @param {HashSet} hashSet - * @constructor - * @private - */ - var Iterator_$1 = (function (Iterator$$1) { - function Iterator_ (hashSet) { - Iterator$$1.call(this); - /** - * @type {HashSet} - * @private - */ - this.hashSet_ = hashSet; - /** - * @type {number} - * @private - */ - this.position_ = 0; - } - - if ( Iterator$$1 ) Iterator_.__proto__ = Iterator$$1; - Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype ); - Iterator_.prototype.constructor = Iterator_; - - /** - * @override - */ - Iterator_.prototype.next = function next () { - if (this.position_ === this.hashSet_.size()) { - throw new NoSuchElementException() - } - return this.hashSet_.array_[this.position_++] - }; - - /** - * @override - */ - Iterator_.prototype.hasNext = function hasNext () { - if (this.position_ < this.hashSet_.size()) { - return true - } else { - return false - } - }; - - /** - * @override - */ - Iterator_.prototype.remove = function remove () { - throw new OperationNotSupported() - }; - - return Iterator_; - }(Iterator$1)); - - var BLACK = 0; - var RED = 1; - function colorOf (p) { return (p === null ? BLACK : p.color) } - function parentOf (p) { return (p === null ? null : p.parent) } - function setColor (p, c) { if (p !== null) { p.color = c; } } - function leftOf (p) { return (p === null ? null : p.left) } - function rightOf (p) { return (p === null ? null : p.right) } - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html - * - * @extends {SortedMap} - * @constructor - * @private - */ - function TreeMap () { - /** - * @type {Object} - * @private - */ - this.root_ = null; - /** - * @type {number} - * @private - */ - this.size_ = 0; - } - TreeMap.prototype = new SortedMap(); - - /** - * @override - */ - TreeMap.prototype.get = function (key) { - var p = this.root_; - while (p !== null) { - var cmp = key['compareTo'](p.key); - if (cmp < 0) { p = p.left; } - else if (cmp > 0) { p = p.right; } - else { return p.value } - } - return null - }; - - /** - * @override - */ - TreeMap.prototype.put = function (key, value) { - if (this.root_ === null) { - this.root_ = { - key: key, - value: value, - left: null, - right: null, - parent: null, - color: BLACK, - getValue: function getValue () { return this.value }, - getKey: function getKey () { return this.key } - }; - this.size_ = 1; - return null - } - var t = this.root_; - var parent; - var cmp; - do { - parent = t; - cmp = key['compareTo'](t.key); - if (cmp < 0) { - t = t.left; - } else if (cmp > 0) { - t = t.right; - } else { - var oldValue = t.value; - t.value = value; - return oldValue - } - } while (t !== null) - var e = { - key: key, - left: null, - right: null, - value: value, - parent: parent, - color: BLACK, - getValue: function getValue () { return this.value }, - getKey: function getKey () { return this.key } - }; - if (cmp < 0) { - parent.left = e; - } else { - parent.right = e; - } - this.fixAfterInsertion(e); - this.size_++; - return null - }; - - /** - * @param {Object} x - */ - TreeMap.prototype.fixAfterInsertion = function (x) { - var this$1 = this; - - x.color = RED; - while (x != null && x !== this.root_ && x.parent.color === RED) { - if (parentOf(x) === leftOf(parentOf(parentOf(x)))) { - var y = rightOf(parentOf(parentOf(x))); - if (colorOf(y) === RED) { - setColor(parentOf(x), BLACK); - setColor(y, BLACK); - setColor(parentOf(parentOf(x)), RED); - x = parentOf(parentOf(x)); - } else { - if (x === rightOf(parentOf(x))) { - x = parentOf(x); - this$1.rotateLeft(x); - } - setColor(parentOf(x), BLACK); - setColor(parentOf(parentOf(x)), RED); - this$1.rotateRight(parentOf(parentOf(x))); - } - } else { - var y$1 = leftOf(parentOf(parentOf(x))); - if (colorOf(y$1) === RED) { - setColor(parentOf(x), BLACK); - setColor(y$1, BLACK); - setColor(parentOf(parentOf(x)), RED); - x = parentOf(parentOf(x)); - } else { - if (x === leftOf(parentOf(x))) { - x = parentOf(x); - this$1.rotateRight(x); - } - setColor(parentOf(x), BLACK); - setColor(parentOf(parentOf(x)), RED); - this$1.rotateLeft(parentOf(parentOf(x))); - } - } - } - this.root_.color = BLACK; - }; - - /** - * @override - */ - TreeMap.prototype.values = function () { - var arrayList = new ArrayList(); - var p = this.getFirstEntry(); - if (p !== null) { - arrayList.add(p.value); - while ((p = TreeMap.successor(p)) !== null) { - arrayList.add(p.value); - } - } - return arrayList - }; - - /** - * @override - */ - TreeMap.prototype.entrySet = function () { - var hashSet = new HashSet(); - var p = this.getFirstEntry(); - if (p !== null) { - hashSet.add(p); - while ((p = TreeMap.successor(p)) !== null) { - hashSet.add(p); - } - } - return hashSet - }; - - /** - * @param {Object} p - */ - TreeMap.prototype.rotateLeft = function (p) { - if (p != null) { - var r = p.right; - p.right = r.left; - if (r.left != null) { r.left.parent = p; } - r.parent = p.parent; - if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; } - r.left = p; - p.parent = r; - } - }; - - /** - * @param {Object} p - */ - TreeMap.prototype.rotateRight = function (p) { - if (p != null) { - var l = p.left; - p.left = l.right; - if (l.right != null) { l.right.parent = p; } - l.parent = p.parent; - if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; } - l.right = p; - p.parent = l; - } - }; - - /** - * @return {Object} - */ - TreeMap.prototype.getFirstEntry = function () { - var p = this.root_; - if (p != null) { - while (p.left != null) { - p = p.left; - } - } - return p - }; - - /** - * @param {Object} t - * @return {Object} - * @private - */ - TreeMap.successor = function (t) { - if (t === null) { return null } else if (t.right !== null) { - var p = t.right; - while (p.left !== null) { - p = p.left; - } - return p - } else { - var p$1 = t.parent; - var ch = t; - while (p$1 !== null && ch === p$1.right) { - ch = p$1; - p$1 = p$1.parent; - } - return p$1 - } - }; - - /** - * @override - */ - TreeMap.prototype.size = function () { - return this.size_ - }; - - var Lineal = function Lineal () {}; - - Lineal.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Lineal.prototype.getClass = function getClass () { - return Lineal - }; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html - * - * @extends {Set} - * @constructor - * @private - */ - function SortedSet () {} - SortedSet.prototype = new Set$2(); - - // import Iterator from './Iterator' - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html - * - * @extends {SortedSet} - * @constructor - * @private - */ - function TreeSet () { - /** - * @type {Array} - * @private - */ - this.array_ = []; - - if (arguments[0] instanceof Collection) { - this.addAll(arguments[0]); - } - } - TreeSet.prototype = new SortedSet(); - - /** - * @override - */ - TreeSet.prototype.contains = function (o) { - var this$1 = this; - - for (var i = 0, len = this.array_.length; i < len; i++) { - var e = this$1.array_[i]; - if (e['compareTo'](o) === 0) { - return true - } - } - return false - }; - - /** - * @override - */ - TreeSet.prototype.add = function (o) { - var this$1 = this; - - if (this.contains(o)) { - return false - } - - for (var i = 0, len = this.array_.length; i < len; i++) { - var e = this$1.array_[i]; - if (e['compareTo'](o) === 1) { - this$1.array_.splice(i, 0, o); - return true - } - } - - this.array_.push(o); - - return true - }; - - /** - * @override - */ - TreeSet.prototype.addAll = function (c) { - var this$1 = this; - - for (var i = c.iterator(); i.hasNext();) { - this$1.add(i.next()); - } - return true - }; - - /** - * @override - */ - TreeSet.prototype.remove = function (e) { - throw new OperationNotSupported() - }; - - /** - * @override - */ - TreeSet.prototype.size = function () { - return this.array_.length - }; - - /** - * @override - */ - TreeSet.prototype.isEmpty = function () { - return this.array_.length === 0 - }; - - /** - * @override - */ - TreeSet.prototype.toArray = function () { - var this$1 = this; - - var array = []; - - for (var i = 0, len = this.array_.length; i < len; i++) { - array.push(this$1.array_[i]); - } - - return array - }; - - /** - * @override - */ - TreeSet.prototype.iterator = function () { - return new Iterator_$2(this) - }; - - /** - * @extends {javascript.util.Iterator} - * @param {javascript.util.TreeSet} treeSet - * @constructor - * @private - */ - var Iterator_$2 = function (treeSet) { - /** - * @type {javascript.util.TreeSet} - * @private - */ - this.treeSet_ = treeSet; - /** - * @type {number} - * @private - */ - this.position_ = 0; - }; - - /** - * @override - */ - Iterator_$2.prototype.next = function () { - if (this.position_ === this.treeSet_.size()) { - throw new NoSuchElementException() - } - return this.treeSet_.array_[this.position_++] - }; - - /** - * @override - */ - Iterator_$2.prototype.hasNext = function () { - if (this.position_ < this.treeSet_.size()) { - return true - } else { - return false - } - }; - - /** - * @override - */ - Iterator_$2.prototype.remove = function () { - throw new OperationNotSupported() - }; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html - * - * @constructor - * @private - */ - var Arrays = function Arrays () {}; - - Arrays.sort = function sort () { - var a = arguments[0]; - var i; - var t; - var comparator; - var compare; - if (arguments.length === 1) { - compare = function (a, b) { - return a.compareTo(b) - }; - a.sort(compare); - } else if (arguments.length === 2) { - comparator = arguments[1]; - compare = function (a, b) { - return comparator['compare'](a, b) - }; - a.sort(compare); - } else if (arguments.length === 3) { - t = a.slice(arguments[1], arguments[2]); - t.sort(); - var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length)); - a.splice(0, a.length); - for (i = 0; i < r.length; i++) { - a.push(r[i]); - } - } else if (arguments.length === 4) { - t = a.slice(arguments[1], arguments[2]); - comparator = arguments[3]; - compare = function (a, b) { - return comparator['compare'](a, b) - }; - t.sort(compare); - r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length)); - a.splice(0, a.length); - for (i = 0; i < r.length; i++) { - a.push(r[i]); - } - } - }; - /** - * @param {Array} array - * @return {ArrayList} - */ - Arrays.asList = function asList (array) { - var arrayList = new ArrayList(); - for (var i = 0, len = array.length; i < len; i++) { - arrayList.add(array[i]); - } - return arrayList - }; - - var Dimension = function Dimension () {}; - - var staticAccessors$14 = { P: { configurable: true },L: { configurable: true },A: { configurable: true },FALSE: { configurable: true },TRUE: { configurable: true },DONTCARE: { configurable: true },SYM_FALSE: { configurable: true },SYM_TRUE: { configurable: true },SYM_DONTCARE: { configurable: true },SYM_P: { configurable: true },SYM_L: { configurable: true },SYM_A: { configurable: true } }; - - staticAccessors$14.P.get = function () { return 0 }; - staticAccessors$14.L.get = function () { return 1 }; - staticAccessors$14.A.get = function () { return 2 }; - staticAccessors$14.FALSE.get = function () { return -1 }; - staticAccessors$14.TRUE.get = function () { return -2 }; - staticAccessors$14.DONTCARE.get = function () { return -3 }; - staticAccessors$14.SYM_FALSE.get = function () { return 'F' }; - staticAccessors$14.SYM_TRUE.get = function () { return 'T' }; - staticAccessors$14.SYM_DONTCARE.get = function () { return '*' }; - staticAccessors$14.SYM_P.get = function () { return '0' }; - staticAccessors$14.SYM_L.get = function () { return '1' }; - staticAccessors$14.SYM_A.get = function () { return '2' }; - - Dimension.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Dimension.prototype.getClass = function getClass () { - return Dimension - }; - Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) { - switch (dimensionValue) { - case Dimension.FALSE: - return Dimension.SYM_FALSE - case Dimension.TRUE: - return Dimension.SYM_TRUE - case Dimension.DONTCARE: - return Dimension.SYM_DONTCARE - case Dimension.P: - return Dimension.SYM_P - case Dimension.L: - return Dimension.SYM_L - case Dimension.A: - return Dimension.SYM_A - } - throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue) - }; - Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) { - switch (Character.toUpperCase(dimensionSymbol)) { - case Dimension.SYM_FALSE: - return Dimension.FALSE - case Dimension.SYM_TRUE: - return Dimension.TRUE - case Dimension.SYM_DONTCARE: - return Dimension.DONTCARE - case Dimension.SYM_P: - return Dimension.P - case Dimension.SYM_L: - return Dimension.L - case Dimension.SYM_A: - return Dimension.A - } - throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol) - }; - - Object.defineProperties( Dimension, staticAccessors$14 ); - - var GeometryFilter = function GeometryFilter () {}; - - GeometryFilter.prototype.filter = function filter (geom) {}; - GeometryFilter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryFilter.prototype.getClass = function getClass () { - return GeometryFilter - }; - - var CoordinateSequenceFilter = function CoordinateSequenceFilter () {}; - - CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {}; - CoordinateSequenceFilter.prototype.isDone = function isDone () {}; - CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {}; - CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateSequenceFilter.prototype.getClass = function getClass () { - return CoordinateSequenceFilter - }; - - var GeometryCollection = (function (Geometry$$1) { - function GeometryCollection (geometries, factory) { - Geometry$$1.call(this, factory); - this._geometries = geometries || []; - - if (Geometry$$1.hasNullElements(this._geometries)) { - throw new IllegalArgumentException('geometries must not contain null elements') - } - } - - if ( Geometry$$1 ) GeometryCollection.__proto__ = Geometry$$1; - GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype ); - GeometryCollection.prototype.constructor = GeometryCollection; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () { - var this$1 = this; - - var envelope = new Envelope(); - for (var i = 0; i < this._geometries.length; i++) { - envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal()); - } - return envelope - }; - GeometryCollection.prototype.getGeometryN = function getGeometryN (n) { - return this._geometries[n] - }; - GeometryCollection.prototype.getSortIndex = function getSortIndex () { - return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION - }; - GeometryCollection.prototype.getCoordinates = function getCoordinates () { - var this$1 = this; - - var coordinates = new Array(this.getNumPoints()).fill(null); - var k = -1; - for (var i = 0; i < this._geometries.length; i++) { - var childCoordinates = this$1._geometries[i].getCoordinates(); - for (var j = 0; j < childCoordinates.length; j++) { - k++; - coordinates[k] = childCoordinates[j]; - } - } - return coordinates - }; - GeometryCollection.prototype.getArea = function getArea () { - var this$1 = this; - - var area = 0.0; - for (var i = 0; i < this._geometries.length; i++) { - area += this$1._geometries[i].getArea(); - } - return area - }; - GeometryCollection.prototype.equalsExact = function equalsExact () { - var this$1 = this; - - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - var otherCollection = other; - if (this._geometries.length !== otherCollection._geometries.length) { - return false - } - for (var i = 0; i < this._geometries.length; i++) { - if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) { - return false - } - } - return true - } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) } - }; - GeometryCollection.prototype.normalize = function normalize () { - var this$1 = this; - - for (var i = 0; i < this._geometries.length; i++) { - this$1._geometries[i].normalize(); - } - Arrays.sort(this._geometries); - }; - GeometryCollection.prototype.getCoordinate = function getCoordinate () { - if (this.isEmpty()) { return null } - return this._geometries[0].getCoordinate() - }; - GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () { - var this$1 = this; - - var dimension = Dimension.FALSE; - for (var i = 0; i < this._geometries.length; i++) { - dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension()); - } - return dimension - }; - GeometryCollection.prototype.getDimension = function getDimension () { - var this$1 = this; - - var dimension = Dimension.FALSE; - for (var i = 0; i < this._geometries.length; i++) { - dimension = Math.max(dimension, this$1._geometries[i].getDimension()); - } - return dimension - }; - GeometryCollection.prototype.getLength = function getLength () { - var this$1 = this; - - var sum = 0.0; - for (var i = 0; i < this._geometries.length; i++) { - sum += this$1._geometries[i].getLength(); - } - return sum - }; - GeometryCollection.prototype.getNumPoints = function getNumPoints () { - var this$1 = this; - - var numPoints = 0; - for (var i = 0; i < this._geometries.length; i++) { - numPoints += this$1._geometries[i].getNumPoints(); - } - return numPoints - }; - GeometryCollection.prototype.getNumGeometries = function getNumGeometries () { - return this._geometries.length - }; - GeometryCollection.prototype.reverse = function reverse () { - var this$1 = this; - - var n = this._geometries.length; - var revGeoms = new Array(n).fill(null); - for (var i = 0; i < this._geometries.length; i++) { - revGeoms[i] = this$1._geometries[i].reverse(); - } - return this.getFactory().createGeometryCollection(revGeoms) - }; - GeometryCollection.prototype.compareToSameClass = function compareToSameClass () { - var this$1 = this; - - if (arguments.length === 1) { - var o = arguments[0]; - var theseElements = new TreeSet(Arrays.asList(this._geometries)); - var otherElements = new TreeSet(Arrays.asList(o._geometries)); - return this.compare(theseElements, otherElements) - } else if (arguments.length === 2) { - var o$1 = arguments[0]; - var comp = arguments[1]; - var gc = o$1; - var n1 = this.getNumGeometries(); - var n2 = gc.getNumGeometries(); - var i = 0; - while (i < n1 && i < n2) { - var thisGeom = this$1.getGeometryN(i); - var otherGeom = gc.getGeometryN(i); - var holeComp = thisGeom.compareToSameClass(otherGeom, comp); - if (holeComp !== 0) { return holeComp } - i++; - } - if (i < n1) { return 1 } - if (i < n2) { return -1 } - return 0 - } - }; - GeometryCollection.prototype.apply = function apply () { - var this$1 = this; - - if (hasInterface(arguments[0], CoordinateFilter)) { - var filter = arguments[0]; - for (var i = 0; i < this._geometries.length; i++) { - this$1._geometries[i].apply(filter); - } - } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) { - var filter$1 = arguments[0]; - if (this._geometries.length === 0) { return null } - for (var i$1 = 0; i$1 < this._geometries.length; i$1++) { - this$1._geometries[i$1].apply(filter$1); - if (filter$1.isDone()) { - break - } - } - if (filter$1.isGeometryChanged()) { this.geometryChanged(); } - } else if (hasInterface(arguments[0], GeometryFilter)) { - var filter$2 = arguments[0]; - filter$2.filter(this); - for (var i$2 = 0; i$2 < this._geometries.length; i$2++) { - this$1._geometries[i$2].apply(filter$2); - } - } else if (hasInterface(arguments[0], GeometryComponentFilter)) { - var filter$3 = arguments[0]; - filter$3.filter(this); - for (var i$3 = 0; i$3 < this._geometries.length; i$3++) { - this$1._geometries[i$3].apply(filter$3); - } - } - }; - GeometryCollection.prototype.getBoundary = function getBoundary () { - this.checkNotGeometryCollection(this); - Assert.shouldNeverReachHere(); - return null - }; - GeometryCollection.prototype.clone = function clone () { - var this$1 = this; - - var gc = Geometry$$1.prototype.clone.call(this); - gc._geometries = new Array(this._geometries.length).fill(null); - for (var i = 0; i < this._geometries.length; i++) { - gc._geometries[i] = this$1._geometries[i].clone(); - } - return gc - }; - GeometryCollection.prototype.getGeometryType = function getGeometryType () { - return 'GeometryCollection' - }; - GeometryCollection.prototype.copy = function copy () { - var this$1 = this; - - var geometries = new Array(this._geometries.length).fill(null); - for (var i = 0; i < geometries.length; i++) { - geometries[i] = this$1._geometries[i].copy(); - } - return new GeometryCollection(geometries, this._factory) - }; - GeometryCollection.prototype.isEmpty = function isEmpty () { - var this$1 = this; - - for (var i = 0; i < this._geometries.length; i++) { - if (!this$1._geometries[i].isEmpty()) { - return false - } - } - return true - }; - GeometryCollection.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryCollection.prototype.getClass = function getClass () { - return GeometryCollection - }; - staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 }; - - Object.defineProperties( GeometryCollection, staticAccessors ); - - return GeometryCollection; - }(Geometry)); - - var MultiLineString = (function (GeometryCollection$$1) { - function MultiLineString () { - GeometryCollection$$1.apply(this, arguments); - } - - if ( GeometryCollection$$1 ) MultiLineString.__proto__ = GeometryCollection$$1; - MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype ); - MultiLineString.prototype.constructor = MultiLineString; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - - MultiLineString.prototype.getSortIndex = function getSortIndex () { - return Geometry.SORTINDEX_MULTILINESTRING - }; - MultiLineString.prototype.equalsExact = function equalsExact () { - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance) - } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) } - }; - MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () { - if (this.isClosed()) { - return Dimension.FALSE - } - return 0 - }; - MultiLineString.prototype.isClosed = function isClosed () { - var this$1 = this; - - if (this.isEmpty()) { - return false - } - for (var i = 0; i < this._geometries.length; i++) { - if (!this$1._geometries[i].isClosed()) { - return false - } - } - return true - }; - MultiLineString.prototype.getDimension = function getDimension () { - return 1 - }; - MultiLineString.prototype.reverse = function reverse () { - var this$1 = this; - - var nLines = this._geometries.length; - var revLines = new Array(nLines).fill(null); - for (var i = 0; i < this._geometries.length; i++) { - revLines[nLines - 1 - i] = this$1._geometries[i].reverse(); - } - return this.getFactory().createMultiLineString(revLines) - }; - MultiLineString.prototype.getBoundary = function getBoundary () { - return new BoundaryOp(this).getBoundary() - }; - MultiLineString.prototype.getGeometryType = function getGeometryType () { - return 'MultiLineString' - }; - MultiLineString.prototype.copy = function copy () { - var this$1 = this; - - var lineStrings = new Array(this._geometries.length).fill(null); - for (var i = 0; i < lineStrings.length; i++) { - lineStrings[i] = this$1._geometries[i].copy(); - } - return new MultiLineString(lineStrings, this._factory) - }; - MultiLineString.prototype.interfaces_ = function interfaces_ () { - return [Lineal] - }; - MultiLineString.prototype.getClass = function getClass () { - return MultiLineString - }; - staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 }; - - Object.defineProperties( MultiLineString, staticAccessors ); - - return MultiLineString; - }(GeometryCollection)); - - var BoundaryOp = function BoundaryOp () { - this._geom = null; - this._geomFact = null; - this._bnRule = null; - this._endpointMap = null; - if (arguments.length === 1) { - var geom = arguments[0]; - var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE; - this._geom = geom; - this._geomFact = geom.getFactory(); - this._bnRule = bnRule; - } else if (arguments.length === 2) { - var geom$1 = arguments[0]; - var bnRule$1 = arguments[1]; - this._geom = geom$1; - this._geomFact = geom$1.getFactory(); - this._bnRule = bnRule$1; - } - }; - BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) { - if (this._geom.isEmpty()) { - return this.getEmptyMultiPoint() - } - var bdyPts = this.computeBoundaryCoordinates(mLine); - if (bdyPts.length === 1) { - return this._geomFact.createPoint(bdyPts[0]) - } - return this._geomFact.createMultiPointFromCoords(bdyPts) - }; - BoundaryOp.prototype.getBoundary = function getBoundary () { - if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) } - if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) } - return this._geom.getBoundary() - }; - BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) { - if (this._geom.isEmpty()) { - return this.getEmptyMultiPoint() - } - if (line.isClosed()) { - var closedEndpointOnBoundary = this._bnRule.isInBoundary(2); - if (closedEndpointOnBoundary) { - return line.getStartPoint() - } else { - return this._geomFact.createMultiPoint() - } - } - return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()]) - }; - BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () { - return this._geomFact.createMultiPoint() - }; - BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) { - var this$1 = this; - - var bdyPts = new ArrayList(); - this._endpointMap = new TreeMap(); - for (var i = 0; i < mLine.getNumGeometries(); i++) { - var line = mLine.getGeometryN(i); - if (line.getNumPoints() === 0) { continue } - this$1.addEndpoint(line.getCoordinateN(0)); - this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1)); - } - for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) { - var entry = it.next(); - var counter = entry.getValue(); - var valence = counter.count; - if (this$1._bnRule.isInBoundary(valence)) { - bdyPts.add(entry.getKey()); - } - } - return CoordinateArrays.toCoordinateArray(bdyPts) - }; - BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) { - var counter = this._endpointMap.get(pt); - if (counter === null) { - counter = new Counter(); - this._endpointMap.put(pt, counter); - } - counter.count++; - }; - BoundaryOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BoundaryOp.prototype.getClass = function getClass () { - return BoundaryOp - }; - BoundaryOp.getBoundary = function getBoundary () { - if (arguments.length === 1) { - var g = arguments[0]; - var bop = new BoundaryOp(g); - return bop.getBoundary() - } else if (arguments.length === 2) { - var g$1 = arguments[0]; - var bnRule = arguments[1]; - var bop$1 = new BoundaryOp(g$1, bnRule); - return bop$1.getBoundary() - } - }; - - var Counter = function Counter () { - this.count = null; - }; - Counter.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Counter.prototype.getClass = function getClass () { - return Counter - }; - - // boundary - - function PrintStream () {} - - function StringReader () {} - - var DecimalFormat = function DecimalFormat () {}; - - function ByteArrayOutputStream () {} - - function IOException () {} - - function LineNumberReader () {} - - var StringUtil = function StringUtil () {}; - - var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } }; - - StringUtil.prototype.interfaces_ = function interfaces_ () { - return [] - }; - StringUtil.prototype.getClass = function getClass () { - return StringUtil - }; - StringUtil.chars = function chars (c, n) { - var ch = new Array(n).fill(null); - for (var i = 0; i < n; i++) { - ch[i] = c; - } - return String(ch) - }; - StringUtil.getStackTrace = function getStackTrace () { - if (arguments.length === 1) { - var t = arguments[0]; - var os = new ByteArrayOutputStream(); - var ps = new PrintStream(os); - t.printStackTrace(ps); - return os.toString() - } else if (arguments.length === 2) { - var t$1 = arguments[0]; - var depth = arguments[1]; - var stackTrace = ''; - var stringReader = new StringReader(StringUtil.getStackTrace(t$1)); - var lineNumberReader = new LineNumberReader(stringReader); - for (var i = 0; i < depth; i++) { - try { - stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE; - } catch (e) { - if (e instanceof IOException) { - Assert.shouldNeverReachHere(); - } else { throw e } - } finally {} - } - return stackTrace - } - }; - StringUtil.split = function split (s, separator) { - var separatorlen = separator.length; - var tokenList = new ArrayList(); - var tmpString = '' + s; - var pos = tmpString.indexOf(separator); - while (pos >= 0) { - var token = tmpString.substring(0, pos); - tokenList.add(token); - tmpString = tmpString.substring(pos + separatorlen); - pos = tmpString.indexOf(separator); - } - if (tmpString.length > 0) { tokenList.add(tmpString); } - var res = new Array(tokenList.size()).fill(null); - for (var i = 0; i < res.length; i++) { - res[i] = tokenList.get(i); - } - return res - }; - StringUtil.toString = function toString () { - if (arguments.length === 1) { - var d = arguments[0]; - return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d) - } - }; - StringUtil.spaces = function spaces (n) { - return StringUtil.chars(' ', n) - }; - staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') }; - staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') }; - - Object.defineProperties( StringUtil, staticAccessors$15 ); - - var CoordinateSequences = function CoordinateSequences () {}; - - CoordinateSequences.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CoordinateSequences.prototype.getClass = function getClass () { - return CoordinateSequences - }; - CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) { - var minDim = Math.min(src.getDimension(), dest.getDimension()); - for (var dim = 0; dim < minDim; dim++) { - dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim)); - } - }; - CoordinateSequences.isRing = function isRing (seq) { - var n = seq.size(); - if (n === 0) { return true } - if (n <= 3) { return false } - return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y) - }; - CoordinateSequences.isEqual = function isEqual (cs1, cs2) { - var cs1Size = cs1.size(); - var cs2Size = cs2.size(); - if (cs1Size !== cs2Size) { return false } - var dim = Math.min(cs1.getDimension(), cs2.getDimension()); - for (var i = 0; i < cs1Size; i++) { - for (var d = 0; d < dim; d++) { - var v1 = cs1.getOrdinate(i, d); - var v2 = cs2.getOrdinate(i, d); - if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue } - if (Double.isNaN(v1) && Double.isNaN(v2)) { continue } - return false - } - } - return true - }; - CoordinateSequences.extend = function extend (fact, seq, size) { - var newseq = fact.create(size, seq.getDimension()); - var n = seq.size(); - CoordinateSequences.copy(seq, 0, newseq, 0, n); - if (n > 0) { - for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); } - } - return newseq - }; - CoordinateSequences.reverse = function reverse (seq) { - var last = seq.size() - 1; - var mid = Math.trunc(last / 2); - for (var i = 0; i <= mid; i++) { - CoordinateSequences.swap(seq, i, last - i); - } - }; - CoordinateSequences.swap = function swap (seq, i, j) { - if (i === j) { return null } - for (var dim = 0; dim < seq.getDimension(); dim++) { - var tmp = seq.getOrdinate(i, dim); - seq.setOrdinate(i, dim, seq.getOrdinate(j, dim)); - seq.setOrdinate(j, dim, tmp); - } - }; - CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) { - for (var i = 0; i < length; i++) { - CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i); - } - }; - CoordinateSequences.toString = function toString () { - if (arguments.length === 1) { - var cs = arguments[0]; - var size = cs.size(); - if (size === 0) { return '()' } - var dim = cs.getDimension(); - var buf = new StringBuffer(); - buf.append('('); - for (var i = 0; i < size; i++) { - if (i > 0) { buf.append(' '); } - for (var d = 0; d < dim; d++) { - if (d > 0) { buf.append(','); } - buf.append(StringUtil.toString(cs.getOrdinate(i, d))); - } - } - buf.append(')'); - return buf.toString() - } - }; - CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) { - var n = seq.size(); - if (n === 0) { return seq } - if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) } - var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y); - if (isClosed) { return seq } - return CoordinateSequences.createClosedRing(fact, seq, n + 1) - }; - CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) { - var newseq = fact.create(size, seq.getDimension()); - var n = seq.size(); - CoordinateSequences.copy(seq, 0, newseq, 0, n); - for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); } - return newseq - }; - - var LineString = (function (Geometry$$1) { - function LineString (points, factory) { - Geometry$$1.call(this, factory); - this._points = null; - this.init(points); - } - - if ( Geometry$$1 ) LineString.__proto__ = Geometry$$1; - LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype ); - LineString.prototype.constructor = LineString; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () { - if (this.isEmpty()) { - return new Envelope() - } - return this._points.expandEnvelope(new Envelope()) - }; - LineString.prototype.isRing = function isRing () { - return this.isClosed() && this.isSimple() - }; - LineString.prototype.getSortIndex = function getSortIndex () { - return Geometry$$1.SORTINDEX_LINESTRING - }; - LineString.prototype.getCoordinates = function getCoordinates () { - return this._points.toCoordinateArray() - }; - LineString.prototype.equalsExact = function equalsExact () { - var this$1 = this; - - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - var otherLineString = other; - if (this._points.size() !== otherLineString._points.size()) { - return false - } - for (var i = 0; i < this._points.size(); i++) { - if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) { - return false - } - } - return true - } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) } - }; - LineString.prototype.normalize = function normalize () { - var this$1 = this; - - for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) { - var j = this$1._points.size() - 1 - i; - if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) { - if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) { - CoordinateSequences.reverse(this$1._points); - } - return null - } - } - }; - LineString.prototype.getCoordinate = function getCoordinate () { - if (this.isEmpty()) { return null } - return this._points.getCoordinate(0) - }; - LineString.prototype.getBoundaryDimension = function getBoundaryDimension () { - if (this.isClosed()) { - return Dimension.FALSE - } - return 0 - }; - LineString.prototype.isClosed = function isClosed () { - if (this.isEmpty()) { - return false - } - return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1)) - }; - LineString.prototype.getEndPoint = function getEndPoint () { - if (this.isEmpty()) { - return null - } - return this.getPointN(this.getNumPoints() - 1) - }; - LineString.prototype.getDimension = function getDimension () { - return 1 - }; - LineString.prototype.getLength = function getLength () { - return CGAlgorithms.computeLength(this._points) - }; - LineString.prototype.getNumPoints = function getNumPoints () { - return this._points.size() - }; - LineString.prototype.reverse = function reverse () { - var seq = this._points.copy(); - CoordinateSequences.reverse(seq); - var revLine = this.getFactory().createLineString(seq); - return revLine - }; - LineString.prototype.compareToSameClass = function compareToSameClass () { - var this$1 = this; - - if (arguments.length === 1) { - var o = arguments[0]; - var line = o; - var i = 0; - var j = 0; - while (i < this._points.size() && j < line._points.size()) { - var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j)); - if (comparison !== 0) { - return comparison - } - i++; - j++; - } - if (i < this._points.size()) { - return 1 - } - if (j < line._points.size()) { - return -1 - } - return 0 - } else if (arguments.length === 2) { - var o$1 = arguments[0]; - var comp = arguments[1]; - var line$1 = o$1; - return comp.compare(this._points, line$1._points) - } - }; - LineString.prototype.apply = function apply () { - var this$1 = this; - - if (hasInterface(arguments[0], CoordinateFilter)) { - var filter = arguments[0]; - for (var i = 0; i < this._points.size(); i++) { - filter.filter(this$1._points.getCoordinate(i)); - } - } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) { - var filter$1 = arguments[0]; - if (this._points.size() === 0) { return null } - for (var i$1 = 0; i$1 < this._points.size(); i$1++) { - filter$1.filter(this$1._points, i$1); - if (filter$1.isDone()) { break } - } - if (filter$1.isGeometryChanged()) { this.geometryChanged(); } - } else if (hasInterface(arguments[0], GeometryFilter)) { - var filter$2 = arguments[0]; - filter$2.filter(this); - } else if (hasInterface(arguments[0], GeometryComponentFilter)) { - var filter$3 = arguments[0]; - filter$3.filter(this); - } - }; - LineString.prototype.getBoundary = function getBoundary () { - return new BoundaryOp(this).getBoundary() - }; - LineString.prototype.isEquivalentClass = function isEquivalentClass (other) { - return other instanceof LineString - }; - LineString.prototype.clone = function clone () { - var ls = Geometry$$1.prototype.clone.call(this); - ls._points = this._points.clone(); - return ls - }; - LineString.prototype.getCoordinateN = function getCoordinateN (n) { - return this._points.getCoordinate(n) - }; - LineString.prototype.getGeometryType = function getGeometryType () { - return 'LineString' - }; - LineString.prototype.copy = function copy () { - return new LineString(this._points.copy(), this._factory) - }; - LineString.prototype.getCoordinateSequence = function getCoordinateSequence () { - return this._points - }; - LineString.prototype.isEmpty = function isEmpty () { - return this._points.size() === 0 - }; - LineString.prototype.init = function init (points) { - if (points === null) { - points = this.getFactory().getCoordinateSequenceFactory().create([]); - } - if (points.size() === 1) { - throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)') - } - this._points = points; - }; - LineString.prototype.isCoordinate = function isCoordinate (pt) { - var this$1 = this; - - for (var i = 0; i < this._points.size(); i++) { - if (this$1._points.getCoordinate(i).equals(pt)) { - return true - } - } - return false - }; - LineString.prototype.getStartPoint = function getStartPoint () { - if (this.isEmpty()) { - return null - } - return this.getPointN(0) - }; - LineString.prototype.getPointN = function getPointN (n) { - return this.getFactory().createPoint(this._points.getCoordinate(n)) - }; - LineString.prototype.interfaces_ = function interfaces_ () { - return [Lineal] - }; - LineString.prototype.getClass = function getClass () { - return LineString - }; - staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 }; - - Object.defineProperties( LineString, staticAccessors ); - - return LineString; - }(Geometry)); - - var Puntal = function Puntal () {}; - - Puntal.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Puntal.prototype.getClass = function getClass () { - return Puntal - }; - - var Point$1 = (function (Geometry$$1) { - function Point (coordinates, factory) { - Geometry$$1.call(this, factory); - this._coordinates = coordinates || null; - this.init(this._coordinates); - } - - if ( Geometry$$1 ) Point.__proto__ = Geometry$$1; - Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype ); - Point.prototype.constructor = Point; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () { - if (this.isEmpty()) { - return new Envelope() - } - var env = new Envelope(); - env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0)); - return env - }; - Point.prototype.getSortIndex = function getSortIndex () { - return Geometry$$1.SORTINDEX_POINT - }; - Point.prototype.getCoordinates = function getCoordinates () { - return this.isEmpty() ? [] : [this.getCoordinate()] - }; - Point.prototype.equalsExact = function equalsExact () { - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - if (this.isEmpty() && other.isEmpty()) { - return true - } - if (this.isEmpty() !== other.isEmpty()) { - return false - } - return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance) - } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) } - }; - Point.prototype.normalize = function normalize () {}; - Point.prototype.getCoordinate = function getCoordinate () { - return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null - }; - Point.prototype.getBoundaryDimension = function getBoundaryDimension () { - return Dimension.FALSE - }; - Point.prototype.getDimension = function getDimension () { - return 0 - }; - Point.prototype.getNumPoints = function getNumPoints () { - return this.isEmpty() ? 0 : 1 - }; - Point.prototype.reverse = function reverse () { - return this.copy() - }; - Point.prototype.getX = function getX () { - if (this.getCoordinate() === null) { - throw new Error('getX called on empty Point') - } - return this.getCoordinate().x - }; - Point.prototype.compareToSameClass = function compareToSameClass () { - if (arguments.length === 1) { - var other = arguments[0]; - var point$1 = other; - return this.getCoordinate().compareTo(point$1.getCoordinate()) - } else if (arguments.length === 2) { - var other$1 = arguments[0]; - var comp = arguments[1]; - var point = other$1; - return comp.compare(this._coordinates, point._coordinates) - } - }; - Point.prototype.apply = function apply () { - if (hasInterface(arguments[0], CoordinateFilter)) { - var filter = arguments[0]; - if (this.isEmpty()) { - return null - } - filter.filter(this.getCoordinate()); - } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) { - var filter$1 = arguments[0]; - if (this.isEmpty()) { return null } - filter$1.filter(this._coordinates, 0); - if (filter$1.isGeometryChanged()) { this.geometryChanged(); } - } else if (hasInterface(arguments[0], GeometryFilter)) { - var filter$2 = arguments[0]; - filter$2.filter(this); - } else if (hasInterface(arguments[0], GeometryComponentFilter)) { - var filter$3 = arguments[0]; - filter$3.filter(this); - } - }; - Point.prototype.getBoundary = function getBoundary () { - return this.getFactory().createGeometryCollection(null) - }; - Point.prototype.clone = function clone () { - var p = Geometry$$1.prototype.clone.call(this); - p._coordinates = this._coordinates.clone(); - return p - }; - Point.prototype.getGeometryType = function getGeometryType () { - return 'Point' - }; - Point.prototype.copy = function copy () { - return new Point(this._coordinates.copy(), this._factory) - }; - Point.prototype.getCoordinateSequence = function getCoordinateSequence () { - return this._coordinates - }; - Point.prototype.getY = function getY () { - if (this.getCoordinate() === null) { - throw new Error('getY called on empty Point') - } - return this.getCoordinate().y - }; - Point.prototype.isEmpty = function isEmpty () { - return this._coordinates.size() === 0 - }; - Point.prototype.init = function init (coordinates) { - if (coordinates === null) { - coordinates = this.getFactory().getCoordinateSequenceFactory().create([]); - } - Assert.isTrue(coordinates.size() <= 1); - this._coordinates = coordinates; - }; - Point.prototype.isSimple = function isSimple () { - return true - }; - Point.prototype.interfaces_ = function interfaces_ () { - return [Puntal] - }; - Point.prototype.getClass = function getClass () { - return Point - }; - staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 }; - - Object.defineProperties( Point, staticAccessors ); - - return Point; - }(Geometry)); - - var Polygonal = function Polygonal () {}; - - Polygonal.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Polygonal.prototype.getClass = function getClass () { - return Polygonal - }; - - var Polygon = (function (Geometry$$1) { - function Polygon (shell, holes, factory) { - Geometry$$1.call(this, factory); - this._shell = null; - this._holes = null; - if (shell === null) { - shell = this.getFactory().createLinearRing(); - } - if (holes === null) { - holes = []; - } - if (Geometry$$1.hasNullElements(holes)) { - throw new IllegalArgumentException('holes must not contain null elements') - } - if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) { - throw new IllegalArgumentException('shell is empty but holes are not') - } - this._shell = shell; - this._holes = holes; - } - - if ( Geometry$$1 ) Polygon.__proto__ = Geometry$$1; - Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype ); - Polygon.prototype.constructor = Polygon; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () { - return this._shell.getEnvelopeInternal() - }; - Polygon.prototype.getSortIndex = function getSortIndex () { - return Geometry$$1.SORTINDEX_POLYGON - }; - Polygon.prototype.getCoordinates = function getCoordinates () { - var this$1 = this; - - if (this.isEmpty()) { - return [] - } - var coordinates = new Array(this.getNumPoints()).fill(null); - var k = -1; - var shellCoordinates = this._shell.getCoordinates(); - for (var x = 0; x < shellCoordinates.length; x++) { - k++; - coordinates[k] = shellCoordinates[x]; - } - for (var i = 0; i < this._holes.length; i++) { - var childCoordinates = this$1._holes[i].getCoordinates(); - for (var j = 0; j < childCoordinates.length; j++) { - k++; - coordinates[k] = childCoordinates[j]; - } - } - return coordinates - }; - Polygon.prototype.getArea = function getArea () { - var this$1 = this; - - var area = 0.0; - area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence())); - for (var i = 0; i < this._holes.length; i++) { - area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence())); - } - return area - }; - Polygon.prototype.isRectangle = function isRectangle () { - if (this.getNumInteriorRing() !== 0) { return false } - if (this._shell === null) { return false } - if (this._shell.getNumPoints() !== 5) { return false } - var seq = this._shell.getCoordinateSequence(); - var env = this.getEnvelopeInternal(); - for (var i = 0; i < 5; i++) { - var x = seq.getX(i); - if (!(x === env.getMinX() || x === env.getMaxX())) { return false } - var y = seq.getY(i); - if (!(y === env.getMinY() || y === env.getMaxY())) { return false } - } - var prevX = seq.getX(0); - var prevY = seq.getY(0); - for (var i$1 = 1; i$1 <= 4; i$1++) { - var x$1 = seq.getX(i$1); - var y$1 = seq.getY(i$1); - var xChanged = x$1 !== prevX; - var yChanged = y$1 !== prevY; - if (xChanged === yChanged) { return false } - prevX = x$1; - prevY = y$1; - } - return true - }; - Polygon.prototype.equalsExact = function equalsExact () { - var this$1 = this; - - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - var otherPolygon = other; - var thisShell = this._shell; - var otherPolygonShell = otherPolygon._shell; - if (!thisShell.equalsExact(otherPolygonShell, tolerance)) { - return false - } - if (this._holes.length !== otherPolygon._holes.length) { - return false - } - for (var i = 0; i < this._holes.length; i++) { - if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) { - return false - } - } - return true - } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) } - }; - Polygon.prototype.normalize = function normalize () { - var this$1 = this; - - if (arguments.length === 0) { - this.normalize(this._shell, true); - for (var i = 0; i < this._holes.length; i++) { - this$1.normalize(this$1._holes[i], false); - } - Arrays.sort(this._holes); - } else if (arguments.length === 2) { - var ring = arguments[0]; - var clockwise = arguments[1]; - if (ring.isEmpty()) { - return null - } - var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null); - System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length); - var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates()); - CoordinateArrays.scroll(uniqueCoordinates, minCoordinate); - System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length); - ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0]; - if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) { - CoordinateArrays.reverse(ring.getCoordinates()); - } - } - }; - Polygon.prototype.getCoordinate = function getCoordinate () { - return this._shell.getCoordinate() - }; - Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () { - return this._holes.length - }; - Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () { - return 1 - }; - Polygon.prototype.getDimension = function getDimension () { - return 2 - }; - Polygon.prototype.getLength = function getLength () { - var this$1 = this; - - var len = 0.0; - len += this._shell.getLength(); - for (var i = 0; i < this._holes.length; i++) { - len += this$1._holes[i].getLength(); - } - return len - }; - Polygon.prototype.getNumPoints = function getNumPoints () { - var this$1 = this; - - var numPoints = this._shell.getNumPoints(); - for (var i = 0; i < this._holes.length; i++) { - numPoints += this$1._holes[i].getNumPoints(); - } - return numPoints - }; - Polygon.prototype.reverse = function reverse () { - var this$1 = this; - - var poly = this.copy(); - poly._shell = this._shell.copy().reverse(); - poly._holes = new Array(this._holes.length).fill(null); - for (var i = 0; i < this._holes.length; i++) { - poly._holes[i] = this$1._holes[i].copy().reverse(); - } - return poly - }; - Polygon.prototype.convexHull = function convexHull () { - return this.getExteriorRing().convexHull() - }; - Polygon.prototype.compareToSameClass = function compareToSameClass () { - var this$1 = this; - - if (arguments.length === 1) { - var o = arguments[0]; - var thisShell = this._shell; - var otherShell = o._shell; - return thisShell.compareToSameClass(otherShell) - } else if (arguments.length === 2) { - var o$1 = arguments[0]; - var comp = arguments[1]; - var poly = o$1; - var thisShell$1 = this._shell; - var otherShell$1 = poly._shell; - var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp); - if (shellComp !== 0) { return shellComp } - var nHole1 = this.getNumInteriorRing(); - var nHole2 = poly.getNumInteriorRing(); - var i = 0; - while (i < nHole1 && i < nHole2) { - var thisHole = this$1.getInteriorRingN(i); - var otherHole = poly.getInteriorRingN(i); - var holeComp = thisHole.compareToSameClass(otherHole, comp); - if (holeComp !== 0) { return holeComp } - i++; - } - if (i < nHole1) { return 1 } - if (i < nHole2) { return -1 } - return 0 - } - }; - Polygon.prototype.apply = function apply (filter) { - var this$1 = this; - - if (hasInterface(filter, CoordinateFilter)) { - this._shell.apply(filter); - for (var i$1 = 0; i$1 < this._holes.length; i$1++) { - this$1._holes[i$1].apply(filter); - } - } else if (hasInterface(filter, CoordinateSequenceFilter)) { - this._shell.apply(filter); - if (!filter.isDone()) { - for (var i$2 = 0; i$2 < this._holes.length; i$2++) { - this$1._holes[i$2].apply(filter); - if (filter.isDone()) { break } - } - } - if (filter.isGeometryChanged()) { this.geometryChanged(); } - } else if (hasInterface(filter, GeometryFilter)) { - filter.filter(this); - } else if (hasInterface(filter, GeometryComponentFilter)) { - filter.filter(this); - this._shell.apply(filter); - for (var i = 0; i < this._holes.length; i++) { - this$1._holes[i].apply(filter); - } - } - }; - Polygon.prototype.getBoundary = function getBoundary () { - var this$1 = this; - - if (this.isEmpty()) { - return this.getFactory().createMultiLineString() - } - var rings = new Array(this._holes.length + 1).fill(null); - rings[0] = this._shell; - for (var i = 0; i < this._holes.length; i++) { - rings[i + 1] = this$1._holes[i]; - } - if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) } - return this.getFactory().createMultiLineString(rings) - }; - Polygon.prototype.clone = function clone () { - var this$1 = this; - - var poly = Geometry$$1.prototype.clone.call(this); - poly._shell = this._shell.clone(); - poly._holes = new Array(this._holes.length).fill(null); - for (var i = 0; i < this._holes.length; i++) { - poly._holes[i] = this$1._holes[i].clone(); - } - return poly - }; - Polygon.prototype.getGeometryType = function getGeometryType () { - return 'Polygon' - }; - Polygon.prototype.copy = function copy () { - var this$1 = this; - - var shell = this._shell.copy(); - var holes = new Array(this._holes.length).fill(null); - for (var i = 0; i < holes.length; i++) { - holes[i] = this$1._holes[i].copy(); - } - return new Polygon(shell, holes, this._factory) - }; - Polygon.prototype.getExteriorRing = function getExteriorRing () { - return this._shell - }; - Polygon.prototype.isEmpty = function isEmpty () { - return this._shell.isEmpty() - }; - Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) { - return this._holes[n] - }; - Polygon.prototype.interfaces_ = function interfaces_ () { - return [Polygonal] - }; - Polygon.prototype.getClass = function getClass () { - return Polygon - }; - staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 }; - - Object.defineProperties( Polygon, staticAccessors ); - - return Polygon; - }(Geometry)); - - var MultiPoint = (function (GeometryCollection$$1) { - function MultiPoint () { - GeometryCollection$$1.apply(this, arguments); - } - - if ( GeometryCollection$$1 ) MultiPoint.__proto__ = GeometryCollection$$1; - MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype ); - MultiPoint.prototype.constructor = MultiPoint; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - - MultiPoint.prototype.getSortIndex = function getSortIndex () { - return Geometry.SORTINDEX_MULTIPOINT - }; - MultiPoint.prototype.isValid = function isValid () { - return true - }; - MultiPoint.prototype.equalsExact = function equalsExact () { - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance) - } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) } - }; - MultiPoint.prototype.getCoordinate = function getCoordinate () { - if (arguments.length === 1) { - var n = arguments[0]; - return this._geometries[n].getCoordinate() - } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) } - }; - MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () { - return Dimension.FALSE - }; - MultiPoint.prototype.getDimension = function getDimension () { - return 0 - }; - MultiPoint.prototype.getBoundary = function getBoundary () { - return this.getFactory().createGeometryCollection(null) - }; - MultiPoint.prototype.getGeometryType = function getGeometryType () { - return 'MultiPoint' - }; - MultiPoint.prototype.copy = function copy () { - var this$1 = this; - - var points = new Array(this._geometries.length).fill(null); - for (var i = 0; i < points.length; i++) { - points[i] = this$1._geometries[i].copy(); - } - return new MultiPoint(points, this._factory) - }; - MultiPoint.prototype.interfaces_ = function interfaces_ () { - return [Puntal] - }; - MultiPoint.prototype.getClass = function getClass () { - return MultiPoint - }; - staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 }; - - Object.defineProperties( MultiPoint, staticAccessors ); - - return MultiPoint; - }(GeometryCollection)); - - var LinearRing = (function (LineString$$1) { - function LinearRing (points, factory) { - if (points instanceof Coordinate && factory instanceof GeometryFactory) { - points = factory.getCoordinateSequenceFactory().create(points); - } - LineString$$1.call(this, points, factory); - this.validateConstruction(); - } - - if ( LineString$$1 ) LinearRing.__proto__ = LineString$$1; - LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype ); - LinearRing.prototype.constructor = LinearRing; - - var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } }; - LinearRing.prototype.getSortIndex = function getSortIndex () { - return Geometry.SORTINDEX_LINEARRING - }; - LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () { - return Dimension.FALSE - }; - LinearRing.prototype.isClosed = function isClosed () { - if (this.isEmpty()) { - return true - } - return LineString$$1.prototype.isClosed.call(this) - }; - LinearRing.prototype.reverse = function reverse () { - var seq = this._points.copy(); - CoordinateSequences.reverse(seq); - var rev = this.getFactory().createLinearRing(seq); - return rev - }; - LinearRing.prototype.validateConstruction = function validateConstruction () { - if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) { - throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring') - } - if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) { - throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)') - } - }; - LinearRing.prototype.getGeometryType = function getGeometryType () { - return 'LinearRing' - }; - LinearRing.prototype.copy = function copy () { - return new LinearRing(this._points.copy(), this._factory) - }; - LinearRing.prototype.interfaces_ = function interfaces_ () { - return [] - }; - LinearRing.prototype.getClass = function getClass () { - return LinearRing - }; - staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 }; - staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 }; - - Object.defineProperties( LinearRing, staticAccessors ); - - return LinearRing; - }(LineString)); - - var MultiPolygon = (function (GeometryCollection$$1) { - function MultiPolygon () { - GeometryCollection$$1.apply(this, arguments); - } - - if ( GeometryCollection$$1 ) MultiPolygon.__proto__ = GeometryCollection$$1; - MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype ); - MultiPolygon.prototype.constructor = MultiPolygon; - - var staticAccessors = { serialVersionUID: { configurable: true } }; - - MultiPolygon.prototype.getSortIndex = function getSortIndex () { - return Geometry.SORTINDEX_MULTIPOLYGON - }; - MultiPolygon.prototype.equalsExact = function equalsExact () { - if (arguments.length === 2) { - var other = arguments[0]; - var tolerance = arguments[1]; - if (!this.isEquivalentClass(other)) { - return false - } - return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance) - } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) } - }; - MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () { - return 1 - }; - MultiPolygon.prototype.getDimension = function getDimension () { - return 2 - }; - MultiPolygon.prototype.reverse = function reverse () { - var this$1 = this; - - var n = this._geometries.length; - var revGeoms = new Array(n).fill(null); - for (var i = 0; i < this._geometries.length; i++) { - revGeoms[i] = this$1._geometries[i].reverse(); - } - return this.getFactory().createMultiPolygon(revGeoms) - }; - MultiPolygon.prototype.getBoundary = function getBoundary () { - var this$1 = this; - - if (this.isEmpty()) { - return this.getFactory().createMultiLineString() - } - var allRings = new ArrayList(); - for (var i = 0; i < this._geometries.length; i++) { - var polygon = this$1._geometries[i]; - var rings = polygon.getBoundary(); - for (var j = 0; j < rings.getNumGeometries(); j++) { - allRings.add(rings.getGeometryN(j)); - } - } - var allRingsArray = new Array(allRings.size()).fill(null); - return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray)) - }; - MultiPolygon.prototype.getGeometryType = function getGeometryType () { - return 'MultiPolygon' - }; - MultiPolygon.prototype.copy = function copy () { - var this$1 = this; - - var polygons = new Array(this._geometries.length).fill(null); - for (var i = 0; i < polygons.length; i++) { - polygons[i] = this$1._geometries[i].copy(); - } - return new MultiPolygon(polygons, this._factory) - }; - MultiPolygon.prototype.interfaces_ = function interfaces_ () { - return [Polygonal] - }; - MultiPolygon.prototype.getClass = function getClass () { - return MultiPolygon - }; - staticAccessors.serialVersionUID.get = function () { return -551033529766975875 }; - - Object.defineProperties( MultiPolygon, staticAccessors ); - - return MultiPolygon; - }(GeometryCollection)); - - var GeometryEditor = function GeometryEditor (factory) { - this._factory = factory || null; - this._isUserDataCopied = false; - }; - - var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } }; - GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) { - this._isUserDataCopied = isUserDataCopied; - }; - GeometryEditor.prototype.edit = function edit (geometry, operation) { - if (geometry === null) { return null } - var result = this.editInternal(geometry, operation); - if (this._isUserDataCopied) { - result.setUserData(geometry.getUserData()); - } - return result - }; - GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) { - if (this._factory === null) { this._factory = geometry.getFactory(); } - if (geometry instanceof GeometryCollection) { - return this.editGeometryCollection(geometry, operation) - } - if (geometry instanceof Polygon) { - return this.editPolygon(geometry, operation) - } - if (geometry instanceof Point$1) { - return operation.edit(geometry, this._factory) - } - if (geometry instanceof LineString) { - return operation.edit(geometry, this._factory) - } - Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName()); - return null - }; - GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) { - var this$1 = this; - - var collectionForType = operation.edit(collection, this._factory); - var geometries = new ArrayList(); - for (var i = 0; i < collectionForType.getNumGeometries(); i++) { - var geometry = this$1.edit(collectionForType.getGeometryN(i), operation); - if (geometry === null || geometry.isEmpty()) { - continue - } - geometries.add(geometry); - } - if (collectionForType.getClass() === MultiPoint) { - return this._factory.createMultiPoint(geometries.toArray([])) - } - if (collectionForType.getClass() === MultiLineString) { - return this._factory.createMultiLineString(geometries.toArray([])) - } - if (collectionForType.getClass() === MultiPolygon) { - return this._factory.createMultiPolygon(geometries.toArray([])) - } - return this._factory.createGeometryCollection(geometries.toArray([])) - }; - GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) { - var this$1 = this; - - var newPolygon = operation.edit(polygon, this._factory); - if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); } - if (newPolygon.isEmpty()) { - return newPolygon - } - var shell = this.edit(newPolygon.getExteriorRing(), operation); - if (shell === null || shell.isEmpty()) { - return this._factory.createPolygon() - } - var holes = new ArrayList(); - for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) { - var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation); - if (hole === null || hole.isEmpty()) { - continue - } - holes.add(hole); - } - return this._factory.createPolygon(shell, holes.toArray([])) - }; - GeometryEditor.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryEditor.prototype.getClass = function getClass () { - return GeometryEditor - }; - GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {}; - staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation }; - staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation }; - staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation }; - - Object.defineProperties( GeometryEditor, staticAccessors$16 ); - - var NoOpGeometryOperation = function NoOpGeometryOperation () {}; - - NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) { - return geometry - }; - NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () { - return [GeometryEditor.GeometryEditorOperation] - }; - NoOpGeometryOperation.prototype.getClass = function getClass () { - return NoOpGeometryOperation - }; - - var CoordinateOperation = function CoordinateOperation () {}; - - CoordinateOperation.prototype.edit = function edit (geometry, factory) { - var coords = this.editCoordinates(geometry.getCoordinates(), geometry); - if (coords === null) { return geometry } - if (geometry instanceof LinearRing) { - return factory.createLinearRing(coords) - } - if (geometry instanceof LineString) { - return factory.createLineString(coords) - } - if (geometry instanceof Point$1) { - if (coords.length > 0) { - return factory.createPoint(coords[0]) - } else { - return factory.createPoint() - } - } - return geometry - }; - CoordinateOperation.prototype.interfaces_ = function interfaces_ () { - return [GeometryEditor.GeometryEditorOperation] - }; - CoordinateOperation.prototype.getClass = function getClass () { - return CoordinateOperation - }; - - var CoordinateSequenceOperation = function CoordinateSequenceOperation () {}; - - CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) { - if (geometry instanceof LinearRing) { - return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry)) - } - if (geometry instanceof LineString) { - return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry)) - } - if (geometry instanceof Point$1) { - return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry)) - } - return geometry - }; - CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () { - return [GeometryEditor.GeometryEditorOperation] - }; - CoordinateSequenceOperation.prototype.getClass = function getClass () { - return CoordinateSequenceOperation - }; - - var CoordinateArraySequence = function CoordinateArraySequence () { - var this$1 = this; - - this._dimension = 3; - this._coordinates = null; - if (arguments.length === 1) { - if (arguments[0] instanceof Array) { - this._coordinates = arguments[0]; - this._dimension = 3; - } else if (Number.isInteger(arguments[0])) { - var size = arguments[0]; - this._coordinates = new Array(size).fill(null); - for (var i = 0; i < size; i++) { - this$1._coordinates[i] = new Coordinate(); - } - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var coordSeq = arguments[0]; - if (coordSeq === null) { - this._coordinates = new Array(0).fill(null); - return null - } - this._dimension = coordSeq.getDimension(); - this._coordinates = new Array(coordSeq.size()).fill(null); - for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) { - this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1); - } - } - } else if (arguments.length === 2) { - if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) { - var coordinates = arguments[0]; - var dimension = arguments[1]; - this._coordinates = coordinates; - this._dimension = dimension; - if (coordinates === null) { this._coordinates = new Array(0).fill(null); } - } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) { - var size$1 = arguments[0]; - var dimension$1 = arguments[1]; - this._coordinates = new Array(size$1).fill(null); - this._dimension = dimension$1; - for (var i$2 = 0; i$2 < size$1; i$2++) { - this$1._coordinates[i$2] = new Coordinate(); - } - } - } - }; - - var staticAccessors$18 = { serialVersionUID: { configurable: true } }; - CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) { - switch (ordinateIndex) { - case CoordinateSequence.X: - this._coordinates[index].x = value; - break - case CoordinateSequence.Y: - this._coordinates[index].y = value; - break - case CoordinateSequence.Z: - this._coordinates[index].z = value; - break - default: - throw new IllegalArgumentException('invalid ordinateIndex') - } - }; - CoordinateArraySequence.prototype.size = function size () { - return this._coordinates.length - }; - CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) { - switch (ordinateIndex) { - case CoordinateSequence.X: - return this._coordinates[index].x - case CoordinateSequence.Y: - return this._coordinates[index].y - case CoordinateSequence.Z: - return this._coordinates[index].z - } - return Double.NaN - }; - CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () { - if (arguments.length === 1) { - var i = arguments[0]; - return this._coordinates[i] - } else if (arguments.length === 2) { - var index = arguments[0]; - var coord = arguments[1]; - coord.x = this._coordinates[index].x; - coord.y = this._coordinates[index].y; - coord.z = this._coordinates[index].z; - } - }; - CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) { - return new Coordinate(this._coordinates[i]) - }; - CoordinateArraySequence.prototype.getDimension = function getDimension () { - return this._dimension - }; - CoordinateArraySequence.prototype.getX = function getX (index) { - return this._coordinates[index].x - }; - CoordinateArraySequence.prototype.clone = function clone () { - var this$1 = this; - - var cloneCoordinates = new Array(this.size()).fill(null); - for (var i = 0; i < this._coordinates.length; i++) { - cloneCoordinates[i] = this$1._coordinates[i].clone(); - } - return new CoordinateArraySequence(cloneCoordinates, this._dimension) - }; - CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) { - var this$1 = this; - - for (var i = 0; i < this._coordinates.length; i++) { - env.expandToInclude(this$1._coordinates[i]); - } - return env - }; - CoordinateArraySequence.prototype.copy = function copy () { - var this$1 = this; - - var cloneCoordinates = new Array(this.size()).fill(null); - for (var i = 0; i < this._coordinates.length; i++) { - cloneCoordinates[i] = this$1._coordinates[i].copy(); - } - return new CoordinateArraySequence(cloneCoordinates, this._dimension) - }; - CoordinateArraySequence.prototype.toString = function toString () { - var this$1 = this; - - if (this._coordinates.length > 0) { - var strBuf = new StringBuffer(17 * this._coordinates.length); - strBuf.append('('); - strBuf.append(this._coordinates[0]); - for (var i = 1; i < this._coordinates.length; i++) { - strBuf.append(', '); - strBuf.append(this$1._coordinates[i]); - } - strBuf.append(')'); - return strBuf.toString() - } else { - return '()' - } - }; - CoordinateArraySequence.prototype.getY = function getY (index) { - return this._coordinates[index].y - }; - CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () { - return this._coordinates - }; - CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () { - return [CoordinateSequence, Serializable] - }; - CoordinateArraySequence.prototype.getClass = function getClass () { - return CoordinateArraySequence - }; - staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 }; - - Object.defineProperties( CoordinateArraySequence, staticAccessors$18 ); - - var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {}; - - var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } }; - - CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () { - return CoordinateArraySequenceFactory.instance() - }; - CoordinateArraySequenceFactory.prototype.create = function create () { - if (arguments.length === 1) { - if (arguments[0] instanceof Array) { - var coordinates = arguments[0]; - return new CoordinateArraySequence(coordinates) - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var coordSeq = arguments[0]; - return new CoordinateArraySequence(coordSeq) - } - } else if (arguments.length === 2) { - var size = arguments[0]; - var dimension = arguments[1]; - if (dimension > 3) { dimension = 3; } - if (dimension < 2) { return new CoordinateArraySequence(size) } - return new CoordinateArraySequence(size, dimension) - } - }; - CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () { - return [CoordinateSequenceFactory, Serializable] - }; - CoordinateArraySequenceFactory.prototype.getClass = function getClass () { - return CoordinateArraySequenceFactory - }; - CoordinateArraySequenceFactory.instance = function instance () { - return CoordinateArraySequenceFactory.instanceObject - }; - - staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 }; - staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() }; - - Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 ); - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html - * - * @extends {javascript.util.Map} - * @constructor - * @private - */ - var HashMap = (function (MapInterface) { - function HashMap () { - MapInterface.call(this); - this.map_ = new Map(); - } - - if ( MapInterface ) HashMap.__proto__ = MapInterface; - HashMap.prototype = Object.create( MapInterface && MapInterface.prototype ); - HashMap.prototype.constructor = HashMap; - /** - * @override - */ - HashMap.prototype.get = function get (key) { - return this.map_.get(key) || null - }; - - /** - * @override - */ - HashMap.prototype.put = function put (key, value) { - this.map_.set(key, value); - return value - }; - - /** - * @override - */ - HashMap.prototype.values = function values () { - var arrayList = new ArrayList(); - var it = this.map_.values(); - var o = it.next(); - while (!o.done) { - arrayList.add(o.value); - o = it.next(); - } - return arrayList - }; - - /** - * @override - */ - HashMap.prototype.entrySet = function entrySet () { - var hashSet = new HashSet(); - this.map_.entries().forEach(function (entry) { return hashSet.add(entry); }); - return hashSet - }; - - /** - * @override - */ - HashMap.prototype.size = function size () { - return this.map_.size() - }; - - return HashMap; - }(Map$1$1)); - - var PrecisionModel = function PrecisionModel () { - this._modelType = null; - this._scale = null; - if (arguments.length === 0) { - this._modelType = PrecisionModel.FLOATING; - } else if (arguments.length === 1) { - if (arguments[0] instanceof Type$2) { - var modelType = arguments[0]; - this._modelType = modelType; - if (modelType === PrecisionModel.FIXED) { - this.setScale(1.0); - } - } else if (typeof arguments[0] === 'number') { - var scale = arguments[0]; - this._modelType = PrecisionModel.FIXED; - this.setScale(scale); - } else if (arguments[0] instanceof PrecisionModel) { - var pm = arguments[0]; - this._modelType = pm._modelType; - this._scale = pm._scale; - } - } - }; - - var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } }; - PrecisionModel.prototype.equals = function equals (other) { - if (!(other instanceof PrecisionModel)) { - return false - } - var otherPrecisionModel = other; - return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale - }; - PrecisionModel.prototype.compareTo = function compareTo (o) { - var other = o; - var sigDigits = this.getMaximumSignificantDigits(); - var otherSigDigits = other.getMaximumSignificantDigits(); - return new Integer(sigDigits).compareTo(new Integer(otherSigDigits)) - }; - PrecisionModel.prototype.getScale = function getScale () { - return this._scale - }; - PrecisionModel.prototype.isFloating = function isFloating () { - return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE - }; - PrecisionModel.prototype.getType = function getType () { - return this._modelType - }; - PrecisionModel.prototype.toString = function toString () { - var description = 'UNKNOWN'; - if (this._modelType === PrecisionModel.FLOATING) { - description = 'Floating'; - } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) { - description = 'Floating-Single'; - } else if (this._modelType === PrecisionModel.FIXED) { - description = 'Fixed (Scale=' + this.getScale() + ')'; - } - return description - }; - PrecisionModel.prototype.makePrecise = function makePrecise () { - if (typeof arguments[0] === 'number') { - var val = arguments[0]; - if (Double.isNaN(val)) { return val } - if (this._modelType === PrecisionModel.FLOATING_SINGLE) { - var floatSingleVal = val; - return floatSingleVal - } - if (this._modelType === PrecisionModel.FIXED) { - return Math.round(val * this._scale) / this._scale - } - return val - } else if (arguments[0] instanceof Coordinate) { - var coord = arguments[0]; - if (this._modelType === PrecisionModel.FLOATING) { return null } - coord.x = this.makePrecise(coord.x); - coord.y = this.makePrecise(coord.y); - } - }; - PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () { - var maxSigDigits = 16; - if (this._modelType === PrecisionModel.FLOATING) { - maxSigDigits = 16; - } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) { - maxSigDigits = 6; - } else if (this._modelType === PrecisionModel.FIXED) { - maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10))); - } - return maxSigDigits - }; - PrecisionModel.prototype.setScale = function setScale (scale) { - this._scale = Math.abs(scale); - }; - PrecisionModel.prototype.interfaces_ = function interfaces_ () { - return [Serializable, Comparable] - }; - PrecisionModel.prototype.getClass = function getClass () { - return PrecisionModel - }; - PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) { - if (pm1.compareTo(pm2) >= 0) { return pm1 } - return pm2 - }; - staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 }; - staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 }; - - Object.defineProperties( PrecisionModel, staticAccessors$19 ); - - var Type$2 = function Type (name) { - this._name = name || null; - Type.nameToTypeMap.put(name, this); - }; - - var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } }; - Type$2.prototype.readResolve = function readResolve () { - return Type$2.nameToTypeMap.get(this._name) - }; - Type$2.prototype.toString = function toString () { - return this._name - }; - Type$2.prototype.interfaces_ = function interfaces_ () { - return [Serializable] - }; - Type$2.prototype.getClass = function getClass () { - return Type$2 - }; - staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 }; - staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() }; - - Object.defineProperties( Type$2, staticAccessors$1$1 ); - - PrecisionModel.Type = Type$2; - PrecisionModel.FIXED = new Type$2('FIXED'); - PrecisionModel.FLOATING = new Type$2('FLOATING'); - PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE'); - - var GeometryFactory = function GeometryFactory () { - this._precisionModel = new PrecisionModel(); - this._SRID = 0; - this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory(); - - if (arguments.length === 0) ; else if (arguments.length === 1) { - if (hasInterface(arguments[0], CoordinateSequenceFactory)) { - this._coordinateSequenceFactory = arguments[0]; - } else if (arguments[0] instanceof PrecisionModel) { - this._precisionModel = arguments[0]; - } - } else if (arguments.length === 2) { - this._precisionModel = arguments[0]; - this._SRID = arguments[1]; - } else if (arguments.length === 3) { - this._precisionModel = arguments[0]; - this._SRID = arguments[1]; - this._coordinateSequenceFactory = arguments[2]; - } - }; - - var staticAccessors$2 = { serialVersionUID: { configurable: true } }; - GeometryFactory.prototype.toGeometry = function toGeometry (envelope) { - if (envelope.isNull()) { - return this.createPoint(null) - } - if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) { - return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY())) - } - if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) { - return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())]) - } - return this.createPolygon(this.createLinearRing([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMinX(), envelope.getMaxY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY()), new Coordinate(envelope.getMaxX(), envelope.getMinY()), new Coordinate(envelope.getMinX(), envelope.getMinY())]), null) - }; - GeometryFactory.prototype.createLineString = function createLineString (coordinates) { - if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) } - else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) } - else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) } - }; - GeometryFactory.prototype.createMultiLineString = function createMultiLineString () { - if (arguments.length === 0) { - return new MultiLineString(null, this) - } else if (arguments.length === 1) { - var lineStrings = arguments[0]; - return new MultiLineString(lineStrings, this) - } - }; - GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) { - var geomClass = null; - var isHeterogeneous = false; - var hasGeometryCollection = false; - for (var i = geomList.iterator(); i.hasNext();) { - var geom = i.next(); - var partClass = geom.getClass(); - if (geomClass === null) { - geomClass = partClass; - } - if (partClass !== geomClass) { - isHeterogeneous = true; - } - if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; } - } - if (geomClass === null) { - return this.createGeometryCollection() - } - if (isHeterogeneous || hasGeometryCollection) { - return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList)) - } - var geom0 = geomList.iterator().next(); - var isCollection = geomList.size() > 1; - if (isCollection) { - if (geom0 instanceof Polygon) { - return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList)) - } else if (geom0 instanceof LineString) { - return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList)) - } else if (geom0 instanceof Point$1) { - return this.createMultiPoint(GeometryFactory.toPointArray(geomList)) - } - Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName()); - } - return geom0 - }; - GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) { - return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null) - }; - GeometryFactory.prototype.createPoint = function createPoint () { - if (arguments.length === 0) { - return this.createPoint(this.getCoordinateSequenceFactory().create([])) - } else if (arguments.length === 1) { - if (arguments[0] instanceof Coordinate) { - var coordinate = arguments[0]; - return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null) - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var coordinates = arguments[0]; - return new Point$1(coordinates, this) - } - } - }; - GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () { - return this._coordinateSequenceFactory - }; - GeometryFactory.prototype.createPolygon = function createPolygon () { - if (arguments.length === 0) { - return new Polygon(null, null, this) - } else if (arguments.length === 1) { - if (hasInterface(arguments[0], CoordinateSequence)) { - var coordinates = arguments[0]; - return this.createPolygon(this.createLinearRing(coordinates)) - } else if (arguments[0] instanceof Array) { - var coordinates$1 = arguments[0]; - return this.createPolygon(this.createLinearRing(coordinates$1)) - } else if (arguments[0] instanceof LinearRing) { - var shell = arguments[0]; - return this.createPolygon(shell, null) - } - } else if (arguments.length === 2) { - var shell$1 = arguments[0]; - var holes = arguments[1]; - return new Polygon(shell$1, holes, this) - } - }; - GeometryFactory.prototype.getSRID = function getSRID () { - return this._SRID - }; - GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () { - if (arguments.length === 0) { - return new GeometryCollection(null, this) - } else if (arguments.length === 1) { - var geometries = arguments[0]; - return new GeometryCollection(geometries, this) - } - }; - GeometryFactory.prototype.createGeometry = function createGeometry (g) { - var editor = new GeometryEditor(this); - return editor.edit(g, { - edit: function () { - if (arguments.length === 2) { - var coordSeq = arguments[0]; - // const geometry = arguments[1] - return this._coordinateSequenceFactory.create(coordSeq) - } - } - }) - }; - GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () { - return this._precisionModel - }; - GeometryFactory.prototype.createLinearRing = function createLinearRing () { - if (arguments.length === 0) { - return this.createLinearRing(this.getCoordinateSequenceFactory().create([])) - } else if (arguments.length === 1) { - if (arguments[0] instanceof Array) { - var coordinates = arguments[0]; - return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null) - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var coordinates$1 = arguments[0]; - return new LinearRing(coordinates$1, this) - } - } - }; - GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () { - if (arguments.length === 0) { - return new MultiPolygon(null, this) - } else if (arguments.length === 1) { - var polygons = arguments[0]; - return new MultiPolygon(polygons, this) - } - }; - GeometryFactory.prototype.createMultiPoint = function createMultiPoint () { - var this$1 = this; - - if (arguments.length === 0) { - return new MultiPoint(null, this) - } else if (arguments.length === 1) { - if (arguments[0] instanceof Array) { - var point = arguments[0]; - return new MultiPoint(point, this) - } else if (arguments[0] instanceof Array) { - var coordinates = arguments[0]; - return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null) - } else if (hasInterface(arguments[0], CoordinateSequence)) { - var coordinates$1 = arguments[0]; - if (coordinates$1 === null) { - return this.createMultiPoint(new Array(0).fill(null)) - } - var points = new Array(coordinates$1.size()).fill(null); - for (var i = 0; i < coordinates$1.size(); i++) { - var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension()); - CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1); - points[i] = this$1.createPoint(ptSeq); - } - return this.createMultiPoint(points) - } - } - }; - GeometryFactory.prototype.interfaces_ = function interfaces_ () { - return [Serializable] - }; - GeometryFactory.prototype.getClass = function getClass () { - return GeometryFactory - }; - GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) { - var multiPolygonArray = new Array(multiPolygons.size()).fill(null); - return multiPolygons.toArray(multiPolygonArray) - }; - GeometryFactory.toGeometryArray = function toGeometryArray (geometries) { - if (geometries === null) { return null } - var geometryArray = new Array(geometries.size()).fill(null); - return geometries.toArray(geometryArray) - }; - GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () { - return CoordinateArraySequenceFactory.instance() - }; - GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) { - var multiLineStringArray = new Array(multiLineStrings.size()).fill(null); - return multiLineStrings.toArray(multiLineStringArray) - }; - GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) { - var lineStringArray = new Array(lineStrings.size()).fill(null); - return lineStrings.toArray(lineStringArray) - }; - GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) { - var multiPointArray = new Array(multiPoints.size()).fill(null); - return multiPoints.toArray(multiPointArray) - }; - GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) { - var linearRingArray = new Array(linearRings.size()).fill(null); - return linearRings.toArray(linearRingArray) - }; - GeometryFactory.toPointArray = function toPointArray (points) { - var pointArray = new Array(points.size()).fill(null); - return points.toArray(pointArray) - }; - GeometryFactory.toPolygonArray = function toPolygonArray (polygons) { - var polygonArray = new Array(polygons.size()).fill(null); - return polygons.toArray(polygonArray) - }; - GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) { - exemplar.getPrecisionModel().makePrecise(coord); - return exemplar.getFactory().createPoint(coord) - }; - staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 }; - - Object.defineProperties( GeometryFactory, staticAccessors$2 ); - - var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon']; - - /** - * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON - * NOTE: Adapted from OpenLayers 2.11 implementation. - */ - - /** - * Create a new parser for GeoJSON - * - * @param {GeometryFactory} geometryFactory - * @return An instance of GeoJsonParser. - * @constructor - * @private - */ - var GeoJSONParser = function GeoJSONParser (geometryFactory) { - this.geometryFactory = geometryFactory || new GeometryFactory(); - }; - /** - * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries - * - * @param {} - * A GeoJSON object. - * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances. - * @private - */ - GeoJSONParser.prototype.read = function read (json) { - var obj; - if (typeof json === 'string') { - obj = JSON.parse(json); - } else { - obj = json; - } - - var type = obj.type; - - if (!parse$2[type]) { - throw new Error('Unknown GeoJSON type: ' + obj.type) - } - - if (geometryTypes.indexOf(type) !== -1) { - return parse$2[type].apply(this, [obj.coordinates]) - } else if (type === 'GeometryCollection') { - return parse$2[type].apply(this, [obj.geometries]) - } - - // feature or feature collection - return parse$2[type].apply(this, [obj]) - }; - - /** - * Serialize a Geometry object into GeoJSON - * - * @param {Geometry} - * geometry A Geometry or array of Geometries. - * @return {Object} A GeoJSON object represting the input Geometry/Geometries. - * @private - */ - GeoJSONParser.prototype.write = function write (geometry) { - var type = geometry.getGeometryType(); - - if (!extract[type]) { - throw new Error('Geometry is not supported') - } - - return extract[type].apply(this, [geometry]) - }; - - var parse$2 = { - /** - * Parse a GeoJSON Feature object - * - * @param {Object} - * obj Object to parse. - * - * @return {Object} Feature with geometry/bbox converted to JSTS Geometries. - */ - Feature: function (obj) { - var feature = {}; - - // copy features - for (var key in obj) { - feature[key] = obj[key]; - } - - // parse geometry - if (obj.geometry) { - var type = obj.geometry.type; - if (!parse$2[type]) { - throw new Error('Unknown GeoJSON type: ' + obj.type) - } - feature.geometry = this.read(obj.geometry); - } - - // bbox - if (obj.bbox) { - feature.bbox = parse$2.bbox.apply(this, [obj.bbox]); - } - - return feature - }, - - /** - * Parse a GeoJSON FeatureCollection object - * - * @param {Object} - * obj Object to parse. - * - * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries. - */ - FeatureCollection: function (obj) { - var this$1 = this; - - var featureCollection = {}; - - if (obj.features) { - featureCollection.features = []; - - for (var i = 0; i < obj.features.length; ++i) { - featureCollection.features.push(this$1.read(obj.features[i])); - } - } - - if (obj.bbox) { - featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]); - } - - return featureCollection - }, - - /** - * Convert the ordinates in an array to an array of Coordinates - * - * @param {Array} - * array Array with {Number}s. - * - * @return {Array} Array with Coordinates. - */ - coordinates: function (array) { - var coordinates = []; - for (var i = 0; i < array.length; ++i) { - var sub = array[i]; - coordinates.push(new Coordinate(sub[0], sub[1])); - } - return coordinates - }, - - /** - * Convert the bbox to a LinearRing - * - * @param {Array} - * array Array with [xMin, yMin, xMax, yMax]. - * - * @return {Array} Array with Coordinates. - */ - bbox: function (array) { - return this.geometryFactory.createLinearRing([ - new Coordinate(array[0], array[1]), - new Coordinate(array[2], array[1]), - new Coordinate(array[2], array[3]), - new Coordinate(array[0], array[3]), - new Coordinate(array[0], array[1]) - ]) - }, - - /** - * Convert an Array with ordinates to a Point - * - * @param {Array} - * array Array with ordinates. - * - * @return {Point} Point. - */ - Point: function (array) { - var coordinate = new Coordinate(array[0], array[1]); - return this.geometryFactory.createPoint(coordinate) - }, - - /** - * Convert an Array with coordinates to a MultiPoint - * - * @param {Array} - * array Array with coordinates. - * - * @return {MultiPoint} MultiPoint. - */ - MultiPoint: function (array) { - var this$1 = this; - - var points = []; - for (var i = 0; i < array.length; ++i) { - points.push(parse$2.Point.apply(this$1, [array[i]])); - } - return this.geometryFactory.createMultiPoint(points) - }, - - /** - * Convert an Array with coordinates to a LineString - * - * @param {Array} - * array Array with coordinates. - * - * @return {LineString} LineString. - */ - LineString: function (array) { - var coordinates = parse$2.coordinates.apply(this, [array]); - return this.geometryFactory.createLineString(coordinates) - }, - - /** - * Convert an Array with coordinates to a MultiLineString - * - * @param {Array} - * array Array with coordinates. - * - * @return {MultiLineString} MultiLineString. - */ - MultiLineString: function (array) { - var this$1 = this; - - var lineStrings = []; - for (var i = 0; i < array.length; ++i) { - lineStrings.push(parse$2.LineString.apply(this$1, [array[i]])); - } - return this.geometryFactory.createMultiLineString(lineStrings) - }, - - /** - * Convert an Array to a Polygon - * - * @param {Array} - * array Array with shell and holes. - * - * @return {Polygon} Polygon. - */ - Polygon: function (array) { - var this$1 = this; - - var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]); - var shell = this.geometryFactory.createLinearRing(shellCoordinates); - var holes = []; - for (var i = 1; i < array.length; ++i) { - var hole = array[i]; - var coordinates = parse$2.coordinates.apply(this$1, [hole]); - var linearRing = this$1.geometryFactory.createLinearRing(coordinates); - holes.push(linearRing); - } - return this.geometryFactory.createPolygon(shell, holes) - }, - - /** - * Convert an Array to a MultiPolygon - * - * @param {Array} - * array Array of arrays with shell and rings. - * - * @return {MultiPolygon} MultiPolygon. - */ - MultiPolygon: function (array) { - var this$1 = this; - - var polygons = []; - for (var i = 0; i < array.length; ++i) { - var polygon = array[i]; - polygons.push(parse$2.Polygon.apply(this$1, [polygon])); - } - return this.geometryFactory.createMultiPolygon(polygons) - }, - - /** - * Convert an Array to a GeometryCollection - * - * @param {Array} - * array Array of GeoJSON geometries. - * - * @return {GeometryCollection} GeometryCollection. - */ - GeometryCollection: function (array) { - var this$1 = this; - - var geometries = []; - for (var i = 0; i < array.length; ++i) { - var geometry = array[i]; - geometries.push(this$1.read(geometry)); - } - return this.geometryFactory.createGeometryCollection(geometries) - } - }; - - var extract = { - /** - * Convert a Coordinate to an Array - * - * @param {Coordinate} - * coordinate Coordinate to convert. - * - * @return {Array} Array of ordinates. - */ - coordinate: function (coordinate) { - return [coordinate.x, coordinate.y] - }, - - /** - * Convert a Point to a GeoJSON object - * - * @param {Point} - * point Point to convert. - * - * @return {Array} Array of 2 ordinates (paired to a coordinate). - */ - Point: function (point) { - var array = extract.coordinate.apply(this, [point.getCoordinate()]); - return { - type: 'Point', - coordinates: array - } - }, - - /** - * Convert a MultiPoint to a GeoJSON object - * - * @param {MultiPoint} - * multipoint MultiPoint to convert. - * - * @return {Array} Array of coordinates. - */ - MultiPoint: function (multipoint) { - var this$1 = this; - - var array = []; - for (var i = 0; i < multipoint._geometries.length; ++i) { - var point = multipoint._geometries[i]; - var geoJson = extract.Point.apply(this$1, [point]); - array.push(geoJson.coordinates); - } - return { - type: 'MultiPoint', - coordinates: array - } - }, - - /** - * Convert a LineString to a GeoJSON object - * - * @param {LineString} - * linestring LineString to convert. - * - * @return {Array} Array of coordinates. - */ - LineString: function (linestring) { - var this$1 = this; - - var array = []; - var coordinates = linestring.getCoordinates(); - for (var i = 0; i < coordinates.length; ++i) { - var coordinate = coordinates[i]; - array.push(extract.coordinate.apply(this$1, [coordinate])); - } - return { - type: 'LineString', - coordinates: array - } - }, - - /** - * Convert a MultiLineString to a GeoJSON object - * - * @param {MultiLineString} - * multilinestring MultiLineString to convert. - * - * @return {Array} Array of Array of coordinates. - */ - MultiLineString: function (multilinestring) { - var this$1 = this; - - var array = []; - for (var i = 0; i < multilinestring._geometries.length; ++i) { - var linestring = multilinestring._geometries[i]; - var geoJson = extract.LineString.apply(this$1, [linestring]); - array.push(geoJson.coordinates); - } - return { - type: 'MultiLineString', - coordinates: array - } - }, - - /** - * Convert a Polygon to a GeoJSON object - * - * @param {Polygon} - * polygon Polygon to convert. - * - * @return {Array} Array with shell, holes. - */ - Polygon: function (polygon) { - var this$1 = this; - - var array = []; - var shellGeoJson = extract.LineString.apply(this, [polygon._shell]); - array.push(shellGeoJson.coordinates); - for (var i = 0; i < polygon._holes.length; ++i) { - var hole = polygon._holes[i]; - var holeGeoJson = extract.LineString.apply(this$1, [hole]); - array.push(holeGeoJson.coordinates); - } - return { - type: 'Polygon', - coordinates: array - } - }, - - /** - * Convert a MultiPolygon to a GeoJSON object - * - * @param {MultiPolygon} - * multipolygon MultiPolygon to convert. - * - * @return {Array} Array of polygons. - */ - MultiPolygon: function (multipolygon) { - var this$1 = this; - - var array = []; - for (var i = 0; i < multipolygon._geometries.length; ++i) { - var polygon = multipolygon._geometries[i]; - var geoJson = extract.Polygon.apply(this$1, [polygon]); - array.push(geoJson.coordinates); - } - return { - type: 'MultiPolygon', - coordinates: array - } - }, - - /** - * Convert a GeometryCollection to a GeoJSON object - * - * @param {GeometryCollection} - * collection GeometryCollection to convert. - * - * @return {Array} Array of geometries. - */ - GeometryCollection: function (collection) { - var this$1 = this; - - var array = []; - for (var i = 0; i < collection._geometries.length; ++i) { - var geometry = collection._geometries[i]; - var type = geometry.getGeometryType(); - array.push(extract[type].apply(this$1, [geometry])); - } - return { - type: 'GeometryCollection', - geometries: array - } - } - }; - - /** - * Converts a geometry in GeoJSON to a {@link Geometry}. - */ - - /** - * A GeoJSONReader is parameterized by a GeometryFactory, - * to allow it to create Geometry objects of the appropriate - * implementation. In particular, the GeometryFactory determines - * the PrecisionModel and SRID that is used. - * - * @param {GeometryFactory} geometryFactory - * @constructor - */ - var GeoJSONReader = function GeoJSONReader (geometryFactory) { - this.geometryFactory = geometryFactory || new GeometryFactory(); - this.precisionModel = this.geometryFactory.getPrecisionModel(); - this.parser = new GeoJSONParser(this.geometryFactory); - }; - /** - * Reads a GeoJSON representation of a {@link Geometry} - * - * Will also parse GeoJSON Features/FeatureCollections as custom objects. - * - * @param {Object|String} geoJson a GeoJSON Object or String. - * @return {Geometry|Object} a Geometry or Feature/FeatureCollection representation. - * @memberof GeoJSONReader - */ - GeoJSONReader.prototype.read = function read (geoJson) { - var geometry = this.parser.read(geoJson); - - if (this.precisionModel.getType() === PrecisionModel.FIXED) { - this.reducePrecision(geometry); - } - - return geometry - }; - - // NOTE: this is a hack - GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) { - var this$1 = this; - - var i, len; - - if (geometry.coordinate) { - this.precisionModel.makePrecise(geometry.coordinate); - } else if (geometry.points) { - for (i = 0, len = geometry.points.length; i < len; i++) { - this$1.precisionModel.makePrecise(geometry.points[i]); - } - } else if (geometry.geometries) { - for (i = 0, len = geometry.geometries.length; i < len; i++) { - this$1.reducePrecision(geometry.geometries[i]); - } - } - }; - - /** - * @module GeoJSONWriter - */ - - /** - * Writes the GeoJSON representation of a {@link Geometry}. The - * The GeoJSON format is defined here. - */ - - /** - * The GeoJSONWriter outputs coordinates rounded to the precision - * model. Only the maximum number of decimal places necessary to represent the - * ordinates to the required precision will be output. - * - * @param {GeometryFactory} geometryFactory - * @constructor - */ - var GeoJSONWriter = function GeoJSONWriter () { - this.parser = new GeoJSONParser(this.geometryFactory); - }; - /** - * Converts a Geometry to its GeoJSON representation. - * - * @param {Geometry} - * geometry a Geometry to process. - * @return {Object} The GeoJSON representation of the Geometry. - * @memberof GeoJSONWriter - */ - GeoJSONWriter.prototype.write = function write (geometry) { - return this.parser.write(geometry) - }; - - /* eslint-disable no-undef */ - - // io - - var Position = function Position () {}; - - var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } }; - - Position.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Position.prototype.getClass = function getClass () { - return Position - }; - Position.opposite = function opposite (position) { - if (position === Position.LEFT) { return Position.RIGHT } - if (position === Position.RIGHT) { return Position.LEFT } - return position - }; - staticAccessors$20.ON.get = function () { return 0 }; - staticAccessors$20.LEFT.get = function () { return 1 }; - staticAccessors$20.RIGHT.get = function () { return 2 }; - - Object.defineProperties( Position, staticAccessors$20 ); - - /** - * @param {string=} message Optional message - * @extends {Error} - * @constructor - * @private - */ - function EmptyStackException (message) { - this.message = message || ''; - } - EmptyStackException.prototype = new Error(); - - /** - * @type {string} - */ - EmptyStackException.prototype.name = 'EmptyStackException'; - - /** - * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html - * - * @extends {List} - * @constructor - * @private - */ - function Stack () { - /** - * @type {Array} - * @private - */ - this.array_ = []; - } - Stack.prototype = new List(); - - /** - * @override - */ - Stack.prototype.add = function (e) { - this.array_.push(e); - return true - }; - - /** - * @override - */ - Stack.prototype.get = function (index) { - if (index < 0 || index >= this.size()) { - throw new Error() - } - - return this.array_[index] - }; - - /** - * Pushes an item onto the top of this stack. - * @param {Object} e - * @return {Object} - */ - Stack.prototype.push = function (e) { - this.array_.push(e); - return e - }; - - /** - * Pushes an item onto the top of this stack. - * @param {Object} e - * @return {Object} - */ - Stack.prototype.pop = function (e) { - if (this.array_.length === 0) { - throw new EmptyStackException() - } - - return this.array_.pop() - }; - - /** - * Looks at the object at the top of this stack without removing it from the - * stack. - * @return {Object} - */ - Stack.prototype.peek = function () { - if (this.array_.length === 0) { - throw new EmptyStackException() - } - - return this.array_[this.array_.length - 1] - }; - - /** - * Tests if this stack is empty. - * @return {boolean} true if and only if this stack contains no items; false - * otherwise. - */ - Stack.prototype.empty = function () { - if (this.array_.length === 0) { - return true - } else { - return false - } - }; - - /** - * @return {boolean} - */ - Stack.prototype.isEmpty = function () { - return this.empty() - }; - - /** - * Returns the 1-based position where an object is on this stack. If the object - * o occurs as an item in this stack, this method returns the distance from the - * top of the stack of the occurrence nearest the top of the stack; the topmost - * item on the stack is considered to be at distance 1. The equals method is - * used to compare o to the items in this stack. - * - * NOTE: does not currently actually use equals. (=== is used) - * - * @param {Object} o - * @return {number} the 1-based position from the top of the stack where the - * object is located; the return value -1 indicates that the object is - * not on the stack. - */ - Stack.prototype.search = function (o) { - return this.array_.indexOf(o) - }; - - /** - * @return {number} - * @export - */ - Stack.prototype.size = function () { - return this.array_.length - }; - - /** - * @return {Array} - */ - Stack.prototype.toArray = function () { - var this$1 = this; - - var array = []; - - for (var i = 0, len = this.array_.length; i < len; i++) { - array.push(this$1.array_[i]); - } - - return array - }; - - var RightmostEdgeFinder = function RightmostEdgeFinder () { - this._minIndex = -1; - this._minCoord = null; - this._minDe = null; - this._orientedDe = null; - }; - RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () { - return this._minCoord - }; - RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) { - var side = this.getRightmostSideOfSegment(de, index); - if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); } - if (side < 0) { - this._minCoord = null; - this.checkForRightmostCoordinate(de); - } - return side - }; - RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () { - var pts = this._minDe.getEdge().getCoordinates(); - Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge'); - var pPrev = pts[this._minIndex - 1]; - var pNext = pts[this._minIndex + 1]; - var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev); - var usePrev = false; - if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) { - usePrev = true; - } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) { - usePrev = true; - } - if (usePrev) { - this._minIndex = this._minIndex - 1; - } - }; - RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) { - var e = de.getEdge(); - var coord = e.getCoordinates(); - if (i < 0 || i + 1 >= coord.length) { return -1 } - if (coord[i].y === coord[i + 1].y) { return -1 } - var pos = Position.LEFT; - if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; } - return pos - }; - RightmostEdgeFinder.prototype.getEdge = function getEdge () { - return this._orientedDe - }; - RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) { - var this$1 = this; - - var coord = de.getEdge().getCoordinates(); - for (var i = 0; i < coord.length - 1; i++) { - if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) { - this$1._minDe = de; - this$1._minIndex = i; - this$1._minCoord = coord[i]; - } - } - }; - RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () { - var node = this._minDe.getNode(); - var star = node.getEdges(); - this._minDe = star.getRightmostEdge(); - if (!this._minDe.isForward()) { - this._minDe = this._minDe.getSym(); - this._minIndex = this._minDe.getEdge().getCoordinates().length - 1; - } - }; - RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) { - var this$1 = this; - - for (var i = dirEdgeList.iterator(); i.hasNext();) { - var de = i.next(); - if (!de.isForward()) { continue } - this$1.checkForRightmostCoordinate(de); - } - Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing'); - if (this._minIndex === 0) { - this.findRightmostEdgeAtNode(); - } else { - this.findRightmostEdgeAtVertex(); - } - this._orientedDe = this._minDe; - var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex); - if (rightmostSide === Position.LEFT) { - this._orientedDe = this._minDe.getSym(); - } - }; - RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - RightmostEdgeFinder.prototype.getClass = function getClass () { - return RightmostEdgeFinder - }; - - var TopologyException = (function (RuntimeException$$1) { - function TopologyException (msg, pt) { - RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt)); - this.pt = pt ? new Coordinate(pt) : null; - this.name = 'TopologyException'; - } - - if ( RuntimeException$$1 ) TopologyException.__proto__ = RuntimeException$$1; - TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype ); - TopologyException.prototype.constructor = TopologyException; - TopologyException.prototype.getCoordinate = function getCoordinate () { - return this.pt - }; - TopologyException.prototype.interfaces_ = function interfaces_ () { - return [] - }; - TopologyException.prototype.getClass = function getClass () { - return TopologyException - }; - TopologyException.msgWithCoord = function msgWithCoord (msg, pt) { - if (!pt) { return msg + ' [ ' + pt + ' ]' } - return msg - }; - - return TopologyException; - }(RuntimeException)); - - var LinkedList = function LinkedList () { - this.array_ = []; - }; - LinkedList.prototype.addLast = function addLast (e) { - this.array_.push(e); - }; - LinkedList.prototype.removeFirst = function removeFirst () { - return this.array_.shift() - }; - LinkedList.prototype.isEmpty = function isEmpty () { - return this.array_.length === 0 - }; - - var BufferSubgraph = function BufferSubgraph () { - this._finder = null; - this._dirEdgeList = new ArrayList(); - this._nodes = new ArrayList(); - this._rightMostCoord = null; - this._env = null; - this._finder = new RightmostEdgeFinder(); - }; - BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () { - for (var it = this._dirEdgeList.iterator(); it.hasNext();) { - var de = it.next(); - de.setVisited(false); - } - }; - BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () { - return this._rightMostCoord - }; - BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) { - var this$1 = this; - - var startEdge = null; - for (var i = n.getEdges().iterator(); i.hasNext();) { - var de = i.next(); - if (de.isVisited() || de.getSym().isVisited()) { - startEdge = de; - break - } - } - if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) } - n.getEdges().computeDepths(startEdge); - for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) { - var de$1 = i$1.next(); - de$1.setVisited(true); - this$1.copySymDepths(de$1); - } - }; - BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) { - this.clearVisitedEdges(); - var de = this._finder.getEdge(); - // const n = de.getNode() - // const label = de.getLabel() - de.setEdgeDepths(Position.RIGHT, outsideDepth); - this.copySymDepths(de); - this.computeDepths(de); - }; - BufferSubgraph.prototype.create = function create (node) { - this.addReachable(node); - this._finder.findEdge(this._dirEdgeList); - this._rightMostCoord = this._finder.getCoordinate(); - }; - BufferSubgraph.prototype.findResultEdges = function findResultEdges () { - for (var it = this._dirEdgeList.iterator(); it.hasNext();) { - var de = it.next(); - if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) { - de.setInResult(true); - } - } - }; - BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) { - var this$1 = this; - - var nodesVisited = new HashSet(); - var nodeQueue = new LinkedList(); - var startNode = startEdge.getNode(); - nodeQueue.addLast(startNode); - nodesVisited.add(startNode); - startEdge.setVisited(true); - while (!nodeQueue.isEmpty()) { - var n = nodeQueue.removeFirst(); - nodesVisited.add(n); - this$1.computeNodeDepth(n); - for (var i = n.getEdges().iterator(); i.hasNext();) { - var de = i.next(); - var sym = de.getSym(); - if (sym.isVisited()) { continue } - var adjNode = sym.getNode(); - if (!nodesVisited.contains(adjNode)) { - nodeQueue.addLast(adjNode); - nodesVisited.add(adjNode); - } - } - } - }; - BufferSubgraph.prototype.compareTo = function compareTo (o) { - var graph = o; - if (this._rightMostCoord.x < graph._rightMostCoord.x) { - return -1 - } - if (this._rightMostCoord.x > graph._rightMostCoord.x) { - return 1 - } - return 0 - }; - BufferSubgraph.prototype.getEnvelope = function getEnvelope () { - if (this._env === null) { - var edgeEnv = new Envelope(); - for (var it = this._dirEdgeList.iterator(); it.hasNext();) { - var dirEdge = it.next(); - var pts = dirEdge.getEdge().getCoordinates(); - for (var i = 0; i < pts.length - 1; i++) { - edgeEnv.expandToInclude(pts[i]); - } - } - this._env = edgeEnv; - } - return this._env - }; - BufferSubgraph.prototype.addReachable = function addReachable (startNode) { - var this$1 = this; - - var nodeStack = new Stack(); - nodeStack.add(startNode); - while (!nodeStack.empty()) { - var node = nodeStack.pop(); - this$1.add(node, nodeStack); - } - }; - BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) { - var sym = de.getSym(); - sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT)); - sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT)); - }; - BufferSubgraph.prototype.add = function add (node, nodeStack) { - var this$1 = this; - - node.setVisited(true); - this._nodes.add(node); - for (var i = node.getEdges().iterator(); i.hasNext();) { - var de = i.next(); - this$1._dirEdgeList.add(de); - var sym = de.getSym(); - var symNode = sym.getNode(); - if (!symNode.isVisited()) { nodeStack.push(symNode); } - } - }; - BufferSubgraph.prototype.getNodes = function getNodes () { - return this._nodes - }; - BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () { - return this._dirEdgeList - }; - BufferSubgraph.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - BufferSubgraph.prototype.getClass = function getClass () { - return BufferSubgraph - }; - - var TopologyLocation = function TopologyLocation () { - var this$1 = this; - - this.location = null; - if (arguments.length === 1) { - if (arguments[0] instanceof Array) { - var location = arguments[0]; - this.init(location.length); - } else if (Number.isInteger(arguments[0])) { - var on = arguments[0]; - this.init(1); - this.location[Position.ON] = on; - } else if (arguments[0] instanceof TopologyLocation) { - var gl = arguments[0]; - this.init(gl.location.length); - if (gl !== null) { - for (var i = 0; i < this.location.length; i++) { - this$1.location[i] = gl.location[i]; - } - } - } - } else if (arguments.length === 3) { - var on$1 = arguments[0]; - var left = arguments[1]; - var right = arguments[2]; - this.init(3); - this.location[Position.ON] = on$1; - this.location[Position.LEFT] = left; - this.location[Position.RIGHT] = right; - } - }; - TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) { - var this$1 = this; - - for (var i = 0; i < this.location.length; i++) { - this$1.location[i] = locValue; - } - }; - TopologyLocation.prototype.isNull = function isNull () { - var this$1 = this; - - for (var i = 0; i < this.location.length; i++) { - if (this$1.location[i] !== Location.NONE) { return false } - } - return true - }; - TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) { - var this$1 = this; - - for (var i = 0; i < this.location.length; i++) { - if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; } - } - }; - TopologyLocation.prototype.isLine = function isLine () { - return this.location.length === 1 - }; - TopologyLocation.prototype.merge = function merge (gl) { - var this$1 = this; - - if (gl.location.length > this.location.length) { - var newLoc = new Array(3).fill(null); - newLoc[Position.ON] = this.location[Position.ON]; - newLoc[Position.LEFT] = Location.NONE; - newLoc[Position.RIGHT] = Location.NONE; - this.location = newLoc; - } - for (var i = 0; i < this.location.length; i++) { - if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; } - } - }; - TopologyLocation.prototype.getLocations = function getLocations () { - return this.location - }; - TopologyLocation.prototype.flip = function flip () { - if (this.location.length <= 1) { return null } - var temp = this.location[Position.LEFT]; - this.location[Position.LEFT] = this.location[Position.RIGHT]; - this.location[Position.RIGHT] = temp; - }; - TopologyLocation.prototype.toString = function toString () { - var buf = new StringBuffer(); - if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); } - buf.append(Location.toLocationSymbol(this.location[Position.ON])); - if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); } - return buf.toString() - }; - TopologyLocation.prototype.setLocations = function setLocations (on, left, right) { - this.location[Position.ON] = on; - this.location[Position.LEFT] = left; - this.location[Position.RIGHT] = right; - }; - TopologyLocation.prototype.get = function get (posIndex) { - if (posIndex < this.location.length) { return this.location[posIndex] } - return Location.NONE - }; - TopologyLocation.prototype.isArea = function isArea () { - return this.location.length > 1 - }; - TopologyLocation.prototype.isAnyNull = function isAnyNull () { - var this$1 = this; - - for (var i = 0; i < this.location.length; i++) { - if (this$1.location[i] === Location.NONE) { return true } - } - return false - }; - TopologyLocation.prototype.setLocation = function setLocation () { - if (arguments.length === 1) { - var locValue = arguments[0]; - this.setLocation(Position.ON, locValue); - } else if (arguments.length === 2) { - var locIndex = arguments[0]; - var locValue$1 = arguments[1]; - this.location[locIndex] = locValue$1; - } - }; - TopologyLocation.prototype.init = function init (size) { - this.location = new Array(size).fill(null); - this.setAllLocations(Location.NONE); - }; - TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) { - return this.location[locIndex] === le.location[locIndex] - }; - TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) { - var this$1 = this; - - for (var i = 0; i < this.location.length; i++) { - if (this$1.location[i] !== loc) { return false } - } - return true - }; - TopologyLocation.prototype.interfaces_ = function interfaces_ () { - return [] - }; - TopologyLocation.prototype.getClass = function getClass () { - return TopologyLocation - }; - - var Label = function Label () { - this.elt = new Array(2).fill(null); - if (arguments.length === 1) { - if (Number.isInteger(arguments[0])) { - var onLoc = arguments[0]; - this.elt[0] = new TopologyLocation(onLoc); - this.elt[1] = new TopologyLocation(onLoc); - } else if (arguments[0] instanceof Label) { - var lbl = arguments[0]; - this.elt[0] = new TopologyLocation(lbl.elt[0]); - this.elt[1] = new TopologyLocation(lbl.elt[1]); - } - } else if (arguments.length === 2) { - var geomIndex = arguments[0]; - var onLoc$1 = arguments[1]; - this.elt[0] = new TopologyLocation(Location.NONE); - this.elt[1] = new TopologyLocation(Location.NONE); - this.elt[geomIndex].setLocation(onLoc$1); - } else if (arguments.length === 3) { - var onLoc$2 = arguments[0]; - var leftLoc = arguments[1]; - var rightLoc = arguments[2]; - this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc); - this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc); - } else if (arguments.length === 4) { - var geomIndex$1 = arguments[0]; - var onLoc$3 = arguments[1]; - var leftLoc$1 = arguments[2]; - var rightLoc$1 = arguments[3]; - this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE); - this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE); - this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1); - } - }; - Label.prototype.getGeometryCount = function getGeometryCount () { - var count = 0; - if (!this.elt[0].isNull()) { count++; } - if (!this.elt[1].isNull()) { count++; } - return count - }; - Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) { - this.elt[geomIndex].setAllLocations(location); - }; - Label.prototype.isNull = function isNull (geomIndex) { - return this.elt[geomIndex].isNull() - }; - Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () { - if (arguments.length === 1) { - var location = arguments[0]; - this.setAllLocationsIfNull(0, location); - this.setAllLocationsIfNull(1, location); - } else if (arguments.length === 2) { - var geomIndex = arguments[0]; - var location$1 = arguments[1]; - this.elt[geomIndex].setAllLocationsIfNull(location$1); - } - }; - Label.prototype.isLine = function isLine (geomIndex) { - return this.elt[geomIndex].isLine() - }; - Label.prototype.merge = function merge (lbl) { - var this$1 = this; - - for (var i = 0; i < 2; i++) { - if (this$1.elt[i] === null && lbl.elt[i] !== null) { - this$1.elt[i] = new TopologyLocation(lbl.elt[i]); - } else { - this$1.elt[i].merge(lbl.elt[i]); - } - } - }; - Label.prototype.flip = function flip () { - this.elt[0].flip(); - this.elt[1].flip(); - }; - Label.prototype.getLocation = function getLocation () { - if (arguments.length === 1) { - var geomIndex = arguments[0]; - return this.elt[geomIndex].get(Position.ON) - } else if (arguments.length === 2) { - var geomIndex$1 = arguments[0]; - var posIndex = arguments[1]; - return this.elt[geomIndex$1].get(posIndex) - } - }; - Label.prototype.toString = function toString () { - var buf = new StringBuffer(); - if (this.elt[0] !== null) { - buf.append('A:'); - buf.append(this.elt[0].toString()); - } - if (this.elt[1] !== null) { - buf.append(' B:'); - buf.append(this.elt[1].toString()); - } - return buf.toString() - }; - Label.prototype.isArea = function isArea () { - if (arguments.length === 0) { - return this.elt[0].isArea() || this.elt[1].isArea() - } else if (arguments.length === 1) { - var geomIndex = arguments[0]; - return this.elt[geomIndex].isArea() - } - }; - Label.prototype.isAnyNull = function isAnyNull (geomIndex) { - return this.elt[geomIndex].isAnyNull() - }; - Label.prototype.setLocation = function setLocation () { - if (arguments.length === 2) { - var geomIndex = arguments[0]; - var location = arguments[1]; - this.elt[geomIndex].setLocation(Position.ON, location); - } else if (arguments.length === 3) { - var geomIndex$1 = arguments[0]; - var posIndex = arguments[1]; - var location$1 = arguments[2]; - this.elt[geomIndex$1].setLocation(posIndex, location$1); - } - }; - Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) { - return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side) - }; - Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) { - return this.elt[geomIndex].allPositionsEqual(loc) - }; - Label.prototype.toLine = function toLine (geomIndex) { - if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); } - }; - Label.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Label.prototype.getClass = function getClass () { - return Label - }; - Label.toLineLabel = function toLineLabel (label) { - var lineLabel = new Label(Location.NONE); - for (var i = 0; i < 2; i++) { - lineLabel.setLocation(i, label.getLocation(i)); - } - return lineLabel - }; - - var EdgeRing = function EdgeRing () { - this._startDe = null; - this._maxNodeDegree = -1; - this._edges = new ArrayList(); - this._pts = new ArrayList(); - this._label = new Label(Location.NONE); - this._ring = null; - this._isHole = null; - this._shell = null; - this._holes = new ArrayList(); - this._geometryFactory = null; - var start = arguments[0]; - var geometryFactory = arguments[1]; - this._geometryFactory = geometryFactory; - this.computePoints(start); - this.computeRing(); - }; - EdgeRing.prototype.computeRing = function computeRing () { - var this$1 = this; - - if (this._ring !== null) { return null } - var coord = new Array(this._pts.size()).fill(null); - for (var i = 0; i < this._pts.size(); i++) { - coord[i] = this$1._pts.get(i); - } - this._ring = this._geometryFactory.createLinearRing(coord); - this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates()); - }; - EdgeRing.prototype.isIsolated = function isIsolated () { - return this._label.getGeometryCount() === 1 - }; - EdgeRing.prototype.computePoints = function computePoints (start) { - var this$1 = this; - - this._startDe = start; - var de = start; - var isFirstEdge = true; - do { - if (de === null) { throw new TopologyException('Found null DirectedEdge') } - if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) } - this$1._edges.add(de); - var label = de.getLabel(); - Assert.isTrue(label.isArea()); - this$1.mergeLabel(label); - this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge); - isFirstEdge = false; - this$1.setEdgeRing(de, this$1); - de = this$1.getNext(de); - } while (de !== this._startDe) - }; - EdgeRing.prototype.getLinearRing = function getLinearRing () { - return this._ring - }; - EdgeRing.prototype.getCoordinate = function getCoordinate (i) { - return this._pts.get(i) - }; - EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () { - var this$1 = this; - - this._maxNodeDegree = 0; - var de = this._startDe; - do { - var node = de.getNode(); - var degree = node.getEdges().getOutgoingDegree(this$1); - if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; } - de = this$1.getNext(de); - } while (de !== this._startDe) - this._maxNodeDegree *= 2; - }; - EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) { - var this$1 = this; - - var edgePts = edge.getCoordinates(); - if (isForward) { - var startIndex = 1; - if (isFirstEdge) { startIndex = 0; } - for (var i = startIndex; i < edgePts.length; i++) { - this$1._pts.add(edgePts[i]); - } - } else { - var startIndex$1 = edgePts.length - 2; - if (isFirstEdge) { startIndex$1 = edgePts.length - 1; } - for (var i$1 = startIndex$1; i$1 >= 0; i$1--) { - this$1._pts.add(edgePts[i$1]); - } - } - }; - EdgeRing.prototype.isHole = function isHole () { - return this._isHole - }; - EdgeRing.prototype.setInResult = function setInResult () { - var de = this._startDe; - do { - de.getEdge().setInResult(true); - de = de.getNext(); - } while (de !== this._startDe) - }; - EdgeRing.prototype.containsPoint = function containsPoint (p) { - var shell = this.getLinearRing(); - var env = shell.getEnvelopeInternal(); - if (!env.contains(p)) { return false } - if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false } - for (var i = this._holes.iterator(); i.hasNext();) { - var hole = i.next(); - if (hole.containsPoint(p)) { return false } - } - return true - }; - EdgeRing.prototype.addHole = function addHole (ring) { - this._holes.add(ring); - }; - EdgeRing.prototype.isShell = function isShell () { - return this._shell === null - }; - EdgeRing.prototype.getLabel = function getLabel () { - return this._label - }; - EdgeRing.prototype.getEdges = function getEdges () { - return this._edges - }; - EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () { - if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); } - return this._maxNodeDegree - }; - EdgeRing.prototype.getShell = function getShell () { - return this._shell - }; - EdgeRing.prototype.mergeLabel = function mergeLabel () { - if (arguments.length === 1) { - var deLabel = arguments[0]; - this.mergeLabel(deLabel, 0); - this.mergeLabel(deLabel, 1); - } else if (arguments.length === 2) { - var deLabel$1 = arguments[0]; - var geomIndex = arguments[1]; - var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT); - if (loc === Location.NONE) { return null } - if (this._label.getLocation(geomIndex) === Location.NONE) { - this._label.setLocation(geomIndex, loc); - return null - } - } - }; - EdgeRing.prototype.setShell = function setShell (shell) { - this._shell = shell; - if (shell !== null) { shell.addHole(this); } - }; - EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) { - var this$1 = this; - - var holeLR = new Array(this._holes.size()).fill(null); - for (var i = 0; i < this._holes.size(); i++) { - holeLR[i] = this$1._holes.get(i).getLinearRing(); - } - var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR); - return poly - }; - EdgeRing.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeRing.prototype.getClass = function getClass () { - return EdgeRing - }; - - var MinimalEdgeRing = (function (EdgeRing$$1) { - function MinimalEdgeRing () { - var start = arguments[0]; - var geometryFactory = arguments[1]; - EdgeRing$$1.call(this, start, geometryFactory); - } - - if ( EdgeRing$$1 ) MinimalEdgeRing.__proto__ = EdgeRing$$1; - MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype ); - MinimalEdgeRing.prototype.constructor = MinimalEdgeRing; - MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) { - de.setMinEdgeRing(er); - }; - MinimalEdgeRing.prototype.getNext = function getNext (de) { - return de.getNextMin() - }; - MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MinimalEdgeRing.prototype.getClass = function getClass () { - return MinimalEdgeRing - }; - - return MinimalEdgeRing; - }(EdgeRing)); - - var MaximalEdgeRing = (function (EdgeRing$$1) { - function MaximalEdgeRing () { - var start = arguments[0]; - var geometryFactory = arguments[1]; - EdgeRing$$1.call(this, start, geometryFactory); - } - - if ( EdgeRing$$1 ) MaximalEdgeRing.__proto__ = EdgeRing$$1; - MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype ); - MaximalEdgeRing.prototype.constructor = MaximalEdgeRing; - MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () { - var this$1 = this; - - var minEdgeRings = new ArrayList(); - var de = this._startDe; - do { - if (de.getMinEdgeRing() === null) { - var minEr = new MinimalEdgeRing(de, this$1._geometryFactory); - minEdgeRings.add(minEr); - } - de = de.getNext(); - } while (de !== this._startDe) - return minEdgeRings - }; - MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) { - de.setEdgeRing(er); - }; - MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () { - var this$1 = this; - - var de = this._startDe; - do { - var node = de.getNode(); - node.getEdges().linkMinimalDirectedEdges(this$1); - de = de.getNext(); - } while (de !== this._startDe) - }; - MaximalEdgeRing.prototype.getNext = function getNext (de) { - return de.getNext() - }; - MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MaximalEdgeRing.prototype.getClass = function getClass () { - return MaximalEdgeRing - }; - - return MaximalEdgeRing; - }(EdgeRing)); - - var GraphComponent = function GraphComponent () { - this._label = null; - this._isInResult = false; - this._isCovered = false; - this._isCoveredSet = false; - this._isVisited = false; - if (arguments.length === 0) ; else if (arguments.length === 1) { - var label = arguments[0]; - this._label = label; - } - }; - GraphComponent.prototype.setVisited = function setVisited (isVisited) { - this._isVisited = isVisited; - }; - GraphComponent.prototype.setInResult = function setInResult (isInResult) { - this._isInResult = isInResult; - }; - GraphComponent.prototype.isCovered = function isCovered () { - return this._isCovered - }; - GraphComponent.prototype.isCoveredSet = function isCoveredSet () { - return this._isCoveredSet - }; - GraphComponent.prototype.setLabel = function setLabel (label) { - this._label = label; - }; - GraphComponent.prototype.getLabel = function getLabel () { - return this._label - }; - GraphComponent.prototype.setCovered = function setCovered (isCovered) { - this._isCovered = isCovered; - this._isCoveredSet = true; - }; - GraphComponent.prototype.updateIM = function updateIM (im) { - Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label'); - this.computeIM(im); - }; - GraphComponent.prototype.isInResult = function isInResult () { - return this._isInResult - }; - GraphComponent.prototype.isVisited = function isVisited () { - return this._isVisited - }; - GraphComponent.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GraphComponent.prototype.getClass = function getClass () { - return GraphComponent - }; - - var Node$1 = (function (GraphComponent$$1) { - function Node () { - GraphComponent$$1.call(this); - this._coord = null; - this._edges = null; - var coord = arguments[0]; - var edges = arguments[1]; - this._coord = coord; - this._edges = edges; - this._label = new Label(0, Location.NONE); - } - - if ( GraphComponent$$1 ) Node.__proto__ = GraphComponent$$1; - Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype ); - Node.prototype.constructor = Node; - Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () { - for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) { - var de = it.next(); - if (de.getEdge().isInResult()) { return true } - } - return false - }; - Node.prototype.isIsolated = function isIsolated () { - return this._label.getGeometryCount() === 1 - }; - Node.prototype.getCoordinate = function getCoordinate () { - return this._coord - }; - Node.prototype.print = function print (out) { - out.println('node ' + this._coord + ' lbl: ' + this._label); - }; - Node.prototype.computeIM = function computeIM (im) {}; - Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) { - var loc = Location.NONE; - loc = this._label.getLocation(eltIndex); - if (!label2.isNull(eltIndex)) { - var nLoc = label2.getLocation(eltIndex); - if (loc !== Location.BOUNDARY) { loc = nLoc; } - } - return loc - }; - Node.prototype.setLabel = function setLabel () { - if (arguments.length === 2) { - var argIndex = arguments[0]; - var onLocation = arguments[1]; - if (this._label === null) { - this._label = new Label(argIndex, onLocation); - } else { this._label.setLocation(argIndex, onLocation); } - } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) } - }; - Node.prototype.getEdges = function getEdges () { - return this._edges - }; - Node.prototype.mergeLabel = function mergeLabel () { - var this$1 = this; - - if (arguments[0] instanceof Node) { - var n = arguments[0]; - this.mergeLabel(n._label); - } else if (arguments[0] instanceof Label) { - var label2 = arguments[0]; - for (var i = 0; i < 2; i++) { - var loc = this$1.computeMergedLocation(label2, i); - var thisLoc = this$1._label.getLocation(i); - if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); } - } - } - }; - Node.prototype.add = function add (e) { - this._edges.insert(e); - e.setNode(this); - }; - Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) { - if (this._label === null) { return null } - var loc = Location.NONE; - if (this._label !== null) { loc = this._label.getLocation(argIndex); } - var newLoc = null; - switch (loc) { - case Location.BOUNDARY: - newLoc = Location.INTERIOR; - break - case Location.INTERIOR: - newLoc = Location.BOUNDARY; - break - default: - newLoc = Location.BOUNDARY; - break - } - this._label.setLocation(argIndex, newLoc); - }; - Node.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Node.prototype.getClass = function getClass () { - return Node - }; - - return Node; - }(GraphComponent)); - - var NodeMap = function NodeMap () { - this.nodeMap = new TreeMap(); - this.nodeFact = null; - var nodeFact = arguments[0]; - this.nodeFact = nodeFact; - }; - NodeMap.prototype.find = function find (coord) { - return this.nodeMap.get(coord) - }; - NodeMap.prototype.addNode = function addNode () { - if (arguments[0] instanceof Coordinate) { - var coord = arguments[0]; - var node = this.nodeMap.get(coord); - if (node === null) { - node = this.nodeFact.createNode(coord); - this.nodeMap.put(coord, node); - } - return node - } else if (arguments[0] instanceof Node$1) { - var n = arguments[0]; - var node$1 = this.nodeMap.get(n.getCoordinate()); - if (node$1 === null) { - this.nodeMap.put(n.getCoordinate(), n); - return n - } - node$1.mergeLabel(n); - return node$1 - } - }; - NodeMap.prototype.print = function print (out) { - for (var it = this.iterator(); it.hasNext();) { - var n = it.next(); - n.print(out); - } - }; - NodeMap.prototype.iterator = function iterator () { - return this.nodeMap.values().iterator() - }; - NodeMap.prototype.values = function values () { - return this.nodeMap.values() - }; - NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) { - var bdyNodes = new ArrayList(); - for (var i = this.iterator(); i.hasNext();) { - var node = i.next(); - if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); } - } - return bdyNodes - }; - NodeMap.prototype.add = function add (e) { - var p = e.getCoordinate(); - var n = this.addNode(p); - n.add(e); - }; - NodeMap.prototype.interfaces_ = function interfaces_ () { - return [] - }; - NodeMap.prototype.getClass = function getClass () { - return NodeMap - }; - - var Quadrant = function Quadrant () {}; - - var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } }; - - Quadrant.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Quadrant.prototype.getClass = function getClass () { - return Quadrant - }; - Quadrant.isNorthern = function isNorthern (quad) { - return quad === Quadrant.NE || quad === Quadrant.NW - }; - Quadrant.isOpposite = function isOpposite (quad1, quad2) { - if (quad1 === quad2) { return false } - var diff = (quad1 - quad2 + 4) % 4; - if (diff === 2) { return true } - return false - }; - Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) { - if (quad1 === quad2) { return quad1 } - var diff = (quad1 - quad2 + 4) % 4; - if (diff === 2) { return -1 } - var min = quad1 < quad2 ? quad1 : quad2; - var max = quad1 > quad2 ? quad1 : quad2; - if (min === 0 && max === 3) { return 3 } - return min - }; - Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) { - if (halfPlane === Quadrant.SE) { - return quad === Quadrant.SE || quad === Quadrant.SW - } - return quad === halfPlane || quad === halfPlane + 1 - }; - Quadrant.quadrant = function quadrant () { - if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { - var dx = arguments[0]; - var dy = arguments[1]; - if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') } - if (dx >= 0.0) { - if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE } - } else { - if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW } - } - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) { - var p0 = arguments[0]; - var p1 = arguments[1]; - if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) } - if (p1.x >= p0.x) { - if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE } - } else { - if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW } - } - } - }; - staticAccessors$21.NE.get = function () { return 0 }; - staticAccessors$21.NW.get = function () { return 1 }; - staticAccessors$21.SW.get = function () { return 2 }; - staticAccessors$21.SE.get = function () { return 3 }; - - Object.defineProperties( Quadrant, staticAccessors$21 ); - - var EdgeEnd = function EdgeEnd () { - this._edge = null; - this._label = null; - this._node = null; - this._p0 = null; - this._p1 = null; - this._dx = null; - this._dy = null; - this._quadrant = null; - if (arguments.length === 1) { - var edge = arguments[0]; - this._edge = edge; - } else if (arguments.length === 3) { - var edge$1 = arguments[0]; - var p0 = arguments[1]; - var p1 = arguments[2]; - var label = null; - this._edge = edge$1; - this.init(p0, p1); - this._label = label; - } else if (arguments.length === 4) { - var edge$2 = arguments[0]; - var p0$1 = arguments[1]; - var p1$1 = arguments[2]; - var label$1 = arguments[3]; - this._edge = edge$2; - this.init(p0$1, p1$1); - this._label = label$1; - } - }; - EdgeEnd.prototype.compareDirection = function compareDirection (e) { - if (this._dx === e._dx && this._dy === e._dy) { return 0 } - if (this._quadrant > e._quadrant) { return 1 } - if (this._quadrant < e._quadrant) { return -1 } - return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1) - }; - EdgeEnd.prototype.getDy = function getDy () { - return this._dy - }; - EdgeEnd.prototype.getCoordinate = function getCoordinate () { - return this._p0 - }; - EdgeEnd.prototype.setNode = function setNode (node) { - this._node = node; - }; - EdgeEnd.prototype.print = function print (out) { - var angle = Math.atan2(this._dy, this._dx); - var className = this.getClass().getName(); - var lastDotPos = className.lastIndexOf('.'); - var name = className.substring(lastDotPos + 1); - out.print(' ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + ' ' + this._label); - }; - EdgeEnd.prototype.compareTo = function compareTo (obj) { - var e = obj; - return this.compareDirection(e) - }; - EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () { - return this._p1 - }; - EdgeEnd.prototype.getDx = function getDx () { - return this._dx - }; - EdgeEnd.prototype.getLabel = function getLabel () { - return this._label - }; - EdgeEnd.prototype.getEdge = function getEdge () { - return this._edge - }; - EdgeEnd.prototype.getQuadrant = function getQuadrant () { - return this._quadrant - }; - EdgeEnd.prototype.getNode = function getNode () { - return this._node - }; - EdgeEnd.prototype.toString = function toString () { - var angle = Math.atan2(this._dy, this._dx); - var className = this.getClass().getName(); - var lastDotPos = className.lastIndexOf('.'); - var name = className.substring(lastDotPos + 1); - return ' ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + ' ' + this._label - }; - EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {}; - EdgeEnd.prototype.init = function init (p0, p1) { - this._p0 = p0; - this._p1 = p1; - this._dx = p1.x - p0.x; - this._dy = p1.y - p0.y; - this._quadrant = Quadrant.quadrant(this._dx, this._dy); - Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found'); - }; - EdgeEnd.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - EdgeEnd.prototype.getClass = function getClass () { - return EdgeEnd - }; - - var DirectedEdge = (function (EdgeEnd$$1) { - function DirectedEdge () { - var edge = arguments[0]; - var isForward = arguments[1]; - EdgeEnd$$1.call(this, edge); - this._isForward = null; - this._isInResult = false; - this._isVisited = false; - this._sym = null; - this._next = null; - this._nextMin = null; - this._edgeRing = null; - this._minEdgeRing = null; - this._depth = [0, -999, -999]; - this._isForward = isForward; - if (isForward) { - this.init(edge.getCoordinate(0), edge.getCoordinate(1)); - } else { - var n = edge.getNumPoints() - 1; - this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1)); - } - this.computeDirectedLabel(); - } - - if ( EdgeEnd$$1 ) DirectedEdge.__proto__ = EdgeEnd$$1; - DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype ); - DirectedEdge.prototype.constructor = DirectedEdge; - DirectedEdge.prototype.getNextMin = function getNextMin () { - return this._nextMin - }; - DirectedEdge.prototype.getDepth = function getDepth (position) { - return this._depth[position] - }; - DirectedEdge.prototype.setVisited = function setVisited (isVisited) { - this._isVisited = isVisited; - }; - DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () { - this._label = new Label(this._edge.getLabel()); - if (!this._isForward) { this._label.flip(); } - }; - DirectedEdge.prototype.getNext = function getNext () { - return this._next - }; - DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) { - if (this._depth[position] !== -999) { - if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) } - } - this._depth[position] = depthVal; - }; - DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () { - var this$1 = this; - - var isInteriorAreaEdge = true; - for (var i = 0; i < 2; i++) { - if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) { - isInteriorAreaEdge = false; - } - } - return isInteriorAreaEdge - }; - DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) { - this._nextMin = nextMin; - }; - DirectedEdge.prototype.print = function print (out) { - EdgeEnd$$1.prototype.print.call(this, out); - out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]); - out.print(' (' + this.getDepthDelta() + ')'); - if (this._isInResult) { out.print(' inResult'); } - }; - DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) { - this._minEdgeRing = minEdgeRing; - }; - DirectedEdge.prototype.isLineEdge = function isLineEdge () { - var isLine = this._label.isLine(0) || this._label.isLine(1); - var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR); - var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR); - return isLine && isExteriorIfArea0 && isExteriorIfArea1 - }; - DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) { - this._edgeRing = edgeRing; - }; - DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () { - return this._minEdgeRing - }; - DirectedEdge.prototype.getDepthDelta = function getDepthDelta () { - var depthDelta = this._edge.getDepthDelta(); - if (!this._isForward) { depthDelta = -depthDelta; } - return depthDelta - }; - DirectedEdge.prototype.setInResult = function setInResult (isInResult) { - this._isInResult = isInResult; - }; - DirectedEdge.prototype.getSym = function getSym () { - return this._sym - }; - DirectedEdge.prototype.isForward = function isForward () { - return this._isForward - }; - DirectedEdge.prototype.getEdge = function getEdge () { - return this._edge - }; - DirectedEdge.prototype.printEdge = function printEdge (out) { - this.print(out); - out.print(' '); - if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); } - }; - DirectedEdge.prototype.setSym = function setSym (de) { - this._sym = de; - }; - DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) { - this.setVisited(isVisited); - this._sym.setVisited(isVisited); - }; - DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) { - var depthDelta = this.getEdge().getDepthDelta(); - if (!this._isForward) { depthDelta = -depthDelta; } - var directionFactor = 1; - if (position === Position.LEFT) { directionFactor = -1; } - var oppositePos = Position.opposite(position); - var delta = depthDelta * directionFactor; - var oppositeDepth = depth + delta; - this.setDepth(position, depth); - this.setDepth(oppositePos, oppositeDepth); - }; - DirectedEdge.prototype.getEdgeRing = function getEdgeRing () { - return this._edgeRing - }; - DirectedEdge.prototype.isInResult = function isInResult () { - return this._isInResult - }; - DirectedEdge.prototype.setNext = function setNext (next) { - this._next = next; - }; - DirectedEdge.prototype.isVisited = function isVisited () { - return this._isVisited - }; - DirectedEdge.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DirectedEdge.prototype.getClass = function getClass () { - return DirectedEdge - }; - DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) { - if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 } - return 0 - }; - - return DirectedEdge; - }(EdgeEnd)); - - var NodeFactory = function NodeFactory () {}; - - NodeFactory.prototype.createNode = function createNode (coord) { - return new Node$1(coord, null) - }; - NodeFactory.prototype.interfaces_ = function interfaces_ () { - return [] - }; - NodeFactory.prototype.getClass = function getClass () { - return NodeFactory - }; - - var PlanarGraph = function PlanarGraph () { - this._edges = new ArrayList(); - this._nodes = null; - this._edgeEndList = new ArrayList(); - if (arguments.length === 0) { - this._nodes = new NodeMap(new NodeFactory()); - } else if (arguments.length === 1) { - var nodeFact = arguments[0]; - this._nodes = new NodeMap(nodeFact); - } - }; - PlanarGraph.prototype.printEdges = function printEdges (out) { - var this$1 = this; - - out.println('Edges:'); - for (var i = 0; i < this._edges.size(); i++) { - out.println('edge ' + i + ':'); - var e = this$1._edges.get(i); - e.print(out); - e.eiList.print(out); - } - }; - PlanarGraph.prototype.find = function find (coord) { - return this._nodes.find(coord) - }; - PlanarGraph.prototype.addNode = function addNode () { - if (arguments[0] instanceof Node$1) { - var node = arguments[0]; - return this._nodes.addNode(node) - } else if (arguments[0] instanceof Coordinate) { - var coord = arguments[0]; - return this._nodes.addNode(coord) - } - }; - PlanarGraph.prototype.getNodeIterator = function getNodeIterator () { - return this._nodes.iterator() - }; - PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () { - for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().linkResultDirectedEdges(); - } - }; - PlanarGraph.prototype.debugPrintln = function debugPrintln (o) { - System.out.println(o); - }; - PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) { - var node = this._nodes.find(coord); - if (node === null) { return false } - var label = node.getLabel(); - if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true } - return false - }; - PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () { - for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().linkAllDirectedEdges(); - } - }; - PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) { - if (!p0.equals(ep0)) { return false } - if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true } - return false - }; - PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () { - return this._edgeEndList - }; - PlanarGraph.prototype.debugPrint = function debugPrint (o) { - System.out.print(o); - }; - PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () { - return this._edges.iterator() - }; - PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) { - var this$1 = this; - - for (var i = 0; i < this._edges.size(); i++) { - var e = this$1._edges.get(i); - var eCoord = e.getCoordinates(); - if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e } - if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e } - } - return null - }; - PlanarGraph.prototype.insertEdge = function insertEdge (e) { - this._edges.add(e); - }; - PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) { - for (var i = this.getEdgeEnds().iterator(); i.hasNext();) { - var ee = i.next(); - if (ee.getEdge() === e) { return ee } - } - return null - }; - PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) { - var this$1 = this; - - for (var it = edgesToAdd.iterator(); it.hasNext();) { - var e = it.next(); - this$1._edges.add(e); - var de1 = new DirectedEdge(e, true); - var de2 = new DirectedEdge(e, false); - de1.setSym(de2); - de2.setSym(de1); - this$1.add(de1); - this$1.add(de2); - } - }; - PlanarGraph.prototype.add = function add (e) { - this._nodes.add(e); - this._edgeEndList.add(e); - }; - PlanarGraph.prototype.getNodes = function getNodes () { - return this._nodes.values() - }; - PlanarGraph.prototype.findEdge = function findEdge (p0, p1) { - var this$1 = this; - - for (var i = 0; i < this._edges.size(); i++) { - var e = this$1._edges.get(i); - var eCoord = e.getCoordinates(); - if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e } - } - return null - }; - PlanarGraph.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PlanarGraph.prototype.getClass = function getClass () { - return PlanarGraph - }; - PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) { - for (var nodeit = nodes.iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().linkResultDirectedEdges(); - } - }; - - var PolygonBuilder = function PolygonBuilder () { - this._geometryFactory = null; - this._shellList = new ArrayList(); - var geometryFactory = arguments[0]; - this._geometryFactory = geometryFactory; - }; - PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) { - for (var it = edgeRings.iterator(); it.hasNext();) { - var er = it.next(); - if (er.isHole()) { - freeHoleList.add(er); - } else { - shellList.add(er); - } - } - }; - PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) { - var this$1 = this; - - var resultPolyList = new ArrayList(); - for (var it = shellList.iterator(); it.hasNext();) { - var er = it.next(); - var poly = er.toPolygon(this$1._geometryFactory); - resultPolyList.add(poly); - } - return resultPolyList - }; - PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) { - var this$1 = this; - - for (var it = freeHoleList.iterator(); it.hasNext();) { - var hole = it.next(); - if (hole.getShell() === null) { - var shell = this$1.findEdgeRingContaining(hole, shellList); - if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) } - hole.setShell(shell); - } - } - }; - PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) { - var this$1 = this; - - var edgeRings = new ArrayList(); - for (var it = maxEdgeRings.iterator(); it.hasNext();) { - var er = it.next(); - if (er.getMaxNodeDegree() > 2) { - er.linkDirectedEdgesForMinimalEdgeRings(); - var minEdgeRings = er.buildMinimalRings(); - var shell = this$1.findShell(minEdgeRings); - if (shell !== null) { - this$1.placePolygonHoles(shell, minEdgeRings); - shellList.add(shell); - } else { - freeHoleList.addAll(minEdgeRings); - } - } else { - edgeRings.add(er); - } - } - return edgeRings - }; - PolygonBuilder.prototype.containsPoint = function containsPoint (p) { - for (var it = this._shellList.iterator(); it.hasNext();) { - var er = it.next(); - if (er.containsPoint(p)) { return true } - } - return false - }; - PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) { - var this$1 = this; - - var maxEdgeRings = new ArrayList(); - for (var it = dirEdges.iterator(); it.hasNext();) { - var de = it.next(); - if (de.isInResult() && de.getLabel().isArea()) { - if (de.getEdgeRing() === null) { - var er = new MaximalEdgeRing(de, this$1._geometryFactory); - maxEdgeRings.add(er); - er.setInResult(); - } - } - } - return maxEdgeRings - }; - PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) { - for (var it = minEdgeRings.iterator(); it.hasNext();) { - var er = it.next(); - if (er.isHole()) { - er.setShell(shell); - } - } - }; - PolygonBuilder.prototype.getPolygons = function getPolygons () { - var resultPolyList = this.computePolygons(this._shellList); - return resultPolyList - }; - PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) { - var testRing = testEr.getLinearRing(); - var testEnv = testRing.getEnvelopeInternal(); - var testPt = testRing.getCoordinateN(0); - var minShell = null; - var minEnv = null; - for (var it = shellList.iterator(); it.hasNext();) { - var tryShell = it.next(); - var tryRing = tryShell.getLinearRing(); - var tryEnv = tryRing.getEnvelopeInternal(); - if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); } - var isContained = false; - if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; } - if (isContained) { - if (minShell === null || minEnv.contains(tryEnv)) { - minShell = tryShell; - } - } - } - return minShell - }; - PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) { - var shellCount = 0; - var shell = null; - for (var it = minEdgeRings.iterator(); it.hasNext();) { - var er = it.next(); - if (!er.isHole()) { - shell = er; - shellCount++; - } - } - Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list'); - return shell - }; - PolygonBuilder.prototype.add = function add () { - if (arguments.length === 1) { - var graph = arguments[0]; - this.add(graph.getEdgeEnds(), graph.getNodes()); - } else if (arguments.length === 2) { - var dirEdges = arguments[0]; - var nodes = arguments[1]; - PlanarGraph.linkResultDirectedEdges(nodes); - var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges); - var freeHoleList = new ArrayList(); - var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList); - this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList); - this.placeFreeHoles(this._shellList, freeHoleList); - } - }; - PolygonBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PolygonBuilder.prototype.getClass = function getClass () { - return PolygonBuilder - }; - - var Boundable = function Boundable () {}; - - Boundable.prototype.getBounds = function getBounds () {}; - Boundable.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Boundable.prototype.getClass = function getClass () { - return Boundable - }; - - var ItemBoundable = function ItemBoundable () { - this._bounds = null; - this._item = null; - var bounds = arguments[0]; - var item = arguments[1]; - this._bounds = bounds; - this._item = item; - }; - ItemBoundable.prototype.getItem = function getItem () { - return this._item - }; - ItemBoundable.prototype.getBounds = function getBounds () { - return this._bounds - }; - ItemBoundable.prototype.interfaces_ = function interfaces_ () { - return [Boundable, Serializable] - }; - ItemBoundable.prototype.getClass = function getClass () { - return ItemBoundable - }; - - var PriorityQueue = function PriorityQueue () { - this._size = null; - this._items = null; - this._size = 0; - this._items = new ArrayList(); - this._items.add(null); - }; - PriorityQueue.prototype.poll = function poll () { - if (this.isEmpty()) { return null } - var minItem = this._items.get(1); - this._items.set(1, this._items.get(this._size)); - this._size -= 1; - this.reorder(1); - return minItem - }; - PriorityQueue.prototype.size = function size () { - return this._size - }; - PriorityQueue.prototype.reorder = function reorder (hole) { - var this$1 = this; - - var child = null; - var tmp = this._items.get(hole); - for (; hole * 2 <= this._size; hole = child) { - child = hole * 2; - if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; } - if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break } - } - this._items.set(hole, tmp); - }; - PriorityQueue.prototype.clear = function clear () { - this._size = 0; - this._items.clear(); - }; - PriorityQueue.prototype.isEmpty = function isEmpty () { - return this._size === 0 - }; - PriorityQueue.prototype.add = function add (x) { - var this$1 = this; - - this._items.add(null); - this._size += 1; - var hole = this._size; - this._items.set(0, x); - for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) { - this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2))); - } - this._items.set(hole, x); - }; - PriorityQueue.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PriorityQueue.prototype.getClass = function getClass () { - return PriorityQueue - }; - - var ItemVisitor = function ItemVisitor () {}; - - ItemVisitor.prototype.visitItem = function visitItem (item) {}; - ItemVisitor.prototype.interfaces_ = function interfaces_ () { - return [] - }; - ItemVisitor.prototype.getClass = function getClass () { - return ItemVisitor - }; - - var SpatialIndex = function SpatialIndex () {}; - - SpatialIndex.prototype.insert = function insert (itemEnv, item) {}; - SpatialIndex.prototype.remove = function remove (itemEnv, item) {}; - SpatialIndex.prototype.query = function query () { - // if (arguments.length === 1) { - // const searchEnv = arguments[0] - // } else if (arguments.length === 2) { - // const searchEnv = arguments[0] - // const visitor = arguments[1] - // } - }; - SpatialIndex.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SpatialIndex.prototype.getClass = function getClass () { - return SpatialIndex - }; - - var AbstractNode = function AbstractNode () { - this._childBoundables = new ArrayList(); - this._bounds = null; - this._level = null; - if (arguments.length === 0) ; else if (arguments.length === 1) { - var level = arguments[0]; - this._level = level; - } - }; - - var staticAccessors$22 = { serialVersionUID: { configurable: true } }; - AbstractNode.prototype.getLevel = function getLevel () { - return this._level - }; - AbstractNode.prototype.size = function size () { - return this._childBoundables.size() - }; - AbstractNode.prototype.getChildBoundables = function getChildBoundables () { - return this._childBoundables - }; - AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) { - Assert.isTrue(this._bounds === null); - this._childBoundables.add(childBoundable); - }; - AbstractNode.prototype.isEmpty = function isEmpty () { - return this._childBoundables.isEmpty() - }; - AbstractNode.prototype.getBounds = function getBounds () { - if (this._bounds === null) { - this._bounds = this.computeBounds(); - } - return this._bounds - }; - AbstractNode.prototype.interfaces_ = function interfaces_ () { - return [Boundable, Serializable] - }; - AbstractNode.prototype.getClass = function getClass () { - return AbstractNode - }; - staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 }; - - Object.defineProperties( AbstractNode, staticAccessors$22 ); - - var Collections = function Collections () {}; - - Collections.reverseOrder = function reverseOrder () { - return { - compare: function compare (a, b) { - return b.compareTo(a) - } - } - }; - Collections.min = function min (l) { - Collections.sort(l); - return l.get(0) - }; - Collections.sort = function sort (l, c) { - var a = l.toArray(); - if (c) { - Arrays.sort(a, c); - } else { - Arrays.sort(a); - } - var i = l.iterator(); - for (var pos = 0, alen = a.length; pos < alen; pos++) { - i.next(); - i.set(a[pos]); - } - }; - Collections.singletonList = function singletonList (o) { - var arrayList = new ArrayList(); - arrayList.add(o); - return arrayList - }; - - var BoundablePair = function BoundablePair () { - this._boundable1 = null; - this._boundable2 = null; - this._distance = null; - this._itemDistance = null; - var boundable1 = arguments[0]; - var boundable2 = arguments[1]; - var itemDistance = arguments[2]; - this._boundable1 = boundable1; - this._boundable2 = boundable2; - this._itemDistance = itemDistance; - this._distance = this.distance(); - }; - BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) { - var isComp1 = BoundablePair.isComposite(this._boundable1); - var isComp2 = BoundablePair.isComposite(this._boundable2); - if (isComp1 && isComp2) { - if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) { - this.expand(this._boundable1, this._boundable2, priQ, minDistance); - return null - } else { - this.expand(this._boundable2, this._boundable1, priQ, minDistance); - return null - } - } else if (isComp1) { - this.expand(this._boundable1, this._boundable2, priQ, minDistance); - return null - } else if (isComp2) { - this.expand(this._boundable2, this._boundable1, priQ, minDistance); - return null - } - throw new IllegalArgumentException('neither boundable is composite') - }; - BoundablePair.prototype.isLeaves = function isLeaves () { - return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2)) - }; - BoundablePair.prototype.compareTo = function compareTo (o) { - var nd = o; - if (this._distance < nd._distance) { return -1 } - if (this._distance > nd._distance) { return 1 } - return 0 - }; - BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) { - var this$1 = this; - - var children = bndComposite.getChildBoundables(); - for (var i = children.iterator(); i.hasNext();) { - var child = i.next(); - var bp = new BoundablePair(child, bndOther, this$1._itemDistance); - if (bp.getDistance() < minDistance) { - priQ.add(bp); - } - } - }; - BoundablePair.prototype.getBoundable = function getBoundable (i) { - if (i === 0) { return this._boundable1 } - return this._boundable2 - }; - BoundablePair.prototype.getDistance = function getDistance () { - return this._distance - }; - BoundablePair.prototype.distance = function distance () { - if (this.isLeaves()) { - return this._itemDistance.distance(this._boundable1, this._boundable2) - } - return this._boundable1.getBounds().distance(this._boundable2.getBounds()) - }; - BoundablePair.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - BoundablePair.prototype.getClass = function getClass () { - return BoundablePair - }; - BoundablePair.area = function area (b) { - return b.getBounds().getArea() - }; - BoundablePair.isComposite = function isComposite (item) { - return item instanceof AbstractNode - }; - - var AbstractSTRtree = function AbstractSTRtree () { - this._root = null; - this._built = false; - this._itemBoundables = new ArrayList(); - this._nodeCapacity = null; - if (arguments.length === 0) { - var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY; - this._nodeCapacity = nodeCapacity; - } else if (arguments.length === 1) { - var nodeCapacity$1 = arguments[0]; - Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1'); - this._nodeCapacity = nodeCapacity$1; - } - }; - - var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } }; - AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () { - return this._nodeCapacity - }; - AbstractSTRtree.prototype.lastNode = function lastNode (nodes) { - return nodes.get(nodes.size() - 1) - }; - AbstractSTRtree.prototype.size = function size () { - var this$1 = this; - - if (arguments.length === 0) { - if (this.isEmpty()) { - return 0 - } - this.build(); - return this.size(this._root) - } else if (arguments.length === 1) { - var node = arguments[0]; - var size = 0; - for (var i = node.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (childBoundable instanceof AbstractNode) { - size += this$1.size(childBoundable); - } else if (childBoundable instanceof ItemBoundable) { - size += 1; - } - } - return size - } - }; - AbstractSTRtree.prototype.removeItem = function removeItem (node, item) { - var childToRemove = null; - for (var i = node.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (childBoundable instanceof ItemBoundable) { - if (childBoundable.getItem() === item) { childToRemove = childBoundable; } - } - } - if (childToRemove !== null) { - node.getChildBoundables().remove(childToRemove); - return true - } - return false - }; - AbstractSTRtree.prototype.itemsTree = function itemsTree () { - var this$1 = this; - - if (arguments.length === 0) { - this.build(); - var valuesTree = this.itemsTree(this._root); - if (valuesTree === null) { return new ArrayList() } - return valuesTree - } else if (arguments.length === 1) { - var node = arguments[0]; - var valuesTreeForNode = new ArrayList(); - for (var i = node.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (childBoundable instanceof AbstractNode) { - var valuesTreeForChild = this$1.itemsTree(childBoundable); - if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); } - } else if (childBoundable instanceof ItemBoundable) { - valuesTreeForNode.add(childBoundable.getItem()); - } else { - Assert.shouldNeverReachHere(); - } - } - if (valuesTreeForNode.size() <= 0) { return null } - return valuesTreeForNode - } - }; - AbstractSTRtree.prototype.insert = function insert (bounds, item) { - Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.'); - this._itemBoundables.add(new ItemBoundable(bounds, item)); - }; - AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () { - var this$1 = this; - - if (arguments.length === 1) { - var level = arguments[0]; - var boundables = new ArrayList(); - this.boundablesAtLevel(level, this._root, boundables); - return boundables - } else if (arguments.length === 3) { - var level$1 = arguments[0]; - var top = arguments[1]; - var boundables$1 = arguments[2]; - Assert.isTrue(level$1 > -2); - if (top.getLevel() === level$1) { - boundables$1.add(top); - return null - } - for (var i = top.getChildBoundables().iterator(); i.hasNext();) { - var boundable = i.next(); - if (boundable instanceof AbstractNode) { - this$1.boundablesAtLevel(level$1, boundable, boundables$1); - } else { - Assert.isTrue(boundable instanceof ItemBoundable); - if (level$1 === -1) { - boundables$1.add(boundable); - } - } - } - return null - } - }; - AbstractSTRtree.prototype.query = function query () { - var this$1 = this; - - if (arguments.length === 1) { - var searchBounds = arguments[0]; - this.build(); - var matches = new ArrayList(); - if (this.isEmpty()) { - return matches - } - if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) { - this.query(searchBounds, this._root, matches); - } - return matches - } else if (arguments.length === 2) { - var searchBounds$1 = arguments[0]; - var visitor = arguments[1]; - this.build(); - if (this.isEmpty()) { - return null - } - if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) { - this.query(searchBounds$1, this._root, visitor); - } - } else if (arguments.length === 3) { - if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) { - var searchBounds$2 = arguments[0]; - var node = arguments[1]; - var visitor$1 = arguments[2]; - var childBoundables = node.getChildBoundables(); - for (var i = 0; i < childBoundables.size(); i++) { - var childBoundable = childBoundables.get(i); - if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) { - continue - } - if (childBoundable instanceof AbstractNode) { - this$1.query(searchBounds$2, childBoundable, visitor$1); - } else if (childBoundable instanceof ItemBoundable) { - visitor$1.visitItem(childBoundable.getItem()); - } else { - Assert.shouldNeverReachHere(); - } - } - } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) { - var searchBounds$3 = arguments[0]; - var node$1 = arguments[1]; - var matches$1 = arguments[2]; - var childBoundables$1 = node$1.getChildBoundables(); - for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) { - var childBoundable$1 = childBoundables$1.get(i$1); - if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) { - continue - } - if (childBoundable$1 instanceof AbstractNode) { - this$1.query(searchBounds$3, childBoundable$1, matches$1); - } else if (childBoundable$1 instanceof ItemBoundable) { - matches$1.add(childBoundable$1.getItem()); - } else { - Assert.shouldNeverReachHere(); - } - } - } - } - }; - AbstractSTRtree.prototype.build = function build () { - if (this._built) { return null } - this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1); - this._itemBoundables = null; - this._built = true; - }; - AbstractSTRtree.prototype.getRoot = function getRoot () { - this.build(); - return this._root - }; - AbstractSTRtree.prototype.remove = function remove () { - var this$1 = this; - - if (arguments.length === 2) { - var searchBounds = arguments[0]; - var item = arguments[1]; - this.build(); - if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) { - return this.remove(searchBounds, this._root, item) - } - return false - } else if (arguments.length === 3) { - var searchBounds$1 = arguments[0]; - var node = arguments[1]; - var item$1 = arguments[2]; - var found = this.removeItem(node, item$1); - if (found) { return true } - var childToPrune = null; - for (var i = node.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) { - continue - } - if (childBoundable instanceof AbstractNode) { - found = this$1.remove(searchBounds$1, childBoundable, item$1); - if (found) { - childToPrune = childBoundable; - break - } - } - } - if (childToPrune !== null) { - if (childToPrune.getChildBoundables().isEmpty()) { - node.getChildBoundables().remove(childToPrune); - } - } - return found - } - }; - AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) { - Assert.isTrue(!boundablesOfALevel.isEmpty()); - var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1); - if (parentBoundables.size() === 1) { - return parentBoundables.get(0) - } - return this.createHigherLevels(parentBoundables, level + 1) - }; - AbstractSTRtree.prototype.depth = function depth () { - var this$1 = this; - - if (arguments.length === 0) { - if (this.isEmpty()) { - return 0 - } - this.build(); - return this.depth(this._root) - } else if (arguments.length === 1) { - var node = arguments[0]; - var maxChildDepth = 0; - for (var i = node.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (childBoundable instanceof AbstractNode) { - var childDepth = this$1.depth(childBoundable); - if (childDepth > maxChildDepth) { maxChildDepth = childDepth; } - } - } - return maxChildDepth + 1 - } - }; - AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) { - var this$1 = this; - - Assert.isTrue(!childBoundables.isEmpty()); - var parentBoundables = new ArrayList(); - parentBoundables.add(this.createNode(newLevel)); - var sortedChildBoundables = new ArrayList(childBoundables); - Collections.sort(sortedChildBoundables, this.getComparator()); - for (var i = sortedChildBoundables.iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) { - parentBoundables.add(this$1.createNode(newLevel)); - } - this$1.lastNode(parentBoundables).addChildBoundable(childBoundable); - } - return parentBoundables - }; - AbstractSTRtree.prototype.isEmpty = function isEmpty () { - if (!this._built) { return this._itemBoundables.isEmpty() } - return this._root.isEmpty() - }; - AbstractSTRtree.prototype.interfaces_ = function interfaces_ () { - return [Serializable] - }; - AbstractSTRtree.prototype.getClass = function getClass () { - return AbstractSTRtree - }; - AbstractSTRtree.compareDoubles = function compareDoubles (a, b) { - return a > b ? 1 : a < b ? -1 : 0 - }; - staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp }; - staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 }; - staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 }; - - Object.defineProperties( AbstractSTRtree, staticAccessors$23 ); - - var IntersectsOp = function IntersectsOp () {}; - - var ItemDistance = function ItemDistance () {}; - - ItemDistance.prototype.distance = function distance (item1, item2) {}; - ItemDistance.prototype.interfaces_ = function interfaces_ () { - return [] - }; - ItemDistance.prototype.getClass = function getClass () { - return ItemDistance - }; - - var STRtree = (function (AbstractSTRtree$$1) { - function STRtree (nodeCapacity) { - nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY; - AbstractSTRtree$$1.call(this, nodeCapacity); - } - - if ( AbstractSTRtree$$1 ) STRtree.__proto__ = AbstractSTRtree$$1; - STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype ); - STRtree.prototype.constructor = STRtree; - - var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } }; - STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) { - var this$1 = this; - - Assert.isTrue(verticalSlices.length > 0); - var parentBoundables = new ArrayList(); - for (var i = 0; i < verticalSlices.length; i++) { - parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel)); - } - return parentBoundables - }; - STRtree.prototype.createNode = function createNode (level) { - return new STRtreeNode(level) - }; - STRtree.prototype.size = function size () { - if (arguments.length === 0) { - return AbstractSTRtree$$1.prototype.size.call(this) - } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) } - }; - STRtree.prototype.insert = function insert () { - if (arguments.length === 2) { - var itemEnv = arguments[0]; - var item = arguments[1]; - if (itemEnv.isNull()) { - return null - } - AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item); - } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) } - }; - STRtree.prototype.getIntersectsOp = function getIntersectsOp () { - return STRtree.intersectsOp - }; - STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) { - var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount)); - var slices = new Array(sliceCount).fill(null); - var i = childBoundables.iterator(); - for (var j = 0; j < sliceCount; j++) { - slices[j] = new ArrayList(); - var boundablesAddedToSlice = 0; - while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) { - var childBoundable = i.next(); - slices[j].add(childBoundable); - boundablesAddedToSlice++; - } - } - return slices - }; - STRtree.prototype.query = function query () { - if (arguments.length === 1) { - var searchEnv = arguments[0]; - return AbstractSTRtree$$1.prototype.query.call(this, searchEnv) - } else if (arguments.length === 2) { - var searchEnv$1 = arguments[0]; - var visitor = arguments[1]; - AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor); - } else if (arguments.length === 3) { - if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) { - var searchBounds = arguments[0]; - var node = arguments[1]; - var visitor$1 = arguments[2]; - AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1); - } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) { - var searchBounds$1 = arguments[0]; - var node$1 = arguments[1]; - var matches = arguments[2]; - AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches); - } - } - }; - STRtree.prototype.getComparator = function getComparator () { - return STRtree.yComparator - }; - STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) { - return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel) - }; - STRtree.prototype.remove = function remove () { - if (arguments.length === 2) { - var itemEnv = arguments[0]; - var item = arguments[1]; - return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item) - } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) } - }; - STRtree.prototype.depth = function depth () { - if (arguments.length === 0) { - return AbstractSTRtree$$1.prototype.depth.call(this) - } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) } - }; - STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) { - Assert.isTrue(!childBoundables.isEmpty()); - var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity())); - var sortedChildBoundables = new ArrayList(childBoundables); - Collections.sort(sortedChildBoundables, STRtree.xComparator); - var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount)))); - return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel) - }; - STRtree.prototype.nearestNeighbour = function nearestNeighbour () { - if (arguments.length === 1) { - if (hasInterface(arguments[0], ItemDistance)) { - var itemDist = arguments[0]; - var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist); - return this.nearestNeighbour(bp) - } else if (arguments[0] instanceof BoundablePair) { - var initBndPair = arguments[0]; - return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY) - } - } else if (arguments.length === 2) { - if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) { - var tree = arguments[0]; - var itemDist$1 = arguments[1]; - var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1); - return this.nearestNeighbour(bp$1) - } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') { - var initBndPair$1 = arguments[0]; - var maxDistance = arguments[1]; - var distanceLowerBound = maxDistance; - var minPair = null; - var priQ = new PriorityQueue(); - priQ.add(initBndPair$1); - while (!priQ.isEmpty() && distanceLowerBound > 0.0) { - var bndPair = priQ.poll(); - var currentDistance = bndPair.getDistance(); - if (currentDistance >= distanceLowerBound) { break } - if (bndPair.isLeaves()) { - distanceLowerBound = currentDistance; - minPair = bndPair; - } else { - bndPair.expandToQueue(priQ, distanceLowerBound); - } - } - return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()] - } - } else if (arguments.length === 3) { - var env = arguments[0]; - var item = arguments[1]; - var itemDist$2 = arguments[2]; - var bnd = new ItemBoundable(env, item); - var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2); - return this.nearestNeighbour(bp$2)[0] - } - }; - STRtree.prototype.interfaces_ = function interfaces_ () { - return [SpatialIndex, Serializable] - }; - STRtree.prototype.getClass = function getClass () { - return STRtree - }; - STRtree.centreX = function centreX (e) { - return STRtree.avg(e.getMinX(), e.getMaxX()) - }; - STRtree.avg = function avg (a, b) { - return (a + b) / 2 - }; - STRtree.centreY = function centreY (e) { - return STRtree.avg(e.getMinY(), e.getMaxY()) - }; - staticAccessors.STRtreeNode.get = function () { return STRtreeNode }; - staticAccessors.serialVersionUID.get = function () { return 259274702368956900 }; - staticAccessors.xComparator.get = function () { - return { - interfaces_: function () { - return [Comparator] - }, - compare: function (o1, o2) { - return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds())) - } - } - }; - staticAccessors.yComparator.get = function () { - return { - interfaces_: function () { - return [Comparator] - }, - compare: function (o1, o2) { - return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds())) - } - } - }; - staticAccessors.intersectsOp.get = function () { - return { - interfaces_: function () { - return [AbstractSTRtree$$1.IntersectsOp] - }, - intersects: function (aBounds, bBounds) { - return aBounds.intersects(bBounds) - } - } - }; - staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 }; - - Object.defineProperties( STRtree, staticAccessors ); - - return STRtree; - }(AbstractSTRtree)); - - var STRtreeNode = (function (AbstractNode$$1) { - function STRtreeNode () { - var level = arguments[0]; - AbstractNode$$1.call(this, level); - } - - if ( AbstractNode$$1 ) STRtreeNode.__proto__ = AbstractNode$$1; - STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype ); - STRtreeNode.prototype.constructor = STRtreeNode; - STRtreeNode.prototype.computeBounds = function computeBounds () { - var bounds = null; - for (var i = this.getChildBoundables().iterator(); i.hasNext();) { - var childBoundable = i.next(); - if (bounds === null) { - bounds = new Envelope(childBoundable.getBounds()); - } else { - bounds.expandToInclude(childBoundable.getBounds()); - } - } - return bounds - }; - STRtreeNode.prototype.interfaces_ = function interfaces_ () { - return [] - }; - STRtreeNode.prototype.getClass = function getClass () { - return STRtreeNode - }; - - return STRtreeNode; - }(AbstractNode)); - - var SegmentPointComparator = function SegmentPointComparator () {}; - - SegmentPointComparator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentPointComparator.prototype.getClass = function getClass () { - return SegmentPointComparator - }; - SegmentPointComparator.relativeSign = function relativeSign (x0, x1) { - if (x0 < x1) { return -1 } - if (x0 > x1) { return 1 } - return 0 - }; - SegmentPointComparator.compare = function compare (octant, p0, p1) { - if (p0.equals2D(p1)) { return 0 } - var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x); - var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y); - switch (octant) { - case 0: - return SegmentPointComparator.compareValue(xSign, ySign) - case 1: - return SegmentPointComparator.compareValue(ySign, xSign) - case 2: - return SegmentPointComparator.compareValue(ySign, -xSign) - case 3: - return SegmentPointComparator.compareValue(-xSign, ySign) - case 4: - return SegmentPointComparator.compareValue(-xSign, -ySign) - case 5: - return SegmentPointComparator.compareValue(-ySign, -xSign) - case 6: - return SegmentPointComparator.compareValue(-ySign, xSign) - case 7: - return SegmentPointComparator.compareValue(xSign, -ySign) - } - Assert.shouldNeverReachHere('invalid octant value'); - return 0 - }; - SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) { - if (compareSign0 < 0) { return -1 } - if (compareSign0 > 0) { return 1 } - if (compareSign1 < 0) { return -1 } - if (compareSign1 > 0) { return 1 } - return 0 - }; - - var SegmentNode = function SegmentNode () { - this._segString = null; - this.coord = null; - this.segmentIndex = null; - this._segmentOctant = null; - this._isInterior = null; - var segString = arguments[0]; - var coord = arguments[1]; - var segmentIndex = arguments[2]; - var segmentOctant = arguments[3]; - this._segString = segString; - this.coord = new Coordinate(coord); - this.segmentIndex = segmentIndex; - this._segmentOctant = segmentOctant; - this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex)); - }; - SegmentNode.prototype.getCoordinate = function getCoordinate () { - return this.coord - }; - SegmentNode.prototype.print = function print (out) { - out.print(this.coord); - out.print(' seg # = ' + this.segmentIndex); - }; - SegmentNode.prototype.compareTo = function compareTo (obj) { - var other = obj; - if (this.segmentIndex < other.segmentIndex) { return -1 } - if (this.segmentIndex > other.segmentIndex) { return 1 } - if (this.coord.equals2D(other.coord)) { return 0 } - return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord) - }; - SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) { - if (this.segmentIndex === 0 && !this._isInterior) { return true } - if (this.segmentIndex === maxSegmentIndex) { return true } - return false - }; - SegmentNode.prototype.isInterior = function isInterior () { - return this._isInterior - }; - SegmentNode.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - SegmentNode.prototype.getClass = function getClass () { - return SegmentNode - }; - - // import Iterator from '../../../../java/util/Iterator' - var SegmentNodeList = function SegmentNodeList () { - this._nodeMap = new TreeMap(); - this._edge = null; - var edge = arguments[0]; - this._edge = edge; - }; - SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () { - var this$1 = this; - - var coordList = new CoordinateList(); - this.addEndpoints(); - var it = this.iterator(); - var eiPrev = it.next(); - while (it.hasNext()) { - var ei = it.next(); - this$1.addEdgeCoordinates(eiPrev, ei, coordList); - eiPrev = ei; - } - return coordList.toCoordinateArray() - }; - SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () { - var this$1 = this; - - var collapsedVertexIndexes = new ArrayList(); - this.findCollapsesFromInsertedNodes(collapsedVertexIndexes); - this.findCollapsesFromExistingVertices(collapsedVertexIndexes); - for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) { - var vertexIndex = it.next().intValue(); - this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex); - } - }; - SegmentNodeList.prototype.print = function print (out) { - out.println('Intersections:'); - for (var it = this.iterator(); it.hasNext();) { - var ei = it.next(); - ei.print(out); - } - }; - SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) { - var this$1 = this; - - for (var i = 0; i < this._edge.size() - 2; i++) { - var p0 = this$1._edge.getCoordinate(i); - // const p1 = this._edge.getCoordinate(i + 1) - var p2 = this$1._edge.getCoordinate(i + 2); - if (p0.equals2D(p2)) { - collapsedVertexIndexes.add(new Integer(i + 1)); - } - } - }; - SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) { - var this$1 = this; - - // let npts = ei1.segmentIndex - ei0.segmentIndex + 2 - var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex); - var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt); - // if (!useIntPt1) { - // npts-- - // } - // const ipt = 0 - coordList.add(new Coordinate(ei0.coord), false); - for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) { - coordList.add(this$1._edge.getCoordinate(i)); - } - if (useIntPt1) { - coordList.add(new Coordinate(ei1.coord)); - } - }; - SegmentNodeList.prototype.iterator = function iterator () { - return this._nodeMap.values().iterator() - }; - SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) { - var this$1 = this; - - this.addEndpoints(); - this.addCollapsedNodes(); - var it = this.iterator(); - var eiPrev = it.next(); - while (it.hasNext()) { - var ei = it.next(); - var newEdge = this$1.createSplitEdge(eiPrev, ei); - edgeList.add(newEdge); - eiPrev = ei; - } - }; - SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) { - if (!ei0.coord.equals2D(ei1.coord)) { return false } - var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex; - if (!ei1.isInterior()) { - numVerticesBetween--; - } - if (numVerticesBetween === 1) { - collapsedVertexIndex[0] = ei0.segmentIndex + 1; - return true - } - return false - }; - SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) { - var this$1 = this; - - var collapsedVertexIndex = new Array(1).fill(null); - var it = this.iterator(); - var eiPrev = it.next(); - while (it.hasNext()) { - var ei = it.next(); - var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex); - if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); } - eiPrev = ei; - } - }; - SegmentNodeList.prototype.getEdge = function getEdge () { - return this._edge - }; - SegmentNodeList.prototype.addEndpoints = function addEndpoints () { - var maxSegIndex = this._edge.size() - 1; - this.add(this._edge.getCoordinate(0), 0); - this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex); - }; - SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) { - var this$1 = this; - - var npts = ei1.segmentIndex - ei0.segmentIndex + 2; - var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex); - var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt); - if (!useIntPt1) { - npts--; - } - var pts = new Array(npts).fill(null); - var ipt = 0; - pts[ipt++] = new Coordinate(ei0.coord); - for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) { - pts[ipt++] = this$1._edge.getCoordinate(i); - } - if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); } - return new NodedSegmentString(pts, this._edge.getData()) - }; - SegmentNodeList.prototype.add = function add (intPt, segmentIndex) { - var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex)); - var ei = this._nodeMap.get(eiNew); - if (ei !== null) { - Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates'); - return ei - } - this._nodeMap.put(eiNew, eiNew); - return eiNew - }; - SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) { - var edgePts = this._edge.getCoordinates(); - var split0 = splitEdges.get(0); - var pt0 = split0.getCoordinate(0); - if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) } - var splitn = splitEdges.get(splitEdges.size() - 1); - var splitnPts = splitn.getCoordinates(); - var ptn = splitnPts[splitnPts.length - 1]; - if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) } - }; - SegmentNodeList.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentNodeList.prototype.getClass = function getClass () { - return SegmentNodeList - }; - - - - // class NodeVertexIterator { - // constructor () { - // this._nodeList = null - // this._edge = null - // this._nodeIt = null - // this._currNode = null - // this._nextNode = null - // this._currSegIndex = 0 - // let nodeList = arguments[0] - // this._nodeList = nodeList - // this._edge = nodeList.getEdge() - // this._nodeIt = nodeList.iterator() - // this.readNextNode() - // } - // next () { - // if (this._currNode === null) { - // this._currNode = this._nextNode - // this._currSegIndex = this._currNode.segmentIndex - // this.readNextNode() - // return this._currNode - // } - // if (this._nextNode === null) return null - // if (this._nextNode.segmentIndex === this._currNode.segmentIndex) { - // this._currNode = this._nextNode - // this._currSegIndex = this._currNode.segmentIndex - // this.readNextNode() - // return this._currNode - // } - // if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {} - // return null - // } - // remove () { - // // throw new UnsupportedOperationException(this.getClass().getName()) - // } - // hasNext () { - // if (this._nextNode === null) return false - // return true - // } - // readNextNode () { - // if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null - // } - // interfaces_ () { - // return [Iterator] - // } - // getClass () { - // return NodeVertexIterator - // } - // } - - var Octant = function Octant () {}; - - Octant.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Octant.prototype.getClass = function getClass () { - return Octant - }; - Octant.octant = function octant () { - if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { - var dx = arguments[0]; - var dy = arguments[1]; - if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') } - var adx = Math.abs(dx); - var ady = Math.abs(dy); - if (dx >= 0) { - if (dy >= 0) { - if (adx >= ady) { return 0; } else { return 1 } - } else { - if (adx >= ady) { return 7; } else { return 6 } - } - } else { - if (dy >= 0) { - if (adx >= ady) { return 3; } else { return 2 } - } else { - if (adx >= ady) { return 4; } else { return 5 } - } - } - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) { - var p0 = arguments[0]; - var p1 = arguments[1]; - var dx$1 = p1.x - p0.x; - var dy$1 = p1.y - p0.y; - if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) } - return Octant.octant(dx$1, dy$1) - } - }; - - var SegmentString = function SegmentString () {}; - - SegmentString.prototype.getCoordinates = function getCoordinates () {}; - SegmentString.prototype.size = function size () {}; - SegmentString.prototype.getCoordinate = function getCoordinate (i) {}; - SegmentString.prototype.isClosed = function isClosed () {}; - SegmentString.prototype.setData = function setData (data) {}; - SegmentString.prototype.getData = function getData () {}; - SegmentString.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentString.prototype.getClass = function getClass () { - return SegmentString - }; - - var NodableSegmentString = function NodableSegmentString () {}; - - NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {}; - NodableSegmentString.prototype.interfaces_ = function interfaces_ () { - return [SegmentString] - }; - NodableSegmentString.prototype.getClass = function getClass () { - return NodableSegmentString - }; - - var NodedSegmentString = function NodedSegmentString () { - this._nodeList = new SegmentNodeList(this); - this._pts = null; - this._data = null; - var pts = arguments[0]; - var data = arguments[1]; - this._pts = pts; - this._data = data; - }; - NodedSegmentString.prototype.getCoordinates = function getCoordinates () { - return this._pts - }; - NodedSegmentString.prototype.size = function size () { - return this._pts.length - }; - NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) { - return this._pts[i] - }; - NodedSegmentString.prototype.isClosed = function isClosed () { - return this._pts[0].equals(this._pts[this._pts.length - 1]) - }; - NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) { - if (index === this._pts.length - 1) { return -1 } - return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1)) - }; - NodedSegmentString.prototype.setData = function setData (data) { - this._data = data; - }; - NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) { - if (p0.equals2D(p1)) { return 0 } - return Octant.octant(p0, p1) - }; - NodedSegmentString.prototype.getData = function getData () { - return this._data - }; - NodedSegmentString.prototype.addIntersection = function addIntersection () { - if (arguments.length === 2) { - var intPt$1 = arguments[0]; - var segmentIndex = arguments[1]; - this.addIntersectionNode(intPt$1, segmentIndex); - } else if (arguments.length === 4) { - var li = arguments[0]; - var segmentIndex$1 = arguments[1]; - // const geomIndex = arguments[2] - var intIndex = arguments[3]; - var intPt = new Coordinate(li.getIntersection(intIndex)); - this.addIntersection(intPt, segmentIndex$1); - } - }; - NodedSegmentString.prototype.toString = function toString () { - return WKTWriter.toLineString(new CoordinateArraySequence(this._pts)) - }; - NodedSegmentString.prototype.getNodeList = function getNodeList () { - return this._nodeList - }; - NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) { - var normalizedSegmentIndex = segmentIndex; - var nextSegIndex = normalizedSegmentIndex + 1; - if (nextSegIndex < this._pts.length) { - var nextPt = this._pts[nextSegIndex]; - if (intPt.equals2D(nextPt)) { - normalizedSegmentIndex = nextSegIndex; - } - } - var ei = this._nodeList.add(intPt, normalizedSegmentIndex); - return ei - }; - NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) { - var this$1 = this; - - for (var i = 0; i < li.getIntersectionNum(); i++) { - this$1.addIntersection(li, segmentIndex, geomIndex, i); - } - }; - NodedSegmentString.prototype.interfaces_ = function interfaces_ () { - return [NodableSegmentString] - }; - NodedSegmentString.prototype.getClass = function getClass () { - return NodedSegmentString - }; - NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () { - if (arguments.length === 1) { - var segStrings = arguments[0]; - var resultEdgelist = new ArrayList(); - NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist); - return resultEdgelist - } else if (arguments.length === 2) { - var segStrings$1 = arguments[0]; - var resultEdgelist$1 = arguments[1]; - for (var i = segStrings$1.iterator(); i.hasNext();) { - var ss = i.next(); - ss.getNodeList().addSplitEdges(resultEdgelist$1); - } - } - }; - - var LineSegment = function LineSegment () { - this.p0 = null; - this.p1 = null; - if (arguments.length === 0) { - this.p0 = new Coordinate(); - this.p1 = new Coordinate(); - } else if (arguments.length === 1) { - var ls = arguments[0]; - this.p0 = new Coordinate(ls.p0); - this.p1 = new Coordinate(ls.p1); - } else if (arguments.length === 2) { - this.p0 = arguments[0]; - this.p1 = arguments[1]; - } else if (arguments.length === 4) { - var x0 = arguments[0]; - var y0 = arguments[1]; - var x1 = arguments[2]; - var y1 = arguments[3]; - this.p0 = new Coordinate(x0, y0); - this.p1 = new Coordinate(x1, y1); - } - }; - - var staticAccessors$24 = { serialVersionUID: { configurable: true } }; - LineSegment.prototype.minX = function minX () { - return Math.min(this.p0.x, this.p1.x) - }; - LineSegment.prototype.orientationIndex = function orientationIndex () { - if (arguments[0] instanceof LineSegment) { - var seg = arguments[0]; - var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0); - var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1); - if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) } - if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) } - return 0 - } else if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - return CGAlgorithms.orientationIndex(this.p0, this.p1, p) - } - }; - LineSegment.prototype.toGeometry = function toGeometry (geomFactory) { - return geomFactory.createLineString([this.p0, this.p1]) - }; - LineSegment.prototype.isVertical = function isVertical () { - return this.p0.x === this.p1.x - }; - LineSegment.prototype.equals = function equals (o) { - if (!(o instanceof LineSegment)) { - return false - } - var other = o; - return this.p0.equals(other.p0) && this.p1.equals(other.p1) - }; - LineSegment.prototype.intersection = function intersection (line) { - var li = new RobustLineIntersector(); - li.computeIntersection(this.p0, this.p1, line.p0, line.p1); - if (li.hasIntersection()) { return li.getIntersection(0) } - return null - }; - LineSegment.prototype.project = function project () { - if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) } - var r = this.projectionFactor(p); - var coord = new Coordinate(); - coord.x = this.p0.x + r * (this.p1.x - this.p0.x); - coord.y = this.p0.y + r * (this.p1.y - this.p0.y); - return coord - } else if (arguments[0] instanceof LineSegment) { - var seg = arguments[0]; - var pf0 = this.projectionFactor(seg.p0); - var pf1 = this.projectionFactor(seg.p1); - if (pf0 >= 1.0 && pf1 >= 1.0) { return null } - if (pf0 <= 0.0 && pf1 <= 0.0) { return null } - var newp0 = this.project(seg.p0); - if (pf0 < 0.0) { newp0 = this.p0; } - if (pf0 > 1.0) { newp0 = this.p1; } - var newp1 = this.project(seg.p1); - if (pf1 < 0.0) { newp1 = this.p0; } - if (pf1 > 1.0) { newp1 = this.p1; } - return new LineSegment(newp0, newp1) - } - }; - LineSegment.prototype.normalize = function normalize () { - if (this.p1.compareTo(this.p0) < 0) { this.reverse(); } - }; - LineSegment.prototype.angle = function angle () { - return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x) - }; - LineSegment.prototype.getCoordinate = function getCoordinate (i) { - if (i === 0) { return this.p0 } - return this.p1 - }; - LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) { - return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1) - }; - LineSegment.prototype.minY = function minY () { - return Math.min(this.p0.y, this.p1.y) - }; - LineSegment.prototype.midPoint = function midPoint () { - return LineSegment.midPoint(this.p0, this.p1) - }; - LineSegment.prototype.projectionFactor = function projectionFactor (p) { - if (p.equals(this.p0)) { return 0.0 } - if (p.equals(this.p1)) { return 1.0 } - var dx = this.p1.x - this.p0.x; - var dy = this.p1.y - this.p0.y; - var len = dx * dx + dy * dy; - if (len <= 0.0) { return Double.NaN } - var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len; - return r - }; - LineSegment.prototype.closestPoints = function closestPoints (line) { - var intPt = this.intersection(line); - if (intPt !== null) { - return [intPt, intPt] - } - var closestPt = new Array(2).fill(null); - var minDistance = Double.MAX_VALUE; - var dist = null; - var close00 = this.closestPoint(line.p0); - minDistance = close00.distance(line.p0); - closestPt[0] = close00; - closestPt[1] = line.p0; - var close01 = this.closestPoint(line.p1); - dist = close01.distance(line.p1); - if (dist < minDistance) { - minDistance = dist; - closestPt[0] = close01; - closestPt[1] = line.p1; - } - var close10 = line.closestPoint(this.p0); - dist = close10.distance(this.p0); - if (dist < minDistance) { - minDistance = dist; - closestPt[0] = this.p0; - closestPt[1] = close10; - } - var close11 = line.closestPoint(this.p1); - dist = close11.distance(this.p1); - if (dist < minDistance) { - minDistance = dist; - closestPt[0] = this.p1; - closestPt[1] = close11; - } - return closestPt - }; - LineSegment.prototype.closestPoint = function closestPoint (p) { - var factor = this.projectionFactor(p); - if (factor > 0 && factor < 1) { - return this.project(p) - } - var dist0 = this.p0.distance(p); - var dist1 = this.p1.distance(p); - if (dist0 < dist1) { return this.p0 } - return this.p1 - }; - LineSegment.prototype.maxX = function maxX () { - return Math.max(this.p0.x, this.p1.x) - }; - LineSegment.prototype.getLength = function getLength () { - return this.p0.distance(this.p1) - }; - LineSegment.prototype.compareTo = function compareTo (o) { - var other = o; - var comp0 = this.p0.compareTo(other.p0); - if (comp0 !== 0) { return comp0 } - return this.p1.compareTo(other.p1) - }; - LineSegment.prototype.reverse = function reverse () { - var temp = this.p0; - this.p0 = this.p1; - this.p1 = temp; - }; - LineSegment.prototype.equalsTopo = function equalsTopo (other) { - return this.p0.equals(other.p0) && - (this.p1.equals(other.p1) || this.p0.equals(other.p1)) && - this.p1.equals(other.p0) - }; - LineSegment.prototype.lineIntersection = function lineIntersection (line) { - try { - var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1); - return intPt - } catch (ex) { - if (ex instanceof NotRepresentableException) ; else { throw ex } - } finally {} - return null - }; - LineSegment.prototype.maxY = function maxY () { - return Math.max(this.p0.y, this.p1.y) - }; - LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) { - var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x); - var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y); - var dx = this.p1.x - this.p0.x; - var dy = this.p1.y - this.p0.y; - var len = Math.sqrt(dx * dx + dy * dy); - var ux = 0.0; - var uy = 0.0; - if (offsetDistance !== 0.0) { - if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') } - ux = offsetDistance * dx / len; - uy = offsetDistance * dy / len; - } - var offsetx = segx - uy; - var offsety = segy + ux; - var coord = new Coordinate(offsetx, offsety); - return coord - }; - LineSegment.prototype.setCoordinates = function setCoordinates () { - if (arguments.length === 1) { - var ls = arguments[0]; - this.setCoordinates(ls.p0, ls.p1); - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - this.p0.x = p0.x; - this.p0.y = p0.y; - this.p1.x = p1.x; - this.p1.y = p1.y; - } - }; - LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) { - var segFrac = this.projectionFactor(inputPt); - if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; } - return segFrac - }; - LineSegment.prototype.toString = function toString () { - return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')' - }; - LineSegment.prototype.isHorizontal = function isHorizontal () { - return this.p0.y === this.p1.y - }; - LineSegment.prototype.distance = function distance () { - if (arguments[0] instanceof LineSegment) { - var ls = arguments[0]; - return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1) - } else if (arguments[0] instanceof Coordinate) { - var p = arguments[0]; - return CGAlgorithms.distancePointLine(p, this.p0, this.p1) - } - }; - LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) { - var coord = new Coordinate(); - coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x); - coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y); - return coord - }; - LineSegment.prototype.hashCode = function hashCode () { - var bits0 = Double.doubleToLongBits(this.p0.x); - bits0 ^= Double.doubleToLongBits(this.p0.y) * 31; - var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32); - var bits1 = Double.doubleToLongBits(this.p1.x); - bits1 ^= Double.doubleToLongBits(this.p1.y) * 31; - var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32); - return hash0 ^ hash1 - }; - LineSegment.prototype.interfaces_ = function interfaces_ () { - return [Comparable, Serializable] - }; - LineSegment.prototype.getClass = function getClass () { - return LineSegment - }; - LineSegment.midPoint = function midPoint (p0, p1) { - return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2) - }; - staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 }; - - Object.defineProperties( LineSegment, staticAccessors$24 ); - - var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () { - this.tempEnv1 = new Envelope(); - this.tempEnv2 = new Envelope(); - this._overlapSeg1 = new LineSegment(); - this._overlapSeg2 = new LineSegment(); - }; - MonotoneChainOverlapAction.prototype.overlap = function overlap () { - if (arguments.length === 2) ; else if (arguments.length === 4) { - var mc1 = arguments[0]; - var start1 = arguments[1]; - var mc2 = arguments[2]; - var start2 = arguments[3]; - mc1.getLineSegment(start1, this._overlapSeg1); - mc2.getLineSegment(start2, this._overlapSeg2); - this.overlap(this._overlapSeg1, this._overlapSeg2); - } - }; - MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChainOverlapAction.prototype.getClass = function getClass () { - return MonotoneChainOverlapAction - }; - - var MonotoneChain = function MonotoneChain () { - this._pts = null; - this._start = null; - this._end = null; - this._env = null; - this._context = null; - this._id = null; - var pts = arguments[0]; - var start = arguments[1]; - var end = arguments[2]; - var context = arguments[3]; - this._pts = pts; - this._start = start; - this._end = end; - this._context = context; - }; - MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) { - ls.p0 = this._pts[index]; - ls.p1 = this._pts[index + 1]; - }; - MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) { - var p0 = this._pts[start0]; - var p1 = this._pts[end0]; - mcs.tempEnv1.init(p0, p1); - if (end0 - start0 === 1) { - mcs.select(this, start0); - return null - } - if (!searchEnv.intersects(mcs.tempEnv1)) { return null } - var mid = Math.trunc((start0 + end0) / 2); - if (start0 < mid) { - this.computeSelect(searchEnv, start0, mid, mcs); - } - if (mid < end0) { - this.computeSelect(searchEnv, mid, end0, mcs); - } - }; - MonotoneChain.prototype.getCoordinates = function getCoordinates () { - var this$1 = this; - - var coord = new Array(this._end - this._start + 1).fill(null); - var index = 0; - for (var i = this._start; i <= this._end; i++) { - coord[index++] = this$1._pts[i]; - } - return coord - }; - MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) { - this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco); - }; - MonotoneChain.prototype.setId = function setId (id) { - this._id = id; - }; - MonotoneChain.prototype.select = function select (searchEnv, mcs) { - this.computeSelect(searchEnv, this._start, this._end, mcs); - }; - MonotoneChain.prototype.getEnvelope = function getEnvelope () { - if (this._env === null) { - var p0 = this._pts[this._start]; - var p1 = this._pts[this._end]; - this._env = new Envelope(p0, p1); - } - return this._env - }; - MonotoneChain.prototype.getEndIndex = function getEndIndex () { - return this._end - }; - MonotoneChain.prototype.getStartIndex = function getStartIndex () { - return this._start - }; - MonotoneChain.prototype.getContext = function getContext () { - return this._context - }; - MonotoneChain.prototype.getId = function getId () { - return this._id - }; - MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) { - var p00 = this._pts[start0]; - var p01 = this._pts[end0]; - var p10 = mc._pts[start1]; - var p11 = mc._pts[end1]; - if (end0 - start0 === 1 && end1 - start1 === 1) { - mco.overlap(this, start0, mc, start1); - return null - } - mco.tempEnv1.init(p00, p01); - mco.tempEnv2.init(p10, p11); - if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null } - var mid0 = Math.trunc((start0 + end0) / 2); - var mid1 = Math.trunc((start1 + end1) / 2); - if (start0 < mid0) { - if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); } - if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); } - } - if (mid0 < end0) { - if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); } - if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); } - } - }; - MonotoneChain.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChain.prototype.getClass = function getClass () { - return MonotoneChain - }; - - var MonotoneChainBuilder = function MonotoneChainBuilder () {}; - - MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChainBuilder.prototype.getClass = function getClass () { - return MonotoneChainBuilder - }; - MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) { - var start = 0; - var startIndexList = new ArrayList(); - startIndexList.add(new Integer(start)); - do { - var last = MonotoneChainBuilder.findChainEnd(pts, start); - startIndexList.add(new Integer(last)); - start = last; - } while (start < pts.length - 1) - var startIndex = MonotoneChainBuilder.toIntArray(startIndexList); - return startIndex - }; - MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) { - var safeStart = start; - while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) { - safeStart++; - } - if (safeStart >= pts.length - 1) { - return pts.length - 1 - } - var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]); - var last = start + 1; - while (last < pts.length) { - if (!pts[last - 1].equals2D(pts[last])) { - var quad = Quadrant.quadrant(pts[last - 1], pts[last]); - if (quad !== chainQuad) { break } - } - last++; - } - return last - 1 - }; - MonotoneChainBuilder.getChains = function getChains () { - if (arguments.length === 1) { - var pts = arguments[0]; - return MonotoneChainBuilder.getChains(pts, null) - } else if (arguments.length === 2) { - var pts$1 = arguments[0]; - var context = arguments[1]; - var mcList = new ArrayList(); - var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1); - for (var i = 0; i < startIndex.length - 1; i++) { - var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context); - mcList.add(mc); - } - return mcList - } - }; - MonotoneChainBuilder.toIntArray = function toIntArray (list) { - var array = new Array(list.size()).fill(null); - for (var i = 0; i < array.length; i++) { - array[i] = list.get(i).intValue(); - } - return array - }; - - var Noder = function Noder () {}; - - Noder.prototype.computeNodes = function computeNodes (segStrings) {}; - Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {}; - Noder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Noder.prototype.getClass = function getClass () { - return Noder - }; - - var SinglePassNoder = function SinglePassNoder () { - this._segInt = null; - if (arguments.length === 0) ; else if (arguments.length === 1) { - var segInt = arguments[0]; - this.setSegmentIntersector(segInt); - } - }; - SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) { - this._segInt = segInt; - }; - SinglePassNoder.prototype.interfaces_ = function interfaces_ () { - return [Noder] - }; - SinglePassNoder.prototype.getClass = function getClass () { - return SinglePassNoder - }; - - var MCIndexNoder = (function (SinglePassNoder$$1) { - function MCIndexNoder (si) { - if (si) { SinglePassNoder$$1.call(this, si); } - else { SinglePassNoder$$1.call(this); } - this._monoChains = new ArrayList(); - this._index = new STRtree(); - this._idCounter = 0; - this._nodedSegStrings = null; - this._nOverlaps = 0; - } - - if ( SinglePassNoder$$1 ) MCIndexNoder.__proto__ = SinglePassNoder$$1; - MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype ); - MCIndexNoder.prototype.constructor = MCIndexNoder; - - var staticAccessors = { SegmentOverlapAction: { configurable: true } }; - MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () { - return this._monoChains - }; - MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () { - return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings) - }; - MCIndexNoder.prototype.getIndex = function getIndex () { - return this._index - }; - MCIndexNoder.prototype.add = function add (segStr) { - var this$1 = this; - - var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr); - for (var i = segChains.iterator(); i.hasNext();) { - var mc = i.next(); - mc.setId(this$1._idCounter++); - this$1._index.insert(mc.getEnvelope(), mc); - this$1._monoChains.add(mc); - } - }; - MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) { - var this$1 = this; - - this._nodedSegStrings = inputSegStrings; - for (var i = inputSegStrings.iterator(); i.hasNext();) { - this$1.add(i.next()); - } - this.intersectChains(); - }; - MCIndexNoder.prototype.intersectChains = function intersectChains () { - var this$1 = this; - - var overlapAction = new SegmentOverlapAction(this._segInt); - for (var i = this._monoChains.iterator(); i.hasNext();) { - var queryChain = i.next(); - var overlapChains = this$1._index.query(queryChain.getEnvelope()); - for (var j = overlapChains.iterator(); j.hasNext();) { - var testChain = j.next(); - if (testChain.getId() > queryChain.getId()) { - queryChain.computeOverlaps(testChain, overlapAction); - this$1._nOverlaps++; - } - if (this$1._segInt.isDone()) { return null } - } - } - }; - MCIndexNoder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MCIndexNoder.prototype.getClass = function getClass () { - return MCIndexNoder - }; - staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction }; - - Object.defineProperties( MCIndexNoder, staticAccessors ); - - return MCIndexNoder; - }(SinglePassNoder)); - - var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) { - function SegmentOverlapAction () { - MonotoneChainOverlapAction$$1.call(this); - this._si = null; - var si = arguments[0]; - this._si = si; - } - - if ( MonotoneChainOverlapAction$$1 ) SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1; - SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype ); - SegmentOverlapAction.prototype.constructor = SegmentOverlapAction; - SegmentOverlapAction.prototype.overlap = function overlap () { - if (arguments.length === 4) { - var mc1 = arguments[0]; - var start1 = arguments[1]; - var mc2 = arguments[2]; - var start2 = arguments[3]; - var ss1 = mc1.getContext(); - var ss2 = mc2.getContext(); - this._si.processIntersections(ss1, start1, ss2, start2); - } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) } - }; - SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentOverlapAction.prototype.getClass = function getClass () { - return SegmentOverlapAction - }; - - return SegmentOverlapAction; - }(MonotoneChainOverlapAction)); - - var BufferParameters = function BufferParameters () { - this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS; - this._endCapStyle = BufferParameters.CAP_ROUND; - this._joinStyle = BufferParameters.JOIN_ROUND; - this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT; - this._isSingleSided = false; - this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR; - - if (arguments.length === 0) ; else if (arguments.length === 1) { - var quadrantSegments = arguments[0]; - this.setQuadrantSegments(quadrantSegments); - } else if (arguments.length === 2) { - var quadrantSegments$1 = arguments[0]; - var endCapStyle = arguments[1]; - this.setQuadrantSegments(quadrantSegments$1); - this.setEndCapStyle(endCapStyle); - } else if (arguments.length === 4) { - var quadrantSegments$2 = arguments[0]; - var endCapStyle$1 = arguments[1]; - var joinStyle = arguments[2]; - var mitreLimit = arguments[3]; - this.setQuadrantSegments(quadrantSegments$2); - this.setEndCapStyle(endCapStyle$1); - this.setJoinStyle(joinStyle); - this.setMitreLimit(mitreLimit); - } - }; - - var staticAccessors$25 = { CAP_ROUND: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },JOIN_ROUND: { configurable: true },JOIN_MITRE: { configurable: true },JOIN_BEVEL: { configurable: true },DEFAULT_QUADRANT_SEGMENTS: { configurable: true },DEFAULT_MITRE_LIMIT: { configurable: true },DEFAULT_SIMPLIFY_FACTOR: { configurable: true } }; - BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () { - return this._endCapStyle - }; - BufferParameters.prototype.isSingleSided = function isSingleSided () { - return this._isSingleSided - }; - BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) { - this._quadrantSegments = quadSegs; - if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; } - if (this._quadrantSegments < 0) { - this._joinStyle = BufferParameters.JOIN_MITRE; - this._mitreLimit = Math.abs(this._quadrantSegments); - } - if (quadSegs <= 0) { - this._quadrantSegments = 1; - } - if (this._joinStyle !== BufferParameters.JOIN_ROUND) { - this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS; - } - }; - BufferParameters.prototype.getJoinStyle = function getJoinStyle () { - return this._joinStyle - }; - BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) { - this._joinStyle = joinStyle; - }; - BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) { - this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor; - }; - BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () { - return this._simplifyFactor - }; - BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () { - return this._quadrantSegments - }; - BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) { - this._endCapStyle = endCapStyle; - }; - BufferParameters.prototype.getMitreLimit = function getMitreLimit () { - return this._mitreLimit - }; - BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) { - this._mitreLimit = mitreLimit; - }; - BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) { - this._isSingleSided = isSingleSided; - }; - BufferParameters.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferParameters.prototype.getClass = function getClass () { - return BufferParameters - }; - BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) { - var alpha = Math.PI / 2.0 / quadSegs; - return 1 - Math.cos(alpha / 2.0) - }; - staticAccessors$25.CAP_ROUND.get = function () { return 1 }; - staticAccessors$25.CAP_FLAT.get = function () { return 2 }; - staticAccessors$25.CAP_SQUARE.get = function () { return 3 }; - staticAccessors$25.JOIN_ROUND.get = function () { return 1 }; - staticAccessors$25.JOIN_MITRE.get = function () { return 2 }; - staticAccessors$25.JOIN_BEVEL.get = function () { return 3 }; - staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 }; - staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 }; - staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 }; - - Object.defineProperties( BufferParameters, staticAccessors$25 ); - - var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) { - this._distanceTol = null; - this._isDeleted = null; - this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE; - this._inputLine = inputLine || null; - }; - - var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } }; - BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) { - var p0 = this._inputLine[i0]; - var p1 = this._inputLine[i1]; - var p2 = this._inputLine[i2]; - if (!this.isConcave(p0, p1, p2)) { return false } - if (!this.isShallow(p0, p1, p2, distanceTol)) { return false } - return this.isShallowSampled(p0, p1, i0, i2, distanceTol) - }; - BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () { - var this$1 = this; - - var index = 1; - // const maxIndex = this._inputLine.length - 1 - var midIndex = this.findNextNonDeletedIndex(index); - var lastIndex = this.findNextNonDeletedIndex(midIndex); - var isChanged = false; - while (lastIndex < this._inputLine.length) { - var isMiddleVertexDeleted = false; - if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) { - this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE; - isMiddleVertexDeleted = true; - isChanged = true; - } - if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; } - midIndex = this$1.findNextNonDeletedIndex(index); - lastIndex = this$1.findNextNonDeletedIndex(midIndex); - } - return isChanged - }; - BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) { - var orientation = CGAlgorithms.computeOrientation(p0, p1, p2); - var isAngleToSimplify = orientation === this._angleOrientation; - if (!isAngleToSimplify) { return false } - var dist = CGAlgorithms.distancePointLine(p1, p0, p2); - return dist < distanceTol - }; - BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) { - var this$1 = this; - - var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK); - if (inc <= 0) { inc = 1; } - for (var i = i0; i < i2; i += inc) { - if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false } - } - return true - }; - BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) { - var orientation = CGAlgorithms.computeOrientation(p0, p1, p2); - var isConcave = orientation === this._angleOrientation; - return isConcave - }; - BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) { - var this$1 = this; - - this._distanceTol = Math.abs(distanceTol); - if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; } - this._isDeleted = new Array(this._inputLine.length).fill(null); - var isChanged = false; - do { - isChanged = this$1.deleteShallowConcavities(); - } while (isChanged) - return this.collapseLine() - }; - BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) { - var next = index + 1; - while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; } - return next - }; - BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) { - var dist = CGAlgorithms.distancePointLine(p1, p0, p2); - return dist < distanceTol - }; - BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () { - var this$1 = this; - - var coordList = new CoordinateList(); - for (var i = 0; i < this._inputLine.length; i++) { - if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); } - } - return coordList.toCoordinateArray() - }; - BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferInputLineSimplifier.prototype.getClass = function getClass () { - return BufferInputLineSimplifier - }; - BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) { - var simp = new BufferInputLineSimplifier(inputLine); - return simp.simplify(distanceTol) - }; - staticAccessors$26.INIT.get = function () { return 0 }; - staticAccessors$26.DELETE.get = function () { return 1 }; - staticAccessors$26.KEEP.get = function () { return 1 }; - staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 }; - - Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 ); - - var OffsetSegmentString = function OffsetSegmentString () { - this._ptList = null; - this._precisionModel = null; - this._minimimVertexDistance = 0.0; - this._ptList = new ArrayList(); - }; - - var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } }; - OffsetSegmentString.prototype.getCoordinates = function getCoordinates () { - var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE); - return coord - }; - OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) { - this._precisionModel = precisionModel; - }; - OffsetSegmentString.prototype.addPt = function addPt (pt) { - var bufPt = new Coordinate(pt); - this._precisionModel.makePrecise(bufPt); - if (this.isRedundant(bufPt)) { return null } - this._ptList.add(bufPt); - }; - OffsetSegmentString.prototype.revere = function revere () {}; - OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) { - var this$1 = this; - - if (isForward) { - for (var i = 0; i < pt.length; i++) { - this$1.addPt(pt[i]); - } - } else { - for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) { - this$1.addPt(pt[i$1]); - } - } - }; - OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) { - if (this._ptList.size() < 1) { return false } - var lastPt = this._ptList.get(this._ptList.size() - 1); - var ptDist = pt.distance(lastPt); - if (ptDist < this._minimimVertexDistance) { return true } - return false - }; - OffsetSegmentString.prototype.toString = function toString () { - var fact = new GeometryFactory(); - var line = fact.createLineString(this.getCoordinates()); - return line.toString() - }; - OffsetSegmentString.prototype.closeRing = function closeRing () { - if (this._ptList.size() < 1) { return null } - var startPt = new Coordinate(this._ptList.get(0)); - var lastPt = this._ptList.get(this._ptList.size() - 1); - // const last2Pt = null - // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2) - if (startPt.equals(lastPt)) { return null } - this._ptList.add(startPt); - }; - OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) { - this._minimimVertexDistance = minimimVertexDistance; - }; - OffsetSegmentString.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OffsetSegmentString.prototype.getClass = function getClass () { - return OffsetSegmentString - }; - staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) }; - - Object.defineProperties( OffsetSegmentString, staticAccessors$28 ); - - var Angle = function Angle () {}; - - var staticAccessors$29 = { PI_TIMES_2: { configurable: true },PI_OVER_2: { configurable: true },PI_OVER_4: { configurable: true },COUNTERCLOCKWISE: { configurable: true },CLOCKWISE: { configurable: true },NONE: { configurable: true } }; - - Angle.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Angle.prototype.getClass = function getClass () { - return Angle - }; - Angle.toDegrees = function toDegrees (radians) { - return radians * 180 / Math.PI - }; - Angle.normalize = function normalize (angle) { - while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; } - while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; } - return angle - }; - Angle.angle = function angle () { - if (arguments.length === 1) { - var p = arguments[0]; - return Math.atan2(p.y, p.x) - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - var dx = p1.x - p0.x; - var dy = p1.y - p0.y; - return Math.atan2(dy, dx) - } - }; - Angle.isAcute = function isAcute (p0, p1, p2) { - var dx0 = p0.x - p1.x; - var dy0 = p0.y - p1.y; - var dx1 = p2.x - p1.x; - var dy1 = p2.y - p1.y; - var dotprod = dx0 * dx1 + dy0 * dy1; - return dotprod > 0 - }; - Angle.isObtuse = function isObtuse (p0, p1, p2) { - var dx0 = p0.x - p1.x; - var dy0 = p0.y - p1.y; - var dx1 = p2.x - p1.x; - var dy1 = p2.y - p1.y; - var dotprod = dx0 * dx1 + dy0 * dy1; - return dotprod < 0 - }; - Angle.interiorAngle = function interiorAngle (p0, p1, p2) { - var anglePrev = Angle.angle(p1, p0); - var angleNext = Angle.angle(p1, p2); - return Math.abs(angleNext - anglePrev) - }; - Angle.normalizePositive = function normalizePositive (angle) { - if (angle < 0.0) { - while (angle < 0.0) { angle += Angle.PI_TIMES_2; } - if (angle >= Angle.PI_TIMES_2) { angle = 0.0; } - } else { - while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; } - if (angle < 0.0) { angle = 0.0; } - } - return angle - }; - Angle.angleBetween = function angleBetween (tip1, tail, tip2) { - var a1 = Angle.angle(tail, tip1); - var a2 = Angle.angle(tail, tip2); - return Angle.diff(a1, a2) - }; - Angle.diff = function diff (ang1, ang2) { - var delAngle = null; - if (ang1 < ang2) { - delAngle = ang2 - ang1; - } else { - delAngle = ang1 - ang2; - } - if (delAngle > Math.PI) { - delAngle = 2 * Math.PI - delAngle; - } - return delAngle - }; - Angle.toRadians = function toRadians (angleDegrees) { - return angleDegrees * Math.PI / 180.0 - }; - Angle.getTurn = function getTurn (ang1, ang2) { - var crossproduct = Math.sin(ang2 - ang1); - if (crossproduct > 0) { - return Angle.COUNTERCLOCKWISE - } - if (crossproduct < 0) { - return Angle.CLOCKWISE - } - return Angle.NONE - }; - Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) { - var a1 = Angle.angle(tail, tip1); - var a2 = Angle.angle(tail, tip2); - var angDel = a2 - a1; - if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 } - if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 } - return angDel - }; - staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI }; - staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 }; - staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 }; - staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE }; - staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE }; - staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR }; - - Object.defineProperties( Angle, staticAccessors$29 ); - - var OffsetSegmentGenerator = function OffsetSegmentGenerator () { - this._maxCurveSegmentError = 0.0; - this._filletAngleQuantum = null; - this._closingSegLengthFactor = 1; - this._segList = null; - this._distance = 0.0; - this._precisionModel = null; - this._bufParams = null; - this._li = null; - this._s0 = null; - this._s1 = null; - this._s2 = null; - this._seg0 = new LineSegment(); - this._seg1 = new LineSegment(); - this._offset0 = new LineSegment(); - this._offset1 = new LineSegment(); - this._side = 0; - this._hasNarrowConcaveAngle = false; - var precisionModel = arguments[0]; - var bufParams = arguments[1]; - var distance = arguments[2]; - this._precisionModel = precisionModel; - this._bufParams = bufParams; - this._li = new RobustLineIntersector(); - this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments(); - if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; } - this.init(distance); - }; - - var staticAccessors$27 = { OFFSET_SEGMENT_SEPARATION_FACTOR: { configurable: true },INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR: { configurable: true },CURVE_VERTEX_SNAP_DISTANCE_FACTOR: { configurable: true },MAX_CLOSING_SEG_LEN_FACTOR: { configurable: true } }; - OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) { - this._s0 = this._s1; - this._s1 = this._s2; - this._s2 = p; - this._seg0.setCoordinates(this._s0, this._s1); - this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0); - this._seg1.setCoordinates(this._s1, this._s2); - this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1); - if (this._s1.equals(this._s2)) { return null } - var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2); - var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT); - if (orientation === 0) { - this.addCollinear(addStartPoint); - } else if (outsideTurn) { - this.addOutsideTurn(orientation, addStartPoint); - } else { - this.addInsideTurn(orientation, addStartPoint); - } - }; - OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) { - var seg = new LineSegment(p0, p1); - var offsetL = new LineSegment(); - this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL); - var offsetR = new LineSegment(); - this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR); - var dx = p1.x - p0.x; - var dy = p1.y - p0.y; - var angle = Math.atan2(dy, dx); - switch (this._bufParams.getEndCapStyle()) { - case BufferParameters.CAP_ROUND: - this._segList.addPt(offsetL.p1); - this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance); - this._segList.addPt(offsetR.p1); - break - case BufferParameters.CAP_FLAT: - this._segList.addPt(offsetL.p1); - this._segList.addPt(offsetR.p1); - break - case BufferParameters.CAP_SQUARE: - var squareCapSideOffset = new Coordinate(); - squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle); - squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle); - var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y); - var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y); - this._segList.addPt(squareCapLOffset); - this._segList.addPt(squareCapROffset); - break - } - }; - OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () { - var pts = this._segList.getCoordinates(); - return pts - }; - OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) { - var isMitreWithinLimit = true; - var intPt = null; - try { - intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1); - var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance); - if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; } - } catch (ex) { - if (ex instanceof NotRepresentableException) { - intPt = new Coordinate(0, 0); - isMitreWithinLimit = false; - } else { throw ex } - } finally {} - if (isMitreWithinLimit) { - this._segList.addPt(intPt); - } else { - this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit()); - } - }; - OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) { - var dx0 = p0.x - p.x; - var dy0 = p0.y - p.y; - var startAngle = Math.atan2(dy0, dx0); - var dx1 = p1.x - p.x; - var dy1 = p1.y - p.y; - var endAngle = Math.atan2(dy1, dx1); - if (direction === CGAlgorithms.CLOCKWISE) { - if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; } - } else { - if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; } - } - this._segList.addPt(p0); - this.addFilletArc(p, startAngle, endAngle, direction, radius); - this._segList.addPt(p1); - }; - OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) { - if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) { - this._segList.addPt(this._offset0.p1); - return null - } - if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) { - this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance); - } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) { - this.addBevelJoin(this._offset0, this._offset1); - } else { - if (addStartPoint) { this._segList.addPt(this._offset0.p1); } - this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance); - this._segList.addPt(this._offset1.p0); - } - }; - OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) { - this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance)); - this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance)); - this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance)); - this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance)); - this._segList.closeRing(); - }; - OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) { - this._segList.addPts(pt, isForward); - }; - OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () { - this._segList.addPt(this._offset1.p0); - }; - OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () { - this._segList.addPt(this._offset1.p1); - }; - OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) { - this._s1 = s1; - this._s2 = s2; - this._side = side; - this._seg1.setCoordinates(s1, s2); - this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1); - }; - OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) { - var basePt = this._seg0.p1; - var ang0 = Angle.angle(basePt, this._seg0.p0); - // const ang1 = Angle.angle(basePt, this._seg1.p1) - var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1); - var angDiffHalf = angDiff / 2; - var midAng = Angle.normalize(ang0 + angDiffHalf); - var mitreMidAng = Angle.normalize(midAng + Math.PI); - var mitreDist = mitreLimit * distance; - var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf)); - var bevelHalfLen = distance - bevelDelta; - var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng); - var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng); - var bevelMidPt = new Coordinate(bevelMidX, bevelMidY); - var mitreMidLine = new LineSegment(basePt, bevelMidPt); - var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen); - var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen); - if (this._side === Position.LEFT) { - this._segList.addPt(bevelEndLeft); - this._segList.addPt(bevelEndRight); - } else { - this._segList.addPt(bevelEndRight); - this._segList.addPt(bevelEndLeft); - } - }; - OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) { - var sideSign = side === Position.LEFT ? 1 : -1; - var dx = seg.p1.x - seg.p0.x; - var dy = seg.p1.y - seg.p0.y; - var len = Math.sqrt(dx * dx + dy * dy); - var ux = sideSign * distance * dx / len; - var uy = sideSign * distance * dy / len; - offset.p0.x = seg.p0.x - uy; - offset.p0.y = seg.p0.y + ux; - offset.p1.x = seg.p1.x - uy; - offset.p1.y = seg.p1.y + ux; - }; - OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) { - var this$1 = this; - - var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1; - var totalAngle = Math.abs(startAngle - endAngle); - var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5); - if (nSegs < 1) { return null } - var initAngle = 0.0; - var currAngleInc = totalAngle / nSegs; - var currAngle = initAngle; - var pt = new Coordinate(); - while (currAngle < totalAngle) { - var angle = startAngle + directionFactor * currAngle; - pt.x = p.x + radius * Math.cos(angle); - pt.y = p.y + radius * Math.sin(angle); - this$1._segList.addPt(pt); - currAngle += currAngleInc; - } - }; - OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) { - this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1); - if (this._li.hasIntersection()) { - this._segList.addPt(this._li.getIntersection(0)); - } else { - this._hasNarrowConcaveAngle = true; - if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) { - this._segList.addPt(this._offset0.p1); - } else { - this._segList.addPt(this._offset0.p1); - if (this._closingSegLengthFactor > 0) { - var mid0 = new Coordinate((this._closingSegLengthFactor * this._offset0.p1.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset0.p1.y + this._s1.y) / (this._closingSegLengthFactor + 1)); - this._segList.addPt(mid0); - var mid1 = new Coordinate((this._closingSegLengthFactor * this._offset1.p0.x + this._s1.x) / (this._closingSegLengthFactor + 1), (this._closingSegLengthFactor * this._offset1.p0.y + this._s1.y) / (this._closingSegLengthFactor + 1)); - this._segList.addPt(mid1); - } else { - this._segList.addPt(this._s1); - } - this._segList.addPt(this._offset1.p0); - } - } - }; - OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) { - var pt = new Coordinate(p.x + this._distance, p.y); - this._segList.addPt(pt); - this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance); - this._segList.closeRing(); - }; - OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) { - this._segList.addPt(offset0.p1); - this._segList.addPt(offset1.p0); - }; - OffsetSegmentGenerator.prototype.init = function init (distance) { - this._distance = distance; - this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0)); - this._segList = new OffsetSegmentString(); - this._segList.setPrecisionModel(this._precisionModel); - this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR); - }; - OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) { - this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2); - var numInt = this._li.getIntersectionNum(); - if (numInt >= 2) { - if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) { - if (addStartPoint) { this._segList.addPt(this._offset0.p1); } - this._segList.addPt(this._offset1.p0); - } else { - this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance); - } - } - }; - OffsetSegmentGenerator.prototype.closeRing = function closeRing () { - this._segList.closeRing(); - }; - OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () { - return this._hasNarrowConcaveAngle - }; - OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OffsetSegmentGenerator.prototype.getClass = function getClass () { - return OffsetSegmentGenerator - }; - staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 }; - staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 }; - staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 }; - staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 }; - - Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 ); - - var OffsetCurveBuilder = function OffsetCurveBuilder () { - this._distance = 0.0; - this._precisionModel = null; - this._bufParams = null; - var precisionModel = arguments[0]; - var bufParams = arguments[1]; - this._precisionModel = precisionModel; - this._bufParams = bufParams; - }; - OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) { - this._distance = distance; - if (distance === 0.0) { return null } - var isRightSide = distance < 0.0; - var posDistance = Math.abs(distance); - var segGen = this.getSegGen(posDistance); - if (inputPts.length <= 1) { - this.computePointCurve(inputPts[0], segGen); - } else { - this.computeOffsetCurve(inputPts, isRightSide, segGen); - } - var curvePts = segGen.getCoordinates(); - if (isRightSide) { CoordinateArrays.reverse(curvePts); } - return curvePts - }; - OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) { - var distTol = this.simplifyTolerance(this._distance); - if (isRightSide) { - segGen.addSegments(inputPts, true); - var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol); - var n2 = simp2.length - 1; - segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT); - segGen.addFirstSegment(); - for (var i = n2 - 2; i >= 0; i--) { - segGen.addNextSegment(simp2[i], true); - } - } else { - segGen.addSegments(inputPts, false); - var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol); - var n1 = simp1.length - 1; - segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT); - segGen.addFirstSegment(); - for (var i$1 = 2; i$1 <= n1; i$1++) { - segGen.addNextSegment(simp1[i$1], true); - } - } - segGen.addLastSegment(); - segGen.closeRing(); - }; - OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) { - var distTol = this.simplifyTolerance(this._distance); - if (side === Position.RIGHT) { distTol = -distTol; } - var simp = BufferInputLineSimplifier.simplify(inputPts, distTol); - var n = simp.length - 1; - segGen.initSideSegments(simp[n - 1], simp[0], side); - for (var i = 1; i <= n; i++) { - var addStartPoint = i !== 1; - segGen.addNextSegment(simp[i], addStartPoint); - } - segGen.closeRing(); - }; - OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) { - var distTol = this.simplifyTolerance(this._distance); - var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol); - var n1 = simp1.length - 1; - segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT); - for (var i = 2; i <= n1; i++) { - segGen.addNextSegment(simp1[i], true); - } - segGen.addLastSegment(); - segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]); - var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol); - var n2 = simp2.length - 1; - segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT); - for (var i$1 = n2 - 2; i$1 >= 0; i$1--) { - segGen.addNextSegment(simp2[i$1], true); - } - segGen.addLastSegment(); - segGen.addLineEndCap(simp2[1], simp2[0]); - segGen.closeRing(); - }; - OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) { - switch (this._bufParams.getEndCapStyle()) { - case BufferParameters.CAP_ROUND: - segGen.createCircle(pt); - break - case BufferParameters.CAP_SQUARE: - segGen.createSquare(pt); - break - } - }; - OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) { - this._distance = distance; - if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null } - if (distance === 0.0) { return null } - var posDistance = Math.abs(distance); - var segGen = this.getSegGen(posDistance); - if (inputPts.length <= 1) { - this.computePointCurve(inputPts[0], segGen); - } else { - if (this._bufParams.isSingleSided()) { - var isRightSide = distance < 0.0; - this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen); - } else { this.computeLineBufferCurve(inputPts, segGen); } - } - var lineCoord = segGen.getCoordinates(); - return lineCoord - }; - OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () { - return this._bufParams - }; - OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) { - return bufDistance * this._bufParams.getSimplifyFactor() - }; - OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) { - this._distance = distance; - if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) } - if (distance === 0.0) { - return OffsetCurveBuilder.copyCoordinates(inputPts) - } - var segGen = this.getSegGen(distance); - this.computeRingBufferCurve(inputPts, side, segGen); - return segGen.getCoordinates() - }; - OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) { - var distTol = this.simplifyTolerance(this._distance); - if (isRightSide) { - var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol); - var n2 = simp2.length - 1; - segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT); - segGen.addFirstSegment(); - for (var i = n2 - 2; i >= 0; i--) { - segGen.addNextSegment(simp2[i], true); - } - } else { - var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol); - var n1 = simp1.length - 1; - segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT); - segGen.addFirstSegment(); - for (var i$1 = 2; i$1 <= n1; i$1++) { - segGen.addNextSegment(simp1[i$1], true); - } - } - segGen.addLastSegment(); - }; - OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) { - return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance) - }; - OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OffsetCurveBuilder.prototype.getClass = function getClass () { - return OffsetCurveBuilder - }; - OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) { - var copy = new Array(pts.length).fill(null); - for (var i = 0; i < copy.length; i++) { - copy[i] = new Coordinate(pts[i]); - } - return copy - }; - - var SubgraphDepthLocater = function SubgraphDepthLocater () { - this._subgraphs = null; - this._seg = new LineSegment(); - this._cga = new CGAlgorithms(); - var subgraphs = arguments[0]; - this._subgraphs = subgraphs; - }; - - var staticAccessors$30 = { DepthSegment: { configurable: true } }; - SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () { - var this$1 = this; - - if (arguments.length === 1) { - var stabbingRayLeftPt = arguments[0]; - var stabbedSegments = new ArrayList(); - for (var i = this._subgraphs.iterator(); i.hasNext();) { - var bsg = i.next(); - var env = bsg.getEnvelope(); - if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue } - this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments); - } - return stabbedSegments - } else if (arguments.length === 3) { - if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) { - var stabbingRayLeftPt$1 = arguments[0]; - var dirEdge = arguments[1]; - var stabbedSegments$1 = arguments[2]; - var pts = dirEdge.getEdge().getCoordinates(); - for (var i$1 = 0; i$1 < pts.length - 1; i$1++) { - this$1._seg.p0 = pts[i$1]; - this$1._seg.p1 = pts[i$1 + 1]; - if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); } - var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x); - if (maxx < stabbingRayLeftPt$1.x) { continue } - if (this$1._seg.isHorizontal()) { continue } - if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue } - if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue } - var depth = dirEdge.getDepth(Position.LEFT); - if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); } - var ds = new DepthSegment(this$1._seg, depth); - stabbedSegments$1.add(ds); - } - } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) { - var stabbingRayLeftPt$2 = arguments[0]; - var dirEdges = arguments[1]; - var stabbedSegments$2 = arguments[2]; - for (var i$2 = dirEdges.iterator(); i$2.hasNext();) { - var de = i$2.next(); - if (!de.isForward()) { continue } - this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2); - } - } - } - }; - SubgraphDepthLocater.prototype.getDepth = function getDepth (p) { - var stabbedSegments = this.findStabbedSegments(p); - if (stabbedSegments.size() === 0) { return 0 } - var ds = Collections.min(stabbedSegments); - return ds._leftDepth - }; - SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SubgraphDepthLocater.prototype.getClass = function getClass () { - return SubgraphDepthLocater - }; - staticAccessors$30.DepthSegment.get = function () { return DepthSegment }; - - Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 ); - - var DepthSegment = function DepthSegment () { - this._upwardSeg = null; - this._leftDepth = null; - var seg = arguments[0]; - var depth = arguments[1]; - this._upwardSeg = new LineSegment(seg); - this._leftDepth = depth; - }; - DepthSegment.prototype.compareTo = function compareTo (obj) { - var other = obj; - if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 } - if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 } - var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg); - if (orientIndex !== 0) { return orientIndex } - orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg); - if (orientIndex !== 0) { return orientIndex } - return this._upwardSeg.compareTo(other._upwardSeg) - }; - DepthSegment.prototype.compareX = function compareX (seg0, seg1) { - var compare0 = seg0.p0.compareTo(seg1.p0); - if (compare0 !== 0) { return compare0 } - return seg0.p1.compareTo(seg1.p1) - }; - DepthSegment.prototype.toString = function toString () { - return this._upwardSeg.toString() - }; - DepthSegment.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - DepthSegment.prototype.getClass = function getClass () { - return DepthSegment - }; - - var Triangle = function Triangle (p0, p1, p2) { - this.p0 = p0 || null; - this.p1 = p1 || null; - this.p2 = p2 || null; - }; - Triangle.prototype.area = function area () { - return Triangle.area(this.p0, this.p1, this.p2) - }; - Triangle.prototype.signedArea = function signedArea () { - return Triangle.signedArea(this.p0, this.p1, this.p2) - }; - Triangle.prototype.interpolateZ = function interpolateZ (p) { - if (p === null) { throw new IllegalArgumentException('Supplied point is null.') } - return Triangle.interpolateZ(p, this.p0, this.p1, this.p2) - }; - Triangle.prototype.longestSideLength = function longestSideLength () { - return Triangle.longestSideLength(this.p0, this.p1, this.p2) - }; - Triangle.prototype.isAcute = function isAcute () { - return Triangle.isAcute(this.p0, this.p1, this.p2) - }; - Triangle.prototype.circumcentre = function circumcentre () { - return Triangle.circumcentre(this.p0, this.p1, this.p2) - }; - Triangle.prototype.area3D = function area3D () { - return Triangle.area3D(this.p0, this.p1, this.p2) - }; - Triangle.prototype.centroid = function centroid () { - return Triangle.centroid(this.p0, this.p1, this.p2) - }; - Triangle.prototype.inCentre = function inCentre () { - return Triangle.inCentre(this.p0, this.p1, this.p2) - }; - Triangle.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Triangle.prototype.getClass = function getClass () { - return Triangle - }; - Triangle.area = function area (a, b, c) { - return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2) - }; - Triangle.signedArea = function signedArea (a, b, c) { - return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2 - }; - Triangle.det = function det (m00, m01, m10, m11) { - return m00 * m11 - m01 * m10 - }; - Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) { - var x0 = v0.x; - var y0 = v0.y; - var a = v1.x - x0; - var b = v2.x - x0; - var c = v1.y - y0; - var d = v2.y - y0; - var det = a * d - b * c; - var dx = p.x - x0; - var dy = p.y - y0; - var t = (d * dx - b * dy) / det; - var u = (-c * dx + a * dy) / det; - var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z); - return z - }; - Triangle.longestSideLength = function longestSideLength (a, b, c) { - var lenAB = a.distance(b); - var lenBC = b.distance(c); - var lenCA = c.distance(a); - var maxLen = lenAB; - if (lenBC > maxLen) { maxLen = lenBC; } - if (lenCA > maxLen) { maxLen = lenCA; } - return maxLen - }; - Triangle.isAcute = function isAcute (a, b, c) { - if (!Angle.isAcute(a, b, c)) { return false } - if (!Angle.isAcute(b, c, a)) { return false } - if (!Angle.isAcute(c, a, b)) { return false } - return true - }; - Triangle.circumcentre = function circumcentre (a, b, c) { - var cx = c.x; - var cy = c.y; - var ax = a.x - cx; - var ay = a.y - cy; - var bx = b.x - cx; - var by = b.y - cy; - var denom = 2 * Triangle.det(ax, ay, bx, by); - var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by); - var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by); - var ccx = cx - numx / denom; - var ccy = cy + numy / denom; - return new Coordinate(ccx, ccy) - }; - Triangle.perpendicularBisector = function perpendicularBisector (a, b) { - var dx = b.x - a.x; - var dy = b.y - a.y; - var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0); - var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0); - return new HCoordinate(l1, l2) - }; - Triangle.angleBisector = function angleBisector (a, b, c) { - var len0 = b.distance(a); - var len2 = b.distance(c); - var frac = len0 / (len0 + len2); - var dx = c.x - a.x; - var dy = c.y - a.y; - var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy); - return splitPt - }; - Triangle.area3D = function area3D (a, b, c) { - var ux = b.x - a.x; - var uy = b.y - a.y; - var uz = b.z - a.z; - var vx = c.x - a.x; - var vy = c.y - a.y; - var vz = c.z - a.z; - var crossx = uy * vz - uz * vy; - var crossy = uz * vx - ux * vz; - var crossz = ux * vy - uy * vx; - var absSq = crossx * crossx + crossy * crossy + crossz * crossz; - var area3D = Math.sqrt(absSq) / 2; - return area3D - }; - Triangle.centroid = function centroid (a, b, c) { - var x = (a.x + b.x + c.x) / 3; - var y = (a.y + b.y + c.y) / 3; - return new Coordinate(x, y) - }; - Triangle.inCentre = function inCentre (a, b, c) { - var len0 = b.distance(c); - var len1 = a.distance(c); - var len2 = a.distance(b); - var circum = len0 + len1 + len2; - var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum; - var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum; - return new Coordinate(inCentreX, inCentreY) - }; - - var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () { - this._inputGeom = null; - this._distance = null; - this._curveBuilder = null; - this._curveList = new ArrayList(); - var inputGeom = arguments[0]; - var distance = arguments[1]; - var curveBuilder = arguments[2]; - this._inputGeom = inputGeom; - this._distance = distance; - this._curveBuilder = curveBuilder; - }; - OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) { - if (this._distance <= 0.0) { return null } - var coord = p.getCoordinates(); - var curve = this._curveBuilder.getLineCurve(coord, this._distance); - this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR); - }; - OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) { - var this$1 = this; - - var offsetDistance = this._distance; - var offsetSide = Position.LEFT; - if (this._distance < 0.0) { - offsetDistance = -this._distance; - offsetSide = Position.RIGHT; - } - var shell = p.getExteriorRing(); - var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates()); - if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null } - if (this._distance <= 0.0 && shellCoord.length < 3) { return null } - this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR); - for (var i = 0; i < p.getNumInteriorRing(); i++) { - var hole = p.getInteriorRingN(i); - var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates()); - if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue } - this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR); - } - }; - OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) { - var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]); - var inCentre = tri.inCentre(); - var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1); - return distToCentre < Math.abs(bufferDistance) - }; - OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) { - if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null } - var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates()); - var curve = this._curveBuilder.getLineCurve(coord, this._distance); - this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR); - }; - OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) { - if (coord === null || coord.length < 2) { return null } - var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc)); - this._curveList.add(e); - }; - OffsetCurveSetBuilder.prototype.getCurves = function getCurves () { - this.add(this._inputGeom); - return this._curveList - }; - OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) { - if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null } - var leftLoc = cwLeftLoc; - var rightLoc = cwRightLoc; - if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) { - leftLoc = cwRightLoc; - rightLoc = cwLeftLoc; - side = Position.opposite(side); - } - var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance); - this.addCurve(curve, leftLoc, rightLoc); - }; - OffsetCurveSetBuilder.prototype.add = function add (g) { - if (g.isEmpty()) { return null } - if (g instanceof Polygon) { this.addPolygon(g); } - else if (g instanceof LineString) { this.addLineString(g); } - else if (g instanceof Point$1) { this.addPoint(g); } - else if (g instanceof MultiPoint) { this.addCollection(g); } - else if (g instanceof MultiLineString) { this.addCollection(g); } - else if (g instanceof MultiPolygon) { this.addCollection(g); } - else if (g instanceof GeometryCollection) { this.addCollection(g); } - // else throw new UnsupportedOperationException(g.getClass().getName()) - }; - OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) { - var ringCoord = ring.getCoordinates(); - // const minDiam = 0.0 - if (ringCoord.length < 4) { return bufferDistance < 0 } - if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) } - var env = ring.getEnvelopeInternal(); - var envMinDimension = Math.min(env.getHeight(), env.getWidth()); - if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true } - return false - }; - OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) { - var this$1 = this; - - for (var i = 0; i < gc.getNumGeometries(); i++) { - var g = gc.getGeometryN(i); - this$1.add(g); - } - }; - OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OffsetCurveSetBuilder.prototype.getClass = function getClass () { - return OffsetCurveSetBuilder - }; - - var PointOnGeometryLocator = function PointOnGeometryLocator () {}; - - PointOnGeometryLocator.prototype.locate = function locate (p) {}; - PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PointOnGeometryLocator.prototype.getClass = function getClass () { - return PointOnGeometryLocator - }; - - var GeometryCollectionIterator = function GeometryCollectionIterator () { - this._parent = null; - this._atStart = null; - this._max = null; - this._index = null; - this._subcollectionIterator = null; - var parent = arguments[0]; - this._parent = parent; - this._atStart = true; - this._index = 0; - this._max = parent.getNumGeometries(); - }; - GeometryCollectionIterator.prototype.next = function next () { - if (this._atStart) { - this._atStart = false; - if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; } - return this._parent - } - if (this._subcollectionIterator !== null) { - if (this._subcollectionIterator.hasNext()) { - return this._subcollectionIterator.next() - } else { - this._subcollectionIterator = null; - } - } - if (this._index >= this._max) { - throw new NoSuchElementException() - } - var obj = this._parent.getGeometryN(this._index++); - if (obj instanceof GeometryCollection) { - this._subcollectionIterator = new GeometryCollectionIterator(obj); - return this._subcollectionIterator.next() - } - return obj - }; - GeometryCollectionIterator.prototype.remove = function remove () { - throw new Error(this.getClass().getName()) - }; - GeometryCollectionIterator.prototype.hasNext = function hasNext () { - if (this._atStart) { - return true - } - if (this._subcollectionIterator !== null) { - if (this._subcollectionIterator.hasNext()) { - return true - } - this._subcollectionIterator = null; - } - if (this._index >= this._max) { - return false - } - return true - }; - GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () { - return [Iterator$1] - }; - GeometryCollectionIterator.prototype.getClass = function getClass () { - return GeometryCollectionIterator - }; - GeometryCollectionIterator.isAtomic = function isAtomic (geom) { - return !(geom instanceof GeometryCollection) - }; - - var SimplePointInAreaLocator = function SimplePointInAreaLocator () { - this._geom = null; - var geom = arguments[0]; - this._geom = geom; - }; - SimplePointInAreaLocator.prototype.locate = function locate (p) { - return SimplePointInAreaLocator.locate(p, this._geom) - }; - SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () { - return [PointOnGeometryLocator] - }; - SimplePointInAreaLocator.prototype.getClass = function getClass () { - return SimplePointInAreaLocator - }; - SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) { - if (!ring.getEnvelopeInternal().intersects(p)) { return false } - return CGAlgorithms.isPointInRing(p, ring.getCoordinates()) - }; - SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) { - if (poly.isEmpty()) { return false } - var shell = poly.getExteriorRing(); - if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false } - for (var i = 0; i < poly.getNumInteriorRing(); i++) { - var hole = poly.getInteriorRingN(i); - if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false } - } - return true - }; - SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) { - if (geom instanceof Polygon) { - return SimplePointInAreaLocator.containsPointInPolygon(p, geom) - } else if (geom instanceof GeometryCollection) { - var geomi = new GeometryCollectionIterator(geom); - while (geomi.hasNext()) { - var g2 = geomi.next(); - if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } } - } - } - return false - }; - SimplePointInAreaLocator.locate = function locate (p, geom) { - if (geom.isEmpty()) { return Location.EXTERIOR } - if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR } - return Location.EXTERIOR - }; - - var EdgeEndStar = function EdgeEndStar () { - this._edgeMap = new TreeMap(); - this._edgeList = null; - this._ptInAreaLocation = [Location.NONE, Location.NONE]; - }; - EdgeEndStar.prototype.getNextCW = function getNextCW (ee) { - this.getEdges(); - var i = this._edgeList.indexOf(ee); - var iNextCW = i - 1; - if (i === 0) { iNextCW = this._edgeList.size() - 1; } - return this._edgeList.get(iNextCW) - }; - EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) { - var startLoc = Location.NONE; - for (var it = this.iterator(); it.hasNext();) { - var e = it.next(); - var label = e.getLabel(); - if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); } - } - if (startLoc === Location.NONE) { return null } - var currLoc = startLoc; - for (var it$1 = this.iterator(); it$1.hasNext();) { - var e$1 = it$1.next(); - var label$1 = e$1.getLabel(); - if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); } - if (label$1.isArea(geomIndex)) { - var leftLoc = label$1.getLocation(geomIndex, Position.LEFT); - var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT); - if (rightLoc !== Location.NONE) { - if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) } - if (leftLoc === Location.NONE) { - Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')'); - } - currLoc = leftLoc; - } else { - Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side'); - label$1.setLocation(geomIndex, Position.RIGHT, currLoc); - label$1.setLocation(geomIndex, Position.LEFT, currLoc); - } - } - } - }; - EdgeEndStar.prototype.getCoordinate = function getCoordinate () { - var it = this.iterator(); - if (!it.hasNext()) { return null } - var e = it.next(); - return e.getCoordinate() - }; - EdgeEndStar.prototype.print = function print (out) { - System.out.println('EdgeEndStar: ' + this.getCoordinate()); - for (var it = this.iterator(); it.hasNext();) { - var e = it.next(); - e.print(out); - } - }; - EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) { - this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule()); - return this.checkAreaLabelsConsistent(0) - }; - EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) { - var edges = this.getEdges(); - if (edges.size() <= 0) { return true } - var lastEdgeIndex = edges.size() - 1; - var startLabel = edges.get(lastEdgeIndex).getLabel(); - var startLoc = startLabel.getLocation(geomIndex, Position.LEFT); - Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge'); - var currLoc = startLoc; - for (var it = this.iterator(); it.hasNext();) { - var e = it.next(); - var label = e.getLabel(); - Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge'); - var leftLoc = label.getLocation(geomIndex, Position.LEFT); - var rightLoc = label.getLocation(geomIndex, Position.RIGHT); - if (leftLoc === rightLoc) { - return false - } - if (rightLoc !== currLoc) { - return false - } - currLoc = leftLoc; - } - return true - }; - EdgeEndStar.prototype.findIndex = function findIndex (eSearch) { - var this$1 = this; - - this.iterator(); - for (var i = 0; i < this._edgeList.size(); i++) { - var e = this$1._edgeList.get(i); - if (e === eSearch) { return i } - } - return -1 - }; - EdgeEndStar.prototype.iterator = function iterator () { - return this.getEdges().iterator() - }; - EdgeEndStar.prototype.getEdges = function getEdges () { - if (this._edgeList === null) { - this._edgeList = new ArrayList(this._edgeMap.values()); - } - return this._edgeList - }; - EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) { - if (this._ptInAreaLocation[geomIndex] === Location.NONE) { - this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry()); - } - return this._ptInAreaLocation[geomIndex] - }; - EdgeEndStar.prototype.toString = function toString () { - var buf = new StringBuffer(); - buf.append('EdgeEndStar: ' + this.getCoordinate()); - buf.append('\n'); - for (var it = this.iterator(); it.hasNext();) { - var e = it.next(); - buf.append(e); - buf.append('\n'); - } - return buf.toString() - }; - EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) { - for (var it = this.iterator(); it.hasNext();) { - var ee = it.next(); - ee.computeLabel(boundaryNodeRule); - } - }; - EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) { - var this$1 = this; - - this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule()); - this.propagateSideLabels(0); - this.propagateSideLabels(1); - var hasDimensionalCollapseEdge = [false, false]; - for (var it = this.iterator(); it.hasNext();) { - var e = it.next(); - var label = e.getLabel(); - for (var geomi = 0; geomi < 2; geomi++) { - if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; } - } - } - for (var it$1 = this.iterator(); it$1.hasNext();) { - var e$1 = it$1.next(); - var label$1 = e$1.getLabel(); - for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) { - if (label$1.isAnyNull(geomi$1)) { - var loc = Location.NONE; - if (hasDimensionalCollapseEdge[geomi$1]) { - loc = Location.EXTERIOR; - } else { - var p = e$1.getCoordinate(); - loc = this$1.getLocation(geomi$1, p, geomGraph); - } - label$1.setAllLocationsIfNull(geomi$1, loc); - } - } - } - }; - EdgeEndStar.prototype.getDegree = function getDegree () { - return this._edgeMap.size() - }; - EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) { - this._edgeMap.put(e, obj); - this._edgeList = null; - }; - EdgeEndStar.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeEndStar.prototype.getClass = function getClass () { - return EdgeEndStar - }; - - var DirectedEdgeStar = (function (EdgeEndStar$$1) { - function DirectedEdgeStar () { - EdgeEndStar$$1.call(this); - this._resultAreaEdgeList = null; - this._label = null; - this._SCANNING_FOR_INCOMING = 1; - this._LINKING_TO_OUTGOING = 2; - } - - if ( EdgeEndStar$$1 ) DirectedEdgeStar.__proto__ = EdgeEndStar$$1; - DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype ); - DirectedEdgeStar.prototype.constructor = DirectedEdgeStar; - DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () { - var this$1 = this; - - this.getResultAreaEdges(); - var firstOut = null; - var incoming = null; - var state = this._SCANNING_FOR_INCOMING; - for (var i = 0; i < this._resultAreaEdgeList.size(); i++) { - var nextOut = this$1._resultAreaEdgeList.get(i); - var nextIn = nextOut.getSym(); - if (!nextOut.getLabel().isArea()) { continue } - if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; } - switch (state) { - case this$1._SCANNING_FOR_INCOMING: - if (!nextIn.isInResult()) { continue } - incoming = nextIn; - state = this$1._LINKING_TO_OUTGOING; - break - case this$1._LINKING_TO_OUTGOING: - if (!nextOut.isInResult()) { continue } - incoming.setNext(nextOut); - state = this$1._SCANNING_FOR_INCOMING; - break - } - } - if (state === this._LINKING_TO_OUTGOING) { - if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) } - Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge'); - incoming.setNext(firstOut); - } - }; - DirectedEdgeStar.prototype.insert = function insert (ee) { - var de = ee; - this.insertEdgeEnd(de, de); - }; - DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () { - var edges = this.getEdges(); - var size = edges.size(); - if (size < 1) { return null } - var de0 = edges.get(0); - if (size === 1) { return de0 } - var deLast = edges.get(size - 1); - var quad0 = de0.getQuadrant(); - var quad1 = deLast.getQuadrant(); - if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else { - // const nonHorizontalEdge = null - if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast } - } - Assert.shouldNeverReachHere('found two horizontal edges incident on node'); - return null - }; - DirectedEdgeStar.prototype.print = function print (out) { - System.out.println('DirectedEdgeStar: ' + this.getCoordinate()); - for (var it = this.iterator(); it.hasNext();) { - var de = it.next(); - out.print('out '); - de.print(out); - out.println(); - out.print('in '); - de.getSym().print(out); - out.println(); - } - }; - DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () { - var this$1 = this; - - if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList } - this._resultAreaEdgeList = new ArrayList(); - for (var it = this.iterator(); it.hasNext();) { - var de = it.next(); - if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); } - } - return this._resultAreaEdgeList - }; - DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) { - for (var it = this.iterator(); it.hasNext();) { - var de = it.next(); - var label = de.getLabel(); - label.setAllLocationsIfNull(0, nodeLabel.getLocation(0)); - label.setAllLocationsIfNull(1, nodeLabel.getLocation(1)); - } - }; - DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () { - var this$1 = this; - - this.getEdges(); - var prevOut = null; - var firstIn = null; - for (var i = this._edgeList.size() - 1; i >= 0; i--) { - var nextOut = this$1._edgeList.get(i); - var nextIn = nextOut.getSym(); - if (firstIn === null) { firstIn = nextIn; } - if (prevOut !== null) { nextIn.setNext(prevOut); } - prevOut = nextOut; - } - firstIn.setNext(prevOut); - }; - DirectedEdgeStar.prototype.computeDepths = function computeDepths () { - var this$1 = this; - - if (arguments.length === 1) { - var de = arguments[0]; - var edgeIndex = this.findIndex(de); - // const label = de.getLabel() - var startDepth = de.getDepth(Position.LEFT); - var targetLastDepth = de.getDepth(Position.RIGHT); - var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth); - var lastDepth = this.computeDepths(0, edgeIndex, nextDepth); - if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) } - } else if (arguments.length === 3) { - var startIndex = arguments[0]; - var endIndex = arguments[1]; - var startDepth$1 = arguments[2]; - var currDepth = startDepth$1; - for (var i = startIndex; i < endIndex; i++) { - var nextDe = this$1._edgeList.get(i); - // const label = nextDe.getLabel() - nextDe.setEdgeDepths(Position.RIGHT, currDepth); - currDepth = nextDe.getDepth(Position.LEFT); - } - return currDepth - } - }; - DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () { - for (var it = this.iterator(); it.hasNext();) { - var de = it.next(); - var label = de.getLabel(); - label.merge(de.getSym().getLabel()); - } - }; - DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) { - var this$1 = this; - - var firstOut = null; - var incoming = null; - var state = this._SCANNING_FOR_INCOMING; - for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) { - var nextOut = this$1._resultAreaEdgeList.get(i); - var nextIn = nextOut.getSym(); - if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; } - switch (state) { - case this$1._SCANNING_FOR_INCOMING: - if (nextIn.getEdgeRing() !== er) { continue } - incoming = nextIn; - state = this$1._LINKING_TO_OUTGOING; - break - case this$1._LINKING_TO_OUTGOING: - if (nextOut.getEdgeRing() !== er) { continue } - incoming.setNextMin(nextOut); - state = this$1._SCANNING_FOR_INCOMING; - break - } - } - if (state === this._LINKING_TO_OUTGOING) { - Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge'); - Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge'); - incoming.setNextMin(firstOut); - } - }; - DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () { - if (arguments.length === 0) { - var degree = 0; - for (var it = this.iterator(); it.hasNext();) { - var de = it.next(); - if (de.isInResult()) { degree++; } - } - return degree - } else if (arguments.length === 1) { - var er = arguments[0]; - var degree$1 = 0; - for (var it$1 = this.iterator(); it$1.hasNext();) { - var de$1 = it$1.next(); - if (de$1.getEdgeRing() === er) { degree$1++; } - } - return degree$1 - } - }; - DirectedEdgeStar.prototype.getLabel = function getLabel () { - return this._label - }; - DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () { - var startLoc = Location.NONE; - for (var it = this.iterator(); it.hasNext();) { - var nextOut = it.next(); - var nextIn = nextOut.getSym(); - if (!nextOut.isLineEdge()) { - if (nextOut.isInResult()) { - startLoc = Location.INTERIOR; - break - } - if (nextIn.isInResult()) { - startLoc = Location.EXTERIOR; - break - } - } - } - if (startLoc === Location.NONE) { return null } - var currLoc = startLoc; - for (var it$1 = this.iterator(); it$1.hasNext();) { - var nextOut$1 = it$1.next(); - var nextIn$1 = nextOut$1.getSym(); - if (nextOut$1.isLineEdge()) { - nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR); - } else { - if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; } - if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; } - } - } - }; - DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) { - var this$1 = this; - - EdgeEndStar$$1.prototype.computeLabelling.call(this, geom); - this._label = new Label(Location.NONE); - for (var it = this.iterator(); it.hasNext();) { - var ee = it.next(); - var e = ee.getEdge(); - var eLabel = e.getLabel(); - for (var i = 0; i < 2; i++) { - var eLoc = eLabel.getLocation(i); - if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); } - } - } - }; - DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DirectedEdgeStar.prototype.getClass = function getClass () { - return DirectedEdgeStar - }; - - return DirectedEdgeStar; - }(EdgeEndStar)); - - var OverlayNodeFactory = (function (NodeFactory$$1) { - function OverlayNodeFactory () { - NodeFactory$$1.apply(this, arguments); - } - - if ( NodeFactory$$1 ) OverlayNodeFactory.__proto__ = NodeFactory$$1; - OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype ); - OverlayNodeFactory.prototype.constructor = OverlayNodeFactory; - - OverlayNodeFactory.prototype.createNode = function createNode (coord) { - return new Node$1(coord, new DirectedEdgeStar()) - }; - OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OverlayNodeFactory.prototype.getClass = function getClass () { - return OverlayNodeFactory - }; - - return OverlayNodeFactory; - }(NodeFactory)); - - var OrientedCoordinateArray = function OrientedCoordinateArray () { - this._pts = null; - this._orientation = null; - var pts = arguments[0]; - this._pts = pts; - this._orientation = OrientedCoordinateArray.orientation(pts); - }; - OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) { - var oca = o1; - var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation); - return comp - }; - OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - OrientedCoordinateArray.prototype.getClass = function getClass () { - return OrientedCoordinateArray - }; - OrientedCoordinateArray.orientation = function orientation (pts) { - return CoordinateArrays.increasingDirection(pts) === 1 - }; - OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) { - var dir1 = orientation1 ? 1 : -1; - var dir2 = orientation2 ? 1 : -1; - var limit1 = orientation1 ? pts1.length : -1; - var limit2 = orientation2 ? pts2.length : -1; - var i1 = orientation1 ? 0 : pts1.length - 1; - var i2 = orientation2 ? 0 : pts2.length - 1; - // const comp = 0 - while (true) { - var compPt = pts1[i1].compareTo(pts2[i2]); - if (compPt !== 0) { return compPt } - i1 += dir1; - i2 += dir2; - var done1 = i1 === limit1; - var done2 = i2 === limit2; - if (done1 && !done2) { return -1 } - if (!done1 && done2) { return 1 } - if (done1 && done2) { return 0 } - } - }; - - var EdgeList = function EdgeList () { - this._edges = new ArrayList(); - this._ocaMap = new TreeMap(); - }; - EdgeList.prototype.print = function print (out) { - var this$1 = this; - - out.print('MULTILINESTRING ( '); - for (var j = 0; j < this._edges.size(); j++) { - var e = this$1._edges.get(j); - if (j > 0) { out.print(','); } - out.print('('); - var pts = e.getCoordinates(); - for (var i = 0; i < pts.length; i++) { - if (i > 0) { out.print(','); } - out.print(pts[i].x + ' ' + pts[i].y); - } - out.println(')'); - } - out.print(') '); - }; - EdgeList.prototype.addAll = function addAll (edgeColl) { - var this$1 = this; - - for (var i = edgeColl.iterator(); i.hasNext();) { - this$1.add(i.next()); - } - }; - EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) { - var this$1 = this; - - for (var i = 0; i < this._edges.size(); i++) { - if (this$1._edges.get(i).equals(e)) { return i } - } - return -1 - }; - EdgeList.prototype.iterator = function iterator () { - return this._edges.iterator() - }; - EdgeList.prototype.getEdges = function getEdges () { - return this._edges - }; - EdgeList.prototype.get = function get (i) { - return this._edges.get(i) - }; - EdgeList.prototype.findEqualEdge = function findEqualEdge (e) { - var oca = new OrientedCoordinateArray(e.getCoordinates()); - var matchEdge = this._ocaMap.get(oca); - return matchEdge - }; - EdgeList.prototype.add = function add (e) { - this._edges.add(e); - var oca = new OrientedCoordinateArray(e.getCoordinates()); - this._ocaMap.put(oca, e); - }; - EdgeList.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeList.prototype.getClass = function getClass () { - return EdgeList - }; - - var SegmentIntersector = function SegmentIntersector () {}; - - SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {}; - SegmentIntersector.prototype.isDone = function isDone () {}; - SegmentIntersector.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentIntersector.prototype.getClass = function getClass () { - return SegmentIntersector - }; - - var IntersectionAdder = function IntersectionAdder () { - this._hasIntersection = false; - this._hasProper = false; - this._hasProperInterior = false; - this._hasInterior = false; - this._properIntersectionPoint = null; - this._li = null; - this._isSelfIntersection = null; - this.numIntersections = 0; - this.numInteriorIntersections = 0; - this.numProperIntersections = 0; - this.numTests = 0; - var li = arguments[0]; - this._li = li; - }; - IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) { - if (e0 === e1) { - if (this._li.getIntersectionNum() === 1) { - if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true } - if (e0.isClosed()) { - var maxSegIndex = e0.size() - 1; - if ((segIndex0 === 0 && segIndex1 === maxSegIndex) || - (segIndex1 === 0 && segIndex0 === maxSegIndex)) { - return true - } - } - } - } - return false - }; - IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () { - return this._properIntersectionPoint - }; - IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () { - return this._hasProperInterior - }; - IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () { - return this._li - }; - IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () { - return this._hasProper - }; - IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) { - if (e0 === e1 && segIndex0 === segIndex1) { return null } - this.numTests++; - var p00 = e0.getCoordinates()[segIndex0]; - var p01 = e0.getCoordinates()[segIndex0 + 1]; - var p10 = e1.getCoordinates()[segIndex1]; - var p11 = e1.getCoordinates()[segIndex1 + 1]; - this._li.computeIntersection(p00, p01, p10, p11); - if (this._li.hasIntersection()) { - this.numIntersections++; - if (this._li.isInteriorIntersection()) { - this.numInteriorIntersections++; - this._hasInterior = true; - } - if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) { - this._hasIntersection = true; - e0.addIntersections(this._li, segIndex0, 0); - e1.addIntersections(this._li, segIndex1, 1); - if (this._li.isProper()) { - this.numProperIntersections++; - this._hasProper = true; - this._hasProperInterior = true; - } - } - } - }; - IntersectionAdder.prototype.hasIntersection = function hasIntersection () { - return this._hasIntersection - }; - IntersectionAdder.prototype.isDone = function isDone () { - return false - }; - IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () { - return this._hasInterior - }; - IntersectionAdder.prototype.interfaces_ = function interfaces_ () { - return [SegmentIntersector] - }; - IntersectionAdder.prototype.getClass = function getClass () { - return IntersectionAdder - }; - IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) { - return Math.abs(i1 - i2) === 1 - }; - - var EdgeIntersection = function EdgeIntersection () { - this.coord = null; - this.segmentIndex = null; - this.dist = null; - var coord = arguments[0]; - var segmentIndex = arguments[1]; - var dist = arguments[2]; - this.coord = new Coordinate(coord); - this.segmentIndex = segmentIndex; - this.dist = dist; - }; - EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () { - return this.segmentIndex - }; - EdgeIntersection.prototype.getCoordinate = function getCoordinate () { - return this.coord - }; - EdgeIntersection.prototype.print = function print (out) { - out.print(this.coord); - out.print(' seg # = ' + this.segmentIndex); - out.println(' dist = ' + this.dist); - }; - EdgeIntersection.prototype.compareTo = function compareTo (obj) { - var other = obj; - return this.compare(other.segmentIndex, other.dist) - }; - EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) { - if (this.segmentIndex === 0 && this.dist === 0.0) { return true } - if (this.segmentIndex === maxSegmentIndex) { return true } - return false - }; - EdgeIntersection.prototype.toString = function toString () { - return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist - }; - EdgeIntersection.prototype.getDistance = function getDistance () { - return this.dist - }; - EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) { - if (this.segmentIndex < segmentIndex) { return -1 } - if (this.segmentIndex > segmentIndex) { return 1 } - if (this.dist < dist) { return -1 } - if (this.dist > dist) { return 1 } - return 0 - }; - EdgeIntersection.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - EdgeIntersection.prototype.getClass = function getClass () { - return EdgeIntersection - }; - - var EdgeIntersectionList = function EdgeIntersectionList () { - this._nodeMap = new TreeMap(); - this.edge = null; - var edge = arguments[0]; - this.edge = edge; - }; - EdgeIntersectionList.prototype.print = function print (out) { - out.println('Intersections:'); - for (var it = this.iterator(); it.hasNext();) { - var ei = it.next(); - ei.print(out); - } - }; - EdgeIntersectionList.prototype.iterator = function iterator () { - return this._nodeMap.values().iterator() - }; - EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) { - var this$1 = this; - - this.addEndpoints(); - var it = this.iterator(); - var eiPrev = it.next(); - while (it.hasNext()) { - var ei = it.next(); - var newEdge = this$1.createSplitEdge(eiPrev, ei); - edgeList.add(newEdge); - eiPrev = ei; - } - }; - EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () { - var maxSegIndex = this.edge.pts.length - 1; - this.add(this.edge.pts[0], 0, 0.0); - this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0); - }; - EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) { - var this$1 = this; - - var npts = ei1.segmentIndex - ei0.segmentIndex + 2; - var lastSegStartPt = this.edge.pts[ei1.segmentIndex]; - var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt); - if (!useIntPt1) { - npts--; - } - var pts = new Array(npts).fill(null); - var ipt = 0; - pts[ipt++] = new Coordinate(ei0.coord); - for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) { - pts[ipt++] = this$1.edge.pts[i]; - } - if (useIntPt1) { pts[ipt] = ei1.coord; } - return new Edge(pts, new Label(this.edge._label)) - }; - EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) { - var eiNew = new EdgeIntersection(intPt, segmentIndex, dist); - var ei = this._nodeMap.get(eiNew); - if (ei !== null) { - return ei - } - this._nodeMap.put(eiNew, eiNew); - return eiNew - }; - EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) { - for (var it = this.iterator(); it.hasNext();) { - var ei = it.next(); - if (ei.coord.equals(pt)) { return true } - } - return false - }; - EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeIntersectionList.prototype.getClass = function getClass () { - return EdgeIntersectionList - }; - - var MonotoneChainIndexer = function MonotoneChainIndexer () {}; - - MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) { - var this$1 = this; - - var start = 0; - var startIndexList = new ArrayList(); - startIndexList.add(new Integer(start)); - do { - var last = this$1.findChainEnd(pts, start); - startIndexList.add(new Integer(last)); - start = last; - } while (start < pts.length - 1) - var startIndex = MonotoneChainIndexer.toIntArray(startIndexList); - return startIndex - }; - MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) { - var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]); - var last = start + 1; - while (last < pts.length) { - var quad = Quadrant.quadrant(pts[last - 1], pts[last]); - if (quad !== chainQuad) { break } - last++; - } - return last - 1 - }; - MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChainIndexer.prototype.getClass = function getClass () { - return MonotoneChainIndexer - }; - MonotoneChainIndexer.toIntArray = function toIntArray (list) { - var array = new Array(list.size()).fill(null); - for (var i = 0; i < array.length; i++) { - array[i] = list.get(i).intValue(); - } - return array - }; - - var MonotoneChainEdge = function MonotoneChainEdge () { - this.e = null; - this.pts = null; - this.startIndex = null; - this.env1 = new Envelope(); - this.env2 = new Envelope(); - var e = arguments[0]; - this.e = e; - this.pts = e.getCoordinates(); - var mcb = new MonotoneChainIndexer(); - this.startIndex = mcb.getChainStartIndices(this.pts); - }; - MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () { - return this.pts - }; - MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) { - var x1 = this.pts[this.startIndex[chainIndex]].x; - var x2 = this.pts[this.startIndex[chainIndex + 1]].x; - return x1 > x2 ? x1 : x2 - }; - MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) { - var x1 = this.pts[this.startIndex[chainIndex]].x; - var x2 = this.pts[this.startIndex[chainIndex + 1]].x; - return x1 < x2 ? x1 : x2 - }; - MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () { - if (arguments.length === 4) { - var chainIndex0 = arguments[0]; - var mce = arguments[1]; - var chainIndex1 = arguments[2]; - var si = arguments[3]; - this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si); - } else if (arguments.length === 6) { - var start0 = arguments[0]; - var end0 = arguments[1]; - var mce$1 = arguments[2]; - var start1 = arguments[3]; - var end1 = arguments[4]; - var ei = arguments[5]; - var p00 = this.pts[start0]; - var p01 = this.pts[end0]; - var p10 = mce$1.pts[start1]; - var p11 = mce$1.pts[end1]; - if (end0 - start0 === 1 && end1 - start1 === 1) { - ei.addIntersections(this.e, start0, mce$1.e, start1); - return null - } - this.env1.init(p00, p01); - this.env2.init(p10, p11); - if (!this.env1.intersects(this.env2)) { return null } - var mid0 = Math.trunc((start0 + end0) / 2); - var mid1 = Math.trunc((start1 + end1) / 2); - if (start0 < mid0) { - if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); } - if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); } - } - if (mid0 < end0) { - if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); } - if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); } - } - } - }; - MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () { - return this.startIndex - }; - MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) { - var this$1 = this; - - for (var i = 0; i < this.startIndex.length - 1; i++) { - for (var j = 0; j < mce.startIndex.length - 1; j++) { - this$1.computeIntersectsForChain(i, mce, j, si); - } - } - }; - MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChainEdge.prototype.getClass = function getClass () { - return MonotoneChainEdge - }; - - var Depth = function Depth () { - var this$1 = this; - - this._depth = Array(2).fill().map(function () { return Array(3); }); - for (var i = 0; i < 2; i++) { - for (var j = 0; j < 3; j++) { - this$1._depth[i][j] = Depth.NULL_VALUE; - } - } - }; - - var staticAccessors$31 = { NULL_VALUE: { configurable: true } }; - Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) { - return this._depth[geomIndex][posIndex] - }; - Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) { - this._depth[geomIndex][posIndex] = depthValue; - }; - Depth.prototype.isNull = function isNull () { - var this$1 = this; - - if (arguments.length === 0) { - for (var i = 0; i < 2; i++) { - for (var j = 0; j < 3; j++) { - if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false } - } - } - return true - } else if (arguments.length === 1) { - var geomIndex = arguments[0]; - return this._depth[geomIndex][1] === Depth.NULL_VALUE - } else if (arguments.length === 2) { - var geomIndex$1 = arguments[0]; - var posIndex = arguments[1]; - return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE - } - }; - Depth.prototype.normalize = function normalize () { - var this$1 = this; - - for (var i = 0; i < 2; i++) { - if (!this$1.isNull(i)) { - var minDepth = this$1._depth[i][1]; - if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; } - if (minDepth < 0) { minDepth = 0; } - for (var j = 1; j < 3; j++) { - var newValue = 0; - if (this$1._depth[i][j] > minDepth) { newValue = 1; } - this$1._depth[i][j] = newValue; - } - } - } - }; - Depth.prototype.getDelta = function getDelta (geomIndex) { - return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT] - }; - Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) { - if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR } - return Location.INTERIOR - }; - Depth.prototype.toString = function toString () { - return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2] - }; - Depth.prototype.add = function add () { - var this$1 = this; - - if (arguments.length === 1) { - var lbl = arguments[0]; - for (var i = 0; i < 2; i++) { - for (var j = 1; j < 3; j++) { - var loc = lbl.getLocation(i, j); - if (loc === Location.EXTERIOR || loc === Location.INTERIOR) { - if (this$1.isNull(i, j)) { - this$1._depth[i][j] = Depth.depthAtLocation(loc); - } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); } - } - } - } - } else if (arguments.length === 3) { - var geomIndex = arguments[0]; - var posIndex = arguments[1]; - var location = arguments[2]; - if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; } - } - }; - Depth.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Depth.prototype.getClass = function getClass () { - return Depth - }; - Depth.depthAtLocation = function depthAtLocation (location) { - if (location === Location.EXTERIOR) { return 0 } - if (location === Location.INTERIOR) { return 1 } - return Depth.NULL_VALUE - }; - staticAccessors$31.NULL_VALUE.get = function () { return -1 }; - - Object.defineProperties( Depth, staticAccessors$31 ); - - var Edge = (function (GraphComponent$$1) { - function Edge () { - GraphComponent$$1.call(this); - this.pts = null; - this._env = null; - this.eiList = new EdgeIntersectionList(this); - this._name = null; - this._mce = null; - this._isIsolated = true; - this._depth = new Depth(); - this._depthDelta = 0; - if (arguments.length === 1) { - var pts = arguments[0]; - Edge.call(this, pts, null); - } else if (arguments.length === 2) { - var pts$1 = arguments[0]; - var label = arguments[1]; - this.pts = pts$1; - this._label = label; - } - } - - if ( GraphComponent$$1 ) Edge.__proto__ = GraphComponent$$1; - Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype ); - Edge.prototype.constructor = Edge; - Edge.prototype.getDepth = function getDepth () { - return this._depth - }; - Edge.prototype.getCollapsedEdge = function getCollapsedEdge () { - var newPts = new Array(2).fill(null); - newPts[0] = this.pts[0]; - newPts[1] = this.pts[1]; - var newe = new Edge(newPts, Label.toLineLabel(this._label)); - return newe - }; - Edge.prototype.isIsolated = function isIsolated () { - return this._isIsolated - }; - Edge.prototype.getCoordinates = function getCoordinates () { - return this.pts - }; - Edge.prototype.setIsolated = function setIsolated (isIsolated) { - this._isIsolated = isIsolated; - }; - Edge.prototype.setName = function setName (name) { - this._name = name; - }; - Edge.prototype.equals = function equals (o) { - var this$1 = this; - - if (!(o instanceof Edge)) { return false } - var e = o; - if (this.pts.length !== e.pts.length) { return false } - var isEqualForward = true; - var isEqualReverse = true; - var iRev = this.pts.length; - for (var i = 0; i < this.pts.length; i++) { - if (!this$1.pts[i].equals2D(e.pts[i])) { - isEqualForward = false; - } - if (!this$1.pts[i].equals2D(e.pts[--iRev])) { - isEqualReverse = false; - } - if (!isEqualForward && !isEqualReverse) { return false } - } - return true - }; - Edge.prototype.getCoordinate = function getCoordinate () { - if (arguments.length === 0) { - if (this.pts.length > 0) { return this.pts[0] } - return null - } else if (arguments.length === 1) { - var i = arguments[0]; - return this.pts[i] - } - }; - Edge.prototype.print = function print (out) { - var this$1 = this; - - out.print('edge ' + this._name + ': '); - out.print('LINESTRING ('); - for (var i = 0; i < this.pts.length; i++) { - if (i > 0) { out.print(','); } - out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y); - } - out.print(') ' + this._label + ' ' + this._depthDelta); - }; - Edge.prototype.computeIM = function computeIM (im) { - Edge.updateIM(this._label, im); - }; - Edge.prototype.isCollapsed = function isCollapsed () { - if (!this._label.isArea()) { return false } - if (this.pts.length !== 3) { return false } - if (this.pts[0].equals(this.pts[2])) { return true } - return false - }; - Edge.prototype.isClosed = function isClosed () { - return this.pts[0].equals(this.pts[this.pts.length - 1]) - }; - Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () { - return this.pts.length - 1 - }; - Edge.prototype.getDepthDelta = function getDepthDelta () { - return this._depthDelta - }; - Edge.prototype.getNumPoints = function getNumPoints () { - return this.pts.length - }; - Edge.prototype.printReverse = function printReverse (out) { - var this$1 = this; - - out.print('edge ' + this._name + ': '); - for (var i = this.pts.length - 1; i >= 0; i--) { - out.print(this$1.pts[i] + ' '); - } - out.println(''); - }; - Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () { - if (this._mce === null) { this._mce = new MonotoneChainEdge(this); } - return this._mce - }; - Edge.prototype.getEnvelope = function getEnvelope () { - var this$1 = this; - - if (this._env === null) { - this._env = new Envelope(); - for (var i = 0; i < this.pts.length; i++) { - this$1._env.expandToInclude(this$1.pts[i]); - } - } - return this._env - }; - Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) { - var intPt = new Coordinate(li.getIntersection(intIndex)); - var normalizedSegmentIndex = segmentIndex; - var dist = li.getEdgeDistance(geomIndex, intIndex); - var nextSegIndex = normalizedSegmentIndex + 1; - if (nextSegIndex < this.pts.length) { - var nextPt = this.pts[nextSegIndex]; - if (intPt.equals2D(nextPt)) { - normalizedSegmentIndex = nextSegIndex; - dist = 0.0; - } - } - this.eiList.add(intPt, normalizedSegmentIndex, dist); - }; - Edge.prototype.toString = function toString () { - var this$1 = this; - - var buf = new StringBuffer(); - buf.append('edge ' + this._name + ': '); - buf.append('LINESTRING ('); - for (var i = 0; i < this.pts.length; i++) { - if (i > 0) { buf.append(','); } - buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y); - } - buf.append(') ' + this._label + ' ' + this._depthDelta); - return buf.toString() - }; - Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) { - var this$1 = this; - - if (this.pts.length !== e.pts.length) { return false } - for (var i = 0; i < this.pts.length; i++) { - if (!this$1.pts[i].equals2D(e.pts[i])) { - return false - } - } - return true - }; - Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) { - this._depthDelta = depthDelta; - }; - Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () { - return this.eiList - }; - Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) { - var this$1 = this; - - for (var i = 0; i < li.getIntersectionNum(); i++) { - this$1.addIntersection(li, segmentIndex, geomIndex, i); - } - }; - Edge.prototype.interfaces_ = function interfaces_ () { - return [] - }; - Edge.prototype.getClass = function getClass () { - return Edge - }; - Edge.updateIM = function updateIM () { - if (arguments.length === 2) { - var label = arguments[0]; - var im = arguments[1]; - im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1); - if (label.isArea()) { - im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2); - im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2); - } - } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) } - }; - - return Edge; - }(GraphComponent)); - - var BufferBuilder = function BufferBuilder (bufParams) { - this._workingPrecisionModel = null; - this._workingNoder = null; - this._geomFact = null; - this._graph = null; - this._edgeList = new EdgeList(); - this._bufParams = bufParams || null; - }; - BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) { - this._workingPrecisionModel = pm; - }; - BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) { - var existingEdge = this._edgeList.findEqualEdge(e); - if (existingEdge !== null) { - var existingLabel = existingEdge.getLabel(); - var labelToMerge = e.getLabel(); - if (!existingEdge.isPointwiseEqual(e)) { - labelToMerge = new Label(e.getLabel()); - labelToMerge.flip(); - } - existingLabel.merge(labelToMerge); - var mergeDelta = BufferBuilder.depthDelta(labelToMerge); - var existingDelta = existingEdge.getDepthDelta(); - var newDelta = existingDelta + mergeDelta; - existingEdge.setDepthDelta(newDelta); - } else { - this._edgeList.add(e); - e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel())); - } - }; - BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) { - var processedGraphs = new ArrayList(); - for (var i = subgraphList.iterator(); i.hasNext();) { - var subgraph = i.next(); - var p = subgraph.getRightmostCoordinate(); - var locater = new SubgraphDepthLocater(processedGraphs); - var outsideDepth = locater.getDepth(p); - subgraph.computeDepth(outsideDepth); - subgraph.findResultEdges(); - processedGraphs.add(subgraph); - polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes()); - } - }; - BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) { - var subgraphList = new ArrayList(); - for (var i = graph.getNodes().iterator(); i.hasNext();) { - var node = i.next(); - if (!node.isVisited()) { - var subgraph = new BufferSubgraph(); - subgraph.create(node); - subgraphList.add(subgraph); - } - } - Collections.sort(subgraphList, Collections.reverseOrder()); - return subgraphList - }; - BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () { - var emptyGeom = this._geomFact.createPolygon(); - return emptyGeom - }; - BufferBuilder.prototype.getNoder = function getNoder (precisionModel) { - if (this._workingNoder !== null) { return this._workingNoder } - var noder = new MCIndexNoder(); - var li = new RobustLineIntersector(); - li.setPrecisionModel(precisionModel); - noder.setSegmentIntersector(new IntersectionAdder(li)); - return noder - }; - BufferBuilder.prototype.buffer = function buffer (g, distance) { - var precisionModel = this._workingPrecisionModel; - if (precisionModel === null) { precisionModel = g.getPrecisionModel(); } - this._geomFact = g.getFactory(); - var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams); - var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder); - var bufferSegStrList = curveSetBuilder.getCurves(); - if (bufferSegStrList.size() <= 0) { - return this.createEmptyResultGeometry() - } - this.computeNodedEdges(bufferSegStrList, precisionModel); - this._graph = new PlanarGraph(new OverlayNodeFactory()); - this._graph.addEdges(this._edgeList.getEdges()); - var subgraphList = this.createSubgraphs(this._graph); - var polyBuilder = new PolygonBuilder(this._geomFact); - this.buildSubgraphs(subgraphList, polyBuilder); - var resultPolyList = polyBuilder.getPolygons(); - if (resultPolyList.size() <= 0) { - return this.createEmptyResultGeometry() - } - var resultGeom = this._geomFact.buildGeometry(resultPolyList); - return resultGeom - }; - BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) { - var this$1 = this; - - var noder = this.getNoder(precisionModel); - noder.computeNodes(bufferSegStrList); - var nodedSegStrings = noder.getNodedSubstrings(); - for (var i = nodedSegStrings.iterator(); i.hasNext();) { - var segStr = i.next(); - var pts = segStr.getCoordinates(); - if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue } - var oldLabel = segStr.getData(); - var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel)); - this$1.insertUniqueEdge(edge); - } - }; - BufferBuilder.prototype.setNoder = function setNoder (noder) { - this._workingNoder = noder; - }; - BufferBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferBuilder.prototype.getClass = function getClass () { - return BufferBuilder - }; - BufferBuilder.depthDelta = function depthDelta (label) { - var lLoc = label.getLocation(0, Position.LEFT); - var rLoc = label.getLocation(0, Position.RIGHT); - if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 } - return 0 - }; - BufferBuilder.convertSegStrings = function convertSegStrings (it) { - var fact = new GeometryFactory(); - var lines = new ArrayList(); - while (it.hasNext()) { - var ss = it.next(); - var line = fact.createLineString(ss.getCoordinates()); - lines.add(line); - } - return fact.buildGeometry(lines) - }; - - var ScaledNoder = function ScaledNoder () { - this._noder = null; - this._scaleFactor = null; - this._offsetX = null; - this._offsetY = null; - this._isScaled = false; - if (arguments.length === 2) { - var noder = arguments[0]; - var scaleFactor = arguments[1]; - this._noder = noder; - this._scaleFactor = scaleFactor; - this._offsetX = 0.0; - this._offsetY = 0.0; - this._isScaled = !this.isIntegerPrecision(); - } else if (arguments.length === 4) { - var noder$1 = arguments[0]; - var scaleFactor$1 = arguments[1]; - var offsetX = arguments[2]; - var offsetY = arguments[3]; - this._noder = noder$1; - this._scaleFactor = scaleFactor$1; - this._offsetX = offsetX; - this._offsetY = offsetY; - this._isScaled = !this.isIntegerPrecision(); - } - }; - ScaledNoder.prototype.rescale = function rescale () { - var this$1 = this; - - if (hasInterface(arguments[0], Collection)) { - var segStrings = arguments[0]; - for (var i = segStrings.iterator(); i.hasNext();) { - var ss = i.next(); - this$1.rescale(ss.getCoordinates()); - } - } else if (arguments[0] instanceof Array) { - var pts = arguments[0]; - // let p0 = null - // let p1 = null - // if (pts.length === 2) { - // p0 = new Coordinate(pts[0]) - // p1 = new Coordinate(pts[1]) - // } - for (var i$1 = 0; i$1 < pts.length; i$1++) { - pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX; - pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY; - } - if (pts.length === 2 && pts[0].equals2D(pts[1])) { - System.out.println(pts); - } - } - }; - ScaledNoder.prototype.scale = function scale () { - var this$1 = this; - - if (hasInterface(arguments[0], Collection)) { - var segStrings = arguments[0]; - var nodedSegmentStrings = new ArrayList(); - for (var i = segStrings.iterator(); i.hasNext();) { - var ss = i.next(); - nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData())); - } - return nodedSegmentStrings - } else if (arguments[0] instanceof Array) { - var pts = arguments[0]; - var roundPts = new Array(pts.length).fill(null); - for (var i$1 = 0; i$1 < pts.length; i$1++) { - roundPts[i$1] = new Coordinate(Math.round((pts[i$1].x - this$1._offsetX) * this$1._scaleFactor), Math.round((pts[i$1].y - this$1._offsetY) * this$1._scaleFactor), pts[i$1].z); - } - var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts); - return roundPtsNoDup - } - }; - ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () { - return this._scaleFactor === 1.0 - }; - ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () { - var splitSS = this._noder.getNodedSubstrings(); - if (this._isScaled) { this.rescale(splitSS); } - return splitSS - }; - ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) { - var intSegStrings = inputSegStrings; - if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); } - this._noder.computeNodes(intSegStrings); - }; - ScaledNoder.prototype.interfaces_ = function interfaces_ () { - return [Noder] - }; - ScaledNoder.prototype.getClass = function getClass () { - return ScaledNoder - }; - - var NodingValidator = function NodingValidator () { - this._li = new RobustLineIntersector(); - this._segStrings = null; - var segStrings = arguments[0]; - this._segStrings = segStrings; - }; - - var staticAccessors$33 = { fact: { configurable: true } }; - NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () { - var this$1 = this; - - if (arguments.length === 0) { - for (var i = this._segStrings.iterator(); i.hasNext();) { - var ss = i.next(); - var pts = ss.getCoordinates(); - this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings); - this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings); - } - } else if (arguments.length === 2) { - var testPt = arguments[0]; - var segStrings = arguments[1]; - for (var i$1 = segStrings.iterator(); i$1.hasNext();) { - var ss$1 = i$1.next(); - var pts$1 = ss$1.getCoordinates(); - for (var j = 1; j < pts$1.length - 1; j++) { - if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) } - } - } - } - }; - NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () { - var this$1 = this; - - if (arguments.length === 0) { - for (var i = this._segStrings.iterator(); i.hasNext();) { - var ss0 = i.next(); - for (var j = this._segStrings.iterator(); j.hasNext();) { - var ss1 = j.next(); - this$1.checkInteriorIntersections(ss0, ss1); - } - } - } else if (arguments.length === 2) { - var ss0$1 = arguments[0]; - var ss1$1 = arguments[1]; - var pts0 = ss0$1.getCoordinates(); - var pts1 = ss1$1.getCoordinates(); - for (var i0 = 0; i0 < pts0.length - 1; i0++) { - for (var i1 = 0; i1 < pts1.length - 1; i1++) { - this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1); - } - } - } else if (arguments.length === 4) { - var e0 = arguments[0]; - var segIndex0 = arguments[1]; - var e1 = arguments[2]; - var segIndex1 = arguments[3]; - if (e0 === e1 && segIndex0 === segIndex1) { return null } - var p00 = e0.getCoordinates()[segIndex0]; - var p01 = e0.getCoordinates()[segIndex0 + 1]; - var p10 = e1.getCoordinates()[segIndex1]; - var p11 = e1.getCoordinates()[segIndex1 + 1]; - this._li.computeIntersection(p00, p01, p10, p11); - if (this._li.hasIntersection()) { - if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) { - throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11) - } - } - } - }; - NodingValidator.prototype.checkValid = function checkValid () { - this.checkEndPtVertexIntersections(); - this.checkInteriorIntersections(); - this.checkCollapses(); - }; - NodingValidator.prototype.checkCollapses = function checkCollapses () { - var this$1 = this; - - if (arguments.length === 0) { - for (var i = this._segStrings.iterator(); i.hasNext();) { - var ss = i.next(); - this$1.checkCollapses(ss); - } - } else if (arguments.length === 1) { - var ss$1 = arguments[0]; - var pts = ss$1.getCoordinates(); - for (var i$1 = 0; i$1 < pts.length - 2; i$1++) { - this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]); - } - } - }; - NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) { - for (var i = 0; i < li.getIntersectionNum(); i++) { - var intPt = li.getIntersection(i); - if (!(intPt.equals(p0) || intPt.equals(p1))) { return true } - } - return false - }; - NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) { - if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) } - }; - NodingValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - NodingValidator.prototype.getClass = function getClass () { - return NodingValidator - }; - staticAccessors$33.fact.get = function () { return new GeometryFactory() }; - - Object.defineProperties( NodingValidator, staticAccessors$33 ); - - var HotPixel = function HotPixel () { - this._li = null; - this._pt = null; - this._originalPt = null; - this._ptScaled = null; - this._p0Scaled = null; - this._p1Scaled = null; - this._scaleFactor = null; - this._minx = null; - this._maxx = null; - this._miny = null; - this._maxy = null; - this._corner = new Array(4).fill(null); - this._safeEnv = null; - var pt = arguments[0]; - var scaleFactor = arguments[1]; - var li = arguments[2]; - this._originalPt = pt; - this._pt = pt; - this._scaleFactor = scaleFactor; - this._li = li; - if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') } - if (scaleFactor !== 1.0) { - this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y)); - this._p0Scaled = new Coordinate(); - this._p1Scaled = new Coordinate(); - } - this.initCorners(this._pt); - }; - - var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } }; - HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) { - var segMinx = Math.min(p0.x, p1.x); - var segMaxx = Math.max(p0.x, p1.x); - var segMiny = Math.min(p0.y, p1.y); - var segMaxy = Math.max(p0.y, p1.y); - var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy; - if (isOutsidePixelEnv) { return false } - var intersects = this.intersectsToleranceSquare(p0, p1); - Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test'); - return intersects - }; - HotPixel.prototype.initCorners = function initCorners (pt) { - var tolerance = 0.5; - this._minx = pt.x - tolerance; - this._maxx = pt.x + tolerance; - this._miny = pt.y - tolerance; - this._maxy = pt.y + tolerance; - this._corner[0] = new Coordinate(this._maxx, this._maxy); - this._corner[1] = new Coordinate(this._minx, this._maxy); - this._corner[2] = new Coordinate(this._minx, this._miny); - this._corner[3] = new Coordinate(this._maxx, this._miny); - }; - HotPixel.prototype.intersects = function intersects (p0, p1) { - if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) } - this.copyScaled(p0, this._p0Scaled); - this.copyScaled(p1, this._p1Scaled); - return this.intersectsScaled(this._p0Scaled, this._p1Scaled) - }; - HotPixel.prototype.scale = function scale (val) { - return Math.round(val * this._scaleFactor) - }; - HotPixel.prototype.getCoordinate = function getCoordinate () { - return this._originalPt - }; - HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) { - pScaled.x = this.scale(p.x); - pScaled.y = this.scale(p.y); - }; - HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () { - if (this._safeEnv === null) { - var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor; - this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance); - } - return this._safeEnv - }; - HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) { - this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]); - if (this._li.hasIntersection()) { return true } - this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]); - if (this._li.hasIntersection()) { return true } - this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]); - if (this._li.hasIntersection()) { return true } - this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]); - if (this._li.hasIntersection()) { return true } - return false - }; - HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) { - var intersectsLeft = false; - var intersectsBottom = false; - this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]); - if (this._li.isProper()) { return true } - this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]); - if (this._li.isProper()) { return true } - if (this._li.hasIntersection()) { intersectsLeft = true; } - this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]); - if (this._li.isProper()) { return true } - if (this._li.hasIntersection()) { intersectsBottom = true; } - this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]); - if (this._li.isProper()) { return true } - if (intersectsLeft && intersectsBottom) { return true } - if (p0.equals(this._pt)) { return true } - if (p1.equals(this._pt)) { return true } - return false - }; - HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) { - var p0 = segStr.getCoordinate(segIndex); - var p1 = segStr.getCoordinate(segIndex + 1); - if (this.intersects(p0, p1)) { - segStr.addIntersection(this.getCoordinate(), segIndex); - return true - } - return false - }; - HotPixel.prototype.interfaces_ = function interfaces_ () { - return [] - }; - HotPixel.prototype.getClass = function getClass () { - return HotPixel - }; - staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 }; - - Object.defineProperties( HotPixel, staticAccessors$34 ); - - var MonotoneChainSelectAction = function MonotoneChainSelectAction () { - this.tempEnv1 = new Envelope(); - this.selectedSegment = new LineSegment(); - }; - MonotoneChainSelectAction.prototype.select = function select () { - if (arguments.length === 1) ; else if (arguments.length === 2) { - var mc = arguments[0]; - var startIndex = arguments[1]; - mc.getLineSegment(startIndex, this.selectedSegment); - this.select(this.selectedSegment); - } - }; - MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChainSelectAction.prototype.getClass = function getClass () { - return MonotoneChainSelectAction - }; - - var MCIndexPointSnapper = function MCIndexPointSnapper () { - this._index = null; - var index = arguments[0]; - this._index = index; - }; - - var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } }; - MCIndexPointSnapper.prototype.snap = function snap () { - if (arguments.length === 1) { - var hotPixel = arguments[0]; - return this.snap(hotPixel, null, -1) - } else if (arguments.length === 3) { - var hotPixel$1 = arguments[0]; - var parentEdge = arguments[1]; - var hotPixelVertexIndex = arguments[2]; - var pixelEnv = hotPixel$1.getSafeEnvelope(); - var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex); - this._index.query(pixelEnv, { - interfaces_: function () { - return [ItemVisitor] - }, - visitItem: function (item) { - var testChain = item; - testChain.select(pixelEnv, hotPixelSnapAction); - } - }); - return hotPixelSnapAction.isNodeAdded() - } - }; - MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MCIndexPointSnapper.prototype.getClass = function getClass () { - return MCIndexPointSnapper - }; - staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction }; - - Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 ); - - var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) { - function HotPixelSnapAction () { - MonotoneChainSelectAction$$1.call(this); - this._hotPixel = null; - this._parentEdge = null; - this._hotPixelVertexIndex = null; - this._isNodeAdded = false; - var hotPixel = arguments[0]; - var parentEdge = arguments[1]; - var hotPixelVertexIndex = arguments[2]; - this._hotPixel = hotPixel; - this._parentEdge = parentEdge; - this._hotPixelVertexIndex = hotPixelVertexIndex; - } - - if ( MonotoneChainSelectAction$$1 ) HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1; - HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype ); - HotPixelSnapAction.prototype.constructor = HotPixelSnapAction; - HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () { - return this._isNodeAdded - }; - HotPixelSnapAction.prototype.select = function select () { - if (arguments.length === 2) { - var mc = arguments[0]; - var startIndex = arguments[1]; - var ss = mc.getContext(); - if (this._parentEdge !== null) { - if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null } - } - this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex); - } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) } - }; - HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () { - return [] - }; - HotPixelSnapAction.prototype.getClass = function getClass () { - return HotPixelSnapAction - }; - - return HotPixelSnapAction; - }(MonotoneChainSelectAction)); - - var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () { - this._li = null; - this._interiorIntersections = null; - var li = arguments[0]; - this._li = li; - this._interiorIntersections = new ArrayList(); - }; - InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) { - var this$1 = this; - - if (e0 === e1 && segIndex0 === segIndex1) { return null } - var p00 = e0.getCoordinates()[segIndex0]; - var p01 = e0.getCoordinates()[segIndex0 + 1]; - var p10 = e1.getCoordinates()[segIndex1]; - var p11 = e1.getCoordinates()[segIndex1 + 1]; - this._li.computeIntersection(p00, p01, p10, p11); - if (this._li.hasIntersection()) { - if (this._li.isInteriorIntersection()) { - for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) { - this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex)); - } - e0.addIntersections(this._li, segIndex0, 0); - e1.addIntersections(this._li, segIndex1, 1); - } - } - }; - InteriorIntersectionFinderAdder.prototype.isDone = function isDone () { - return false - }; - InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () { - return this._interiorIntersections - }; - InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () { - return [SegmentIntersector] - }; - InteriorIntersectionFinderAdder.prototype.getClass = function getClass () { - return InteriorIntersectionFinderAdder - }; - - var MCIndexSnapRounder = function MCIndexSnapRounder () { - this._pm = null; - this._li = null; - this._scaleFactor = null; - this._noder = null; - this._pointSnapper = null; - this._nodedSegStrings = null; - var pm = arguments[0]; - this._pm = pm; - this._li = new RobustLineIntersector(); - this._li.setPrecisionModel(pm); - this._scaleFactor = pm.getScale(); - }; - MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) { - var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings); - var nv = new NodingValidator(resultSegStrings); - try { - nv.checkValid(); - } catch (ex) { - if (ex instanceof Exception) { - ex.printStackTrace(); - } else { throw ex } - } finally {} - }; - MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () { - return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings) - }; - MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) { - var intersections = this.findInteriorIntersections(segStrings, li); - this.computeIntersectionSnaps(intersections); - this.computeVertexSnaps(segStrings); - }; - MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) { - var intFinderAdder = new InteriorIntersectionFinderAdder(li); - this._noder.setSegmentIntersector(intFinderAdder); - this._noder.computeNodes(segStrings); - return intFinderAdder.getInteriorIntersections() - }; - MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () { - var this$1 = this; - - if (hasInterface(arguments[0], Collection)) { - var edges = arguments[0]; - for (var i0 = edges.iterator(); i0.hasNext();) { - var edge0 = i0.next(); - this$1.computeVertexSnaps(edge0); - } - } else if (arguments[0] instanceof NodedSegmentString) { - var e = arguments[0]; - var pts0 = e.getCoordinates(); - for (var i = 0; i < pts0.length; i++) { - var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li); - var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i); - if (isNodeAdded) { - e.addIntersection(pts0[i], i); - } - } - } - }; - MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) { - this._nodedSegStrings = inputSegmentStrings; - this._noder = new MCIndexNoder(); - this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex()); - this.snapRound(inputSegmentStrings, this._li); - }; - MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) { - var this$1 = this; - - for (var it = snapPts.iterator(); it.hasNext();) { - var snapPt = it.next(); - var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li); - this$1._pointSnapper.snap(hotPixel); - } - }; - MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () { - return [Noder] - }; - MCIndexSnapRounder.prototype.getClass = function getClass () { - return MCIndexSnapRounder - }; - - var BufferOp = function BufferOp () { - this._argGeom = null; - this._distance = null; - this._bufParams = new BufferParameters(); - this._resultGeometry = null; - this._saveException = null; - if (arguments.length === 1) { - var g = arguments[0]; - this._argGeom = g; - } else if (arguments.length === 2) { - var g$1 = arguments[0]; - var bufParams = arguments[1]; - this._argGeom = g$1; - this._bufParams = bufParams; - } - }; - - var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } }; - BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) { - var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale()); - var bufBuilder = new BufferBuilder(this._bufParams); - bufBuilder.setWorkingPrecisionModel(fixedPM); - bufBuilder.setNoder(noder); - this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance); - }; - BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () { - var this$1 = this; - - if (arguments.length === 0) { - for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) { - try { - this$1.bufferReducedPrecision(precDigits); - } catch (ex) { - if (ex instanceof TopologyException) { - this$1._saveException = ex; - } else { throw ex } - } finally {} - if (this$1._resultGeometry !== null) { return null } - } - throw this._saveException - } else if (arguments.length === 1) { - var precisionDigits = arguments[0]; - var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits); - var fixedPM = new PrecisionModel(sizeBasedScaleFactor); - this.bufferFixedPrecision(fixedPM); - } - }; - BufferOp.prototype.computeGeometry = function computeGeometry () { - this.bufferOriginalPrecision(); - if (this._resultGeometry !== null) { return null } - var argPM = this._argGeom.getFactory().getPrecisionModel(); - if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); } - }; - BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) { - this._bufParams.setQuadrantSegments(quadrantSegments); - }; - BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () { - try { - var bufBuilder = new BufferBuilder(this._bufParams); - this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance); - } catch (ex) { - if (ex instanceof RuntimeException) { - this._saveException = ex; - } else { throw ex } - } finally {} - }; - BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) { - this._distance = distance; - this.computeGeometry(); - return this._resultGeometry - }; - BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) { - this._bufParams.setEndCapStyle(endCapStyle); - }; - BufferOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferOp.prototype.getClass = function getClass () { - return BufferOp - }; - BufferOp.bufferOp = function bufferOp () { - if (arguments.length === 2) { - var g = arguments[0]; - var distance = arguments[1]; - var gBuf = new BufferOp(g); - var geomBuf = gBuf.getResultGeometry(distance); - return geomBuf - } else if (arguments.length === 3) { - if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) { - var g$1 = arguments[0]; - var distance$1 = arguments[1]; - var quadrantSegments = arguments[2]; - var bufOp = new BufferOp(g$1); - bufOp.setQuadrantSegments(quadrantSegments); - var geomBuf$1 = bufOp.getResultGeometry(distance$1); - return geomBuf$1 - } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) { - var g$2 = arguments[0]; - var distance$2 = arguments[1]; - var params = arguments[2]; - var bufOp$1 = new BufferOp(g$2, params); - var geomBuf$2 = bufOp$1.getResultGeometry(distance$2); - return geomBuf$2 - } - } else if (arguments.length === 4) { - var g$3 = arguments[0]; - var distance$3 = arguments[1]; - var quadrantSegments$1 = arguments[2]; - var endCapStyle = arguments[3]; - var bufOp$2 = new BufferOp(g$3); - bufOp$2.setQuadrantSegments(quadrantSegments$1); - bufOp$2.setEndCapStyle(endCapStyle); - var geomBuf$3 = bufOp$2.getResultGeometry(distance$3); - return geomBuf$3 - } - }; - BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) { - var env = g.getEnvelopeInternal(); - var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY())); - var expandByDistance = distance > 0.0 ? distance : 0.0; - var bufEnvMax = envMax + 2 * expandByDistance; - var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0); - var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits; - var scaleFactor = Math.pow(10.0, minUnitLog10); - return scaleFactor - }; - staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND }; - staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT }; - staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT }; - staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE }; - staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 }; - - Object.defineProperties( BufferOp, staticAccessors$32 ); - - var PointPairDistance = function PointPairDistance () { - this._pt = [new Coordinate(), new Coordinate()]; - this._distance = Double.NaN; - this._isNull = true; - }; - PointPairDistance.prototype.getCoordinates = function getCoordinates () { - return this._pt - }; - PointPairDistance.prototype.getCoordinate = function getCoordinate (i) { - return this._pt[i] - }; - PointPairDistance.prototype.setMinimum = function setMinimum () { - if (arguments.length === 1) { - var ptDist = arguments[0]; - this.setMinimum(ptDist._pt[0], ptDist._pt[1]); - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - if (this._isNull) { - this.initialize(p0, p1); - return null - } - var dist = p0.distance(p1); - if (dist < this._distance) { this.initialize(p0, p1, dist); } - } - }; - PointPairDistance.prototype.initialize = function initialize () { - if (arguments.length === 0) { - this._isNull = true; - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - this._pt[0].setCoordinate(p0); - this._pt[1].setCoordinate(p1); - this._distance = p0.distance(p1); - this._isNull = false; - } else if (arguments.length === 3) { - var p0$1 = arguments[0]; - var p1$1 = arguments[1]; - var distance = arguments[2]; - this._pt[0].setCoordinate(p0$1); - this._pt[1].setCoordinate(p1$1); - this._distance = distance; - this._isNull = false; - } - }; - PointPairDistance.prototype.getDistance = function getDistance () { - return this._distance - }; - PointPairDistance.prototype.setMaximum = function setMaximum () { - if (arguments.length === 1) { - var ptDist = arguments[0]; - this.setMaximum(ptDist._pt[0], ptDist._pt[1]); - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - if (this._isNull) { - this.initialize(p0, p1); - return null - } - var dist = p0.distance(p1); - if (dist > this._distance) { this.initialize(p0, p1, dist); } - } - }; - PointPairDistance.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PointPairDistance.prototype.getClass = function getClass () { - return PointPairDistance - }; - - var DistanceToPointFinder = function DistanceToPointFinder () {}; - - DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DistanceToPointFinder.prototype.getClass = function getClass () { - return DistanceToPointFinder - }; - DistanceToPointFinder.computeDistance = function computeDistance () { - if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) { - var line = arguments[0]; - var pt = arguments[1]; - var ptDist = arguments[2]; - var coords = line.getCoordinates(); - var tempSegment = new LineSegment(); - for (var i = 0; i < coords.length - 1; i++) { - tempSegment.setCoordinates(coords[i], coords[i + 1]); - var closestPt = tempSegment.closestPoint(pt); - ptDist.setMinimum(closestPt, pt); - } - } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) { - var poly = arguments[0]; - var pt$1 = arguments[1]; - var ptDist$1 = arguments[2]; - DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1); - for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) { - DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1); - } - } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) { - var geom = arguments[0]; - var pt$2 = arguments[1]; - var ptDist$2 = arguments[2]; - if (geom instanceof LineString) { - DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2); - } else if (geom instanceof Polygon) { - DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2); - } else if (geom instanceof GeometryCollection) { - var gc = geom; - for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) { - var g = gc.getGeometryN(i$2); - DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2); - } - } else { - ptDist$2.setMinimum(geom.getCoordinate(), pt$2); - } - } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) { - var segment = arguments[0]; - var pt$3 = arguments[1]; - var ptDist$3 = arguments[2]; - var closestPt$1 = segment.closestPoint(pt$3); - ptDist$3.setMinimum(closestPt$1, pt$3); - } - }; - - var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) { - this._maxPtDist = new PointPairDistance(); - this._inputGeom = inputGeom || null; - }; - - var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } }; - BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) { - var distFilter = new MaxMidpointDistanceFilter(this._inputGeom); - curve.apply(distFilter); - this._maxPtDist.setMaximum(distFilter.getMaxPointDistance()); - }; - BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) { - var distFilter = new MaxPointDistanceFilter(this._inputGeom); - curve.apply(distFilter); - this._maxPtDist.setMaximum(distFilter.getMaxPointDistance()); - }; - BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) { - this.computeMaxVertexDistance(bufferCurve); - this.computeMaxMidpointDistance(bufferCurve); - return this._maxPtDist.getDistance() - }; - BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () { - return this._maxPtDist - }; - BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () { - return BufferCurveMaximumDistanceFinder - }; - staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter }; - staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter }; - - Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 ); - - var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) { - this._maxPtDist = new PointPairDistance(); - this._minPtDist = new PointPairDistance(); - this._geom = geom || null; - }; - MaxPointDistanceFilter.prototype.filter = function filter (pt) { - this._minPtDist.initialize(); - DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist); - this._maxPtDist.setMaximum(this._minPtDist); - }; - MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () { - return this._maxPtDist - }; - MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () { - return [CoordinateFilter] - }; - MaxPointDistanceFilter.prototype.getClass = function getClass () { - return MaxPointDistanceFilter - }; - - var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) { - this._maxPtDist = new PointPairDistance(); - this._minPtDist = new PointPairDistance(); - this._geom = geom || null; - }; - MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) { - if (index === 0) { return null } - var p0 = seq.getCoordinate(index - 1); - var p1 = seq.getCoordinate(index); - var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2); - this._minPtDist.initialize(); - DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist); - this._maxPtDist.setMaximum(this._minPtDist); - }; - MaxMidpointDistanceFilter.prototype.isDone = function isDone () { - return false - }; - MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () { - return false - }; - MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () { - return this._maxPtDist - }; - MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () { - return [CoordinateSequenceFilter] - }; - MaxMidpointDistanceFilter.prototype.getClass = function getClass () { - return MaxMidpointDistanceFilter - }; - - var PolygonExtracter = function PolygonExtracter (comps) { - this._comps = comps || null; - }; - PolygonExtracter.prototype.filter = function filter (geom) { - if (geom instanceof Polygon) { this._comps.add(geom); } - }; - PolygonExtracter.prototype.interfaces_ = function interfaces_ () { - return [GeometryFilter] - }; - PolygonExtracter.prototype.getClass = function getClass () { - return PolygonExtracter - }; - PolygonExtracter.getPolygons = function getPolygons () { - if (arguments.length === 1) { - var geom = arguments[0]; - return PolygonExtracter.getPolygons(geom, new ArrayList()) - } else if (arguments.length === 2) { - var geom$1 = arguments[0]; - var list = arguments[1]; - if (geom$1 instanceof Polygon) { - list.add(geom$1); - } else if (geom$1 instanceof GeometryCollection) { - geom$1.apply(new PolygonExtracter(list)); - } - return list - } - }; - - var LinearComponentExtracter = function LinearComponentExtracter () { - this._lines = null; - this._isForcedToLineString = false; - if (arguments.length === 1) { - var lines = arguments[0]; - this._lines = lines; - } else if (arguments.length === 2) { - var lines$1 = arguments[0]; - var isForcedToLineString = arguments[1]; - this._lines = lines$1; - this._isForcedToLineString = isForcedToLineString; - } - }; - LinearComponentExtracter.prototype.filter = function filter (geom) { - if (this._isForcedToLineString && geom instanceof LinearRing) { - var line = geom.getFactory().createLineString(geom.getCoordinateSequence()); - this._lines.add(line); - return null - } - if (geom instanceof LineString) { this._lines.add(geom); } - }; - LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) { - this._isForcedToLineString = isForcedToLineString; - }; - LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () { - return [GeometryComponentFilter] - }; - LinearComponentExtracter.prototype.getClass = function getClass () { - return LinearComponentExtracter - }; - LinearComponentExtracter.getGeometry = function getGeometry () { - if (arguments.length === 1) { - var geom = arguments[0]; - return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom)) - } else if (arguments.length === 2) { - var geom$1 = arguments[0]; - var forceToLineString = arguments[1]; - return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString)) - } - }; - LinearComponentExtracter.getLines = function getLines () { - if (arguments.length === 1) { - var geom = arguments[0]; - return LinearComponentExtracter.getLines(geom, false) - } else if (arguments.length === 2) { - if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) { - var geoms = arguments[0]; - var lines$1 = arguments[1]; - for (var i = geoms.iterator(); i.hasNext();) { - var g = i.next(); - LinearComponentExtracter.getLines(g, lines$1); - } - return lines$1 - } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') { - var geom$1 = arguments[0]; - var forceToLineString = arguments[1]; - var lines = new ArrayList(); - geom$1.apply(new LinearComponentExtracter(lines, forceToLineString)); - return lines - } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) { - var geom$2 = arguments[0]; - var lines$2 = arguments[1]; - if (geom$2 instanceof LineString) { - lines$2.add(geom$2); - } else { - geom$2.apply(new LinearComponentExtracter(lines$2)); - } - return lines$2 - } - } else if (arguments.length === 3) { - if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) { - var geoms$1 = arguments[0]; - var lines$3 = arguments[1]; - var forceToLineString$1 = arguments[2]; - for (var i$1 = geoms$1.iterator(); i$1.hasNext();) { - var g$1 = i$1.next(); - LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1); - } - return lines$3 - } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) { - var geom$3 = arguments[0]; - var lines$4 = arguments[1]; - var forceToLineString$2 = arguments[2]; - geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2)); - return lines$4 - } - } - }; - - var PointLocator = function PointLocator () { - this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE; - this._isIn = null; - this._numBoundaries = null; - if (arguments.length === 0) ; else if (arguments.length === 1) { - var boundaryRule = arguments[0]; - if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') } - this._boundaryRule = boundaryRule; - } - }; - PointLocator.prototype.locateInternal = function locateInternal () { - var this$1 = this; - - if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) { - var p = arguments[0]; - var poly = arguments[1]; - if (poly.isEmpty()) { return Location.EXTERIOR } - var shell = poly.getExteriorRing(); - var shellLoc = this.locateInPolygonRing(p, shell); - if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR } - if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY } - for (var i = 0; i < poly.getNumInteriorRing(); i++) { - var hole = poly.getInteriorRingN(i); - var holeLoc = this$1.locateInPolygonRing(p, hole); - if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR } - if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY } - } - return Location.INTERIOR - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) { - var p$1 = arguments[0]; - var l = arguments[1]; - if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR } - var pt = l.getCoordinates(); - if (!l.isClosed()) { - if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) { - return Location.BOUNDARY - } - } - if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR } - return Location.EXTERIOR - } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) { - var p$2 = arguments[0]; - var pt$1 = arguments[1]; - var ptCoord = pt$1.getCoordinate(); - if (ptCoord.equals2D(p$2)) { return Location.INTERIOR } - return Location.EXTERIOR - } - }; - PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) { - if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR } - return CGAlgorithms.locatePointInRing(p, ring.getCoordinates()) - }; - PointLocator.prototype.intersects = function intersects (p, geom) { - return this.locate(p, geom) !== Location.EXTERIOR - }; - PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) { - if (loc === Location.INTERIOR) { this._isIn = true; } - if (loc === Location.BOUNDARY) { this._numBoundaries++; } - }; - PointLocator.prototype.computeLocation = function computeLocation (p, geom) { - var this$1 = this; - - if (geom instanceof Point$1) { - this.updateLocationInfo(this.locateInternal(p, geom)); - } - if (geom instanceof LineString) { - this.updateLocationInfo(this.locateInternal(p, geom)); - } else if (geom instanceof Polygon) { - this.updateLocationInfo(this.locateInternal(p, geom)); - } else if (geom instanceof MultiLineString) { - var ml = geom; - for (var i = 0; i < ml.getNumGeometries(); i++) { - var l = ml.getGeometryN(i); - this$1.updateLocationInfo(this$1.locateInternal(p, l)); - } - } else if (geom instanceof MultiPolygon) { - var mpoly = geom; - for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) { - var poly = mpoly.getGeometryN(i$1); - this$1.updateLocationInfo(this$1.locateInternal(p, poly)); - } - } else if (geom instanceof GeometryCollection) { - var geomi = new GeometryCollectionIterator(geom); - while (geomi.hasNext()) { - var g2 = geomi.next(); - if (g2 !== geom) { this$1.computeLocation(p, g2); } - } - } - }; - PointLocator.prototype.locate = function locate (p, geom) { - if (geom.isEmpty()) { return Location.EXTERIOR } - if (geom instanceof LineString) { - return this.locateInternal(p, geom) - } else if (geom instanceof Polygon) { - return this.locateInternal(p, geom) - } - this._isIn = false; - this._numBoundaries = 0; - this.computeLocation(p, geom); - if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY } - if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR } - return Location.EXTERIOR - }; - PointLocator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PointLocator.prototype.getClass = function getClass () { - return PointLocator - }; - - var GeometryLocation = function GeometryLocation () { - this._component = null; - this._segIndex = null; - this._pt = null; - if (arguments.length === 2) { - var component = arguments[0]; - var pt = arguments[1]; - GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt); - } else if (arguments.length === 3) { - var component$1 = arguments[0]; - var segIndex = arguments[1]; - var pt$1 = arguments[2]; - this._component = component$1; - this._segIndex = segIndex; - this._pt = pt$1; - } - }; - - var staticAccessors$38 = { INSIDE_AREA: { configurable: true } }; - GeometryLocation.prototype.isInsideArea = function isInsideArea () { - return this._segIndex === GeometryLocation.INSIDE_AREA - }; - GeometryLocation.prototype.getCoordinate = function getCoordinate () { - return this._pt - }; - GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () { - return this._component - }; - GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () { - return this._segIndex - }; - GeometryLocation.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryLocation.prototype.getClass = function getClass () { - return GeometryLocation - }; - staticAccessors$38.INSIDE_AREA.get = function () { return -1 }; - - Object.defineProperties( GeometryLocation, staticAccessors$38 ); - - var PointExtracter = function PointExtracter (pts) { - this._pts = pts || null; - }; - PointExtracter.prototype.filter = function filter (geom) { - if (geom instanceof Point$1) { this._pts.add(geom); } - }; - PointExtracter.prototype.interfaces_ = function interfaces_ () { - return [GeometryFilter] - }; - PointExtracter.prototype.getClass = function getClass () { - return PointExtracter - }; - PointExtracter.getPoints = function getPoints () { - if (arguments.length === 1) { - var geom = arguments[0]; - if (geom instanceof Point$1) { - return Collections.singletonList(geom) - } - return PointExtracter.getPoints(geom, new ArrayList()) - } else if (arguments.length === 2) { - var geom$1 = arguments[0]; - var list = arguments[1]; - if (geom$1 instanceof Point$1) { - list.add(geom$1); - } else if (geom$1 instanceof GeometryCollection) { - geom$1.apply(new PointExtracter(list)); - } - return list - } - }; - - var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () { - this._locations = null; - var locations = arguments[0]; - this._locations = locations; - }; - ConnectedElementLocationFilter.prototype.filter = function filter (geom) { - if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); } - }; - ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () { - return [GeometryFilter] - }; - ConnectedElementLocationFilter.prototype.getClass = function getClass () { - return ConnectedElementLocationFilter - }; - ConnectedElementLocationFilter.getLocations = function getLocations (geom) { - var locations = new ArrayList(); - geom.apply(new ConnectedElementLocationFilter(locations)); - return locations - }; - - var DistanceOp = function DistanceOp () { - this._geom = null; - this._terminateDistance = 0.0; - this._ptLocator = new PointLocator(); - this._minDistanceLocation = null; - this._minDistance = Double.MAX_VALUE; - if (arguments.length === 2) { - var g0 = arguments[0]; - var g1 = arguments[1]; - this._geom = [g0, g1]; - this._terminateDistance = 0.0; - } else if (arguments.length === 3) { - var g0$1 = arguments[0]; - var g1$1 = arguments[1]; - var terminateDistance = arguments[2]; - this._geom = new Array(2).fill(null); - this._geom[0] = g0$1; - this._geom[1] = g1$1; - this._terminateDistance = terminateDistance; - } - }; - DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () { - var this$1 = this; - - if (arguments.length === 0) { - var locPtPoly = new Array(2).fill(null); - this.computeContainmentDistance(0, locPtPoly); - if (this._minDistance <= this._terminateDistance) { return null } - this.computeContainmentDistance(1, locPtPoly); - } else if (arguments.length === 2) { - var polyGeomIndex = arguments[0]; - var locPtPoly$1 = arguments[1]; - var locationsIndex = 1 - polyGeomIndex; - var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]); - if (polys.size() > 0) { - var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]); - this.computeContainmentDistance(insideLocs, polys, locPtPoly$1); - if (this._minDistance <= this._terminateDistance) { - this._minDistanceLocation[locationsIndex] = locPtPoly$1[0]; - this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1]; - return null - } - } - } else if (arguments.length === 3) { - if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) { - var locs = arguments[0]; - var polys$1 = arguments[1]; - var locPtPoly$2 = arguments[2]; - for (var i = 0; i < locs.size(); i++) { - var loc = locs.get(i); - for (var j = 0; j < polys$1.size(); j++) { - this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2); - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } - } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) { - var ptLoc = arguments[0]; - var poly = arguments[1]; - var locPtPoly$3 = arguments[2]; - var pt = ptLoc.getCoordinate(); - if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) { - this._minDistance = 0.0; - locPtPoly$3[0] = ptLoc; - locPtPoly$3[1] = new GeometryLocation(poly, pt); - - return null - } - } - } - }; - DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) { - var this$1 = this; - - for (var i = 0; i < lines.size(); i++) { - var line = lines.get(i); - for (var j = 0; j < points.size(); j++) { - var pt = points.get(j); - this$1.computeMinDistance(line, pt, locGeom); - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } - }; - DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () { - var locGeom = new Array(2).fill(null); - var lines0 = LinearComponentExtracter.getLines(this._geom[0]); - var lines1 = LinearComponentExtracter.getLines(this._geom[1]); - var pts0 = PointExtracter.getPoints(this._geom[0]); - var pts1 = PointExtracter.getPoints(this._geom[1]); - this.computeMinDistanceLines(lines0, lines1, locGeom); - this.updateMinDistance(locGeom, false); - if (this._minDistance <= this._terminateDistance) { return null } - locGeom[0] = null; - locGeom[1] = null; - this.computeMinDistanceLinesPoints(lines0, pts1, locGeom); - this.updateMinDistance(locGeom, false); - if (this._minDistance <= this._terminateDistance) { return null } - locGeom[0] = null; - locGeom[1] = null; - this.computeMinDistanceLinesPoints(lines1, pts0, locGeom); - this.updateMinDistance(locGeom, true); - if (this._minDistance <= this._terminateDistance) { return null } - locGeom[0] = null; - locGeom[1] = null; - this.computeMinDistancePoints(pts0, pts1, locGeom); - this.updateMinDistance(locGeom, false); - }; - DistanceOp.prototype.nearestLocations = function nearestLocations () { - this.computeMinDistance(); - return this._minDistanceLocation - }; - DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) { - if (locGeom[0] === null) { return null } - if (flip) { - this._minDistanceLocation[0] = locGeom[1]; - this._minDistanceLocation[1] = locGeom[0]; - } else { - this._minDistanceLocation[0] = locGeom[0]; - this._minDistanceLocation[1] = locGeom[1]; - } - }; - DistanceOp.prototype.nearestPoints = function nearestPoints () { - this.computeMinDistance(); - var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()]; - return nearestPts - }; - DistanceOp.prototype.computeMinDistance = function computeMinDistance () { - var this$1 = this; - - if (arguments.length === 0) { - if (this._minDistanceLocation !== null) { return null } - this._minDistanceLocation = new Array(2).fill(null); - this.computeContainmentDistance(); - if (this._minDistance <= this._terminateDistance) { return null } - this.computeFacetDistance(); - } else if (arguments.length === 3) { - if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) { - var line = arguments[0]; - var pt = arguments[1]; - var locGeom = arguments[2]; - if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null } - var coord0 = line.getCoordinates(); - var coord = pt.getCoordinate(); - for (var i = 0; i < coord0.length - 1; i++) { - var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]); - if (dist < this$1._minDistance) { - this$1._minDistance = dist; - var seg = new LineSegment(coord0[i], coord0[i + 1]); - var segClosestPoint = seg.closestPoint(coord); - locGeom[0] = new GeometryLocation(line, i, segClosestPoint); - locGeom[1] = new GeometryLocation(pt, 0, coord); - } - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) { - var line0 = arguments[0]; - var line1 = arguments[1]; - var locGeom$1 = arguments[2]; - if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null } - var coord0$1 = line0.getCoordinates(); - var coord1 = line1.getCoordinates(); - for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) { - for (var j = 0; j < coord1.length - 1; j++) { - var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]); - if (dist$1 < this$1._minDistance) { - this$1._minDistance = dist$1; - var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]); - var seg1 = new LineSegment(coord1[j], coord1[j + 1]); - var closestPt = seg0.closestPoints(seg1); - locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]); - locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]); - } - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } - } - } - }; - DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) { - var this$1 = this; - - for (var i = 0; i < points0.size(); i++) { - var pt0 = points0.get(i); - for (var j = 0; j < points1.size(); j++) { - var pt1 = points1.get(j); - var dist = pt0.getCoordinate().distance(pt1.getCoordinate()); - if (dist < this$1._minDistance) { - this$1._minDistance = dist; - locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate()); - locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate()); - } - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } - }; - DistanceOp.prototype.distance = function distance () { - if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') } - if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 } - this.computeMinDistance(); - return this._minDistance - }; - DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) { - var this$1 = this; - - for (var i = 0; i < lines0.size(); i++) { - var line0 = lines0.get(i); - for (var j = 0; j < lines1.size(); j++) { - var line1 = lines1.get(j); - this$1.computeMinDistance(line0, line1, locGeom); - if (this$1._minDistance <= this$1._terminateDistance) { return null } - } - } - }; - DistanceOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DistanceOp.prototype.getClass = function getClass () { - return DistanceOp - }; - DistanceOp.distance = function distance (g0, g1) { - var distOp = new DistanceOp(g0, g1); - return distOp.distance() - }; - DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) { - var distOp = new DistanceOp(g0, g1, distance); - return distOp.distance() <= distance - }; - DistanceOp.nearestPoints = function nearestPoints (g0, g1) { - var distOp = new DistanceOp(g0, g1); - return distOp.nearestPoints() - }; - - var PointPairDistance$2 = function PointPairDistance () { - this._pt = [new Coordinate(), new Coordinate()]; - this._distance = Double.NaN; - this._isNull = true; - }; - PointPairDistance$2.prototype.getCoordinates = function getCoordinates () { - return this._pt - }; - PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) { - return this._pt[i] - }; - PointPairDistance$2.prototype.setMinimum = function setMinimum () { - if (arguments.length === 1) { - var ptDist = arguments[0]; - this.setMinimum(ptDist._pt[0], ptDist._pt[1]); - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - if (this._isNull) { - this.initialize(p0, p1); - return null - } - var dist = p0.distance(p1); - if (dist < this._distance) { this.initialize(p0, p1, dist); } - } - }; - PointPairDistance$2.prototype.initialize = function initialize () { - if (arguments.length === 0) { - this._isNull = true; - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - this._pt[0].setCoordinate(p0); - this._pt[1].setCoordinate(p1); - this._distance = p0.distance(p1); - this._isNull = false; - } else if (arguments.length === 3) { - var p0$1 = arguments[0]; - var p1$1 = arguments[1]; - var distance = arguments[2]; - this._pt[0].setCoordinate(p0$1); - this._pt[1].setCoordinate(p1$1); - this._distance = distance; - this._isNull = false; - } - }; - PointPairDistance$2.prototype.toString = function toString () { - return WKTWriter.toLineString(this._pt[0], this._pt[1]) - }; - PointPairDistance$2.prototype.getDistance = function getDistance () { - return this._distance - }; - PointPairDistance$2.prototype.setMaximum = function setMaximum () { - if (arguments.length === 1) { - var ptDist = arguments[0]; - this.setMaximum(ptDist._pt[0], ptDist._pt[1]); - } else if (arguments.length === 2) { - var p0 = arguments[0]; - var p1 = arguments[1]; - if (this._isNull) { - this.initialize(p0, p1); - return null - } - var dist = p0.distance(p1); - if (dist > this._distance) { this.initialize(p0, p1, dist); } - } - }; - PointPairDistance$2.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PointPairDistance$2.prototype.getClass = function getClass () { - return PointPairDistance$2 - }; - - var DistanceToPoint = function DistanceToPoint () {}; - - DistanceToPoint.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DistanceToPoint.prototype.getClass = function getClass () { - return DistanceToPoint - }; - DistanceToPoint.computeDistance = function computeDistance () { - if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) { - var line = arguments[0]; - var pt = arguments[1]; - var ptDist = arguments[2]; - var tempSegment = new LineSegment(); - var coords = line.getCoordinates(); - for (var i = 0; i < coords.length - 1; i++) { - tempSegment.setCoordinates(coords[i], coords[i + 1]); - var closestPt = tempSegment.closestPoint(pt); - ptDist.setMinimum(closestPt, pt); - } - } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) { - var poly = arguments[0]; - var pt$1 = arguments[1]; - var ptDist$1 = arguments[2]; - DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1); - for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) { - DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1); - } - } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) { - var geom = arguments[0]; - var pt$2 = arguments[1]; - var ptDist$2 = arguments[2]; - if (geom instanceof LineString) { - DistanceToPoint.computeDistance(geom, pt$2, ptDist$2); - } else if (geom instanceof Polygon) { - DistanceToPoint.computeDistance(geom, pt$2, ptDist$2); - } else if (geom instanceof GeometryCollection) { - var gc = geom; - for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) { - var g = gc.getGeometryN(i$2); - DistanceToPoint.computeDistance(g, pt$2, ptDist$2); - } - } else { - ptDist$2.setMinimum(geom.getCoordinate(), pt$2); - } - } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) { - var segment = arguments[0]; - var pt$3 = arguments[1]; - var ptDist$3 = arguments[2]; - var closestPt$1 = segment.closestPoint(pt$3); - ptDist$3.setMinimum(closestPt$1, pt$3); - } - }; - - var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () { - this._g0 = null; - this._g1 = null; - this._ptDist = new PointPairDistance$2(); - this._densifyFrac = 0.0; - var g0 = arguments[0]; - var g1 = arguments[1]; - this._g0 = g0; - this._g1 = g1; - }; - - var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } }; - DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () { - return this._ptDist.getCoordinates() - }; - DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) { - if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') } - this._densifyFrac = densifyFrac; - }; - DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) { - this.computeOrientedDistance(g0, g1, this._ptDist); - this.computeOrientedDistance(g1, g0, this._ptDist); - }; - DiscreteHausdorffDistance.prototype.distance = function distance () { - this.compute(this._g0, this._g1); - return this._ptDist.getDistance() - }; - DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) { - var distFilter = new MaxPointDistanceFilter$1(geom); - discreteGeom.apply(distFilter); - ptDist.setMaximum(distFilter.getMaxPointDistance()); - if (this._densifyFrac > 0) { - var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac); - discreteGeom.apply(fracFilter); - ptDist.setMaximum(fracFilter.getMaxPointDistance()); - } - }; - DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () { - this.computeOrientedDistance(this._g0, this._g1, this._ptDist); - return this._ptDist.getDistance() - }; - DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () { - return [] - }; - DiscreteHausdorffDistance.prototype.getClass = function getClass () { - return DiscreteHausdorffDistance - }; - DiscreteHausdorffDistance.distance = function distance () { - if (arguments.length === 2) { - var g0 = arguments[0]; - var g1 = arguments[1]; - var dist = new DiscreteHausdorffDistance(g0, g1); - return dist.distance() - } else if (arguments.length === 3) { - var g0$1 = arguments[0]; - var g1$1 = arguments[1]; - var densifyFrac = arguments[2]; - var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1); - dist$1.setDensifyFraction(densifyFrac); - return dist$1.distance() - } - }; - staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 }; - staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter }; - - Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 ); - - var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () { - this._maxPtDist = new PointPairDistance$2(); - this._minPtDist = new PointPairDistance$2(); - this._euclideanDist = new DistanceToPoint(); - this._geom = null; - var geom = arguments[0]; - this._geom = geom; - }; - MaxPointDistanceFilter$1.prototype.filter = function filter (pt) { - this._minPtDist.initialize(); - DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist); - this._maxPtDist.setMaximum(this._minPtDist); - }; - MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () { - return this._maxPtDist - }; - MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () { - return [CoordinateFilter] - }; - MaxPointDistanceFilter$1.prototype.getClass = function getClass () { - return MaxPointDistanceFilter$1 - }; - - var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () { - this._maxPtDist = new PointPairDistance$2(); - this._minPtDist = new PointPairDistance$2(); - this._geom = null; - this._numSubSegs = 0; - var geom = arguments[0]; - var fraction = arguments[1]; - this._geom = geom; - this._numSubSegs = Math.trunc(Math.round(1.0 / fraction)); - }; - MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) { - var this$1 = this; - - if (index === 0) { return null } - var p0 = seq.getCoordinate(index - 1); - var p1 = seq.getCoordinate(index); - var delx = (p1.x - p0.x) / this._numSubSegs; - var dely = (p1.y - p0.y) / this._numSubSegs; - for (var i = 0; i < this._numSubSegs; i++) { - var x = p0.x + i * delx; - var y = p0.y + i * dely; - var pt = new Coordinate(x, y); - this$1._minPtDist.initialize(); - DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist); - this$1._maxPtDist.setMaximum(this$1._minPtDist); - } - }; - MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () { - return false - }; - MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () { - return false - }; - MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () { - return this._maxPtDist - }; - MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () { - return [CoordinateSequenceFilter] - }; - MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () { - return MaxDensifiedByFractionDistanceFilter - }; - - var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) { - this._minValidDistance = null; - this._maxValidDistance = null; - this._minDistanceFound = null; - this._maxDistanceFound = null; - this._isValid = true; - this._errMsg = null; - this._errorLocation = null; - this._errorIndicator = null; - this._input = input || null; - this._bufDistance = bufDistance || null; - this._result = result || null; - }; - - var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } }; - BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) { - var haus = new DiscreteHausdorffDistance(bufCurve, input); - haus.setDensifyFraction(0.25); - this._maxDistanceFound = haus.orientedDistance(); - if (this._maxDistanceFound > maxDist) { - this._isValid = false; - var pts = haus.getCoordinates(); - this._errorLocation = pts[1]; - this._errorIndicator = input.getFactory().createLineString(pts); - this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')'; - } - }; - BufferDistanceValidator.prototype.isValid = function isValid () { - var posDistance = Math.abs(this._bufDistance); - var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance; - this._minValidDistance = posDistance - distDelta; - this._maxValidDistance = posDistance + distDelta; - if (this._input.isEmpty() || this._result.isEmpty()) { return true } - if (this._bufDistance > 0.0) { - this.checkPositiveValid(); - } else { - this.checkNegativeValid(); - } - if (BufferDistanceValidator.VERBOSE) { - System.out.println('Min Dist= ' + this._minDistanceFound + ' err= ' + (1.0 - this._minDistanceFound / this._bufDistance) + ' Max Dist= ' + this._maxDistanceFound + ' err= ' + (this._maxDistanceFound / this._bufDistance - 1.0)); - } - return this._isValid - }; - BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () { - if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) { - return null - } - var inputCurve = this.getPolygonLines(this._input); - this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance); - if (!this._isValid) { return null } - this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance); - }; - BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () { - return this._errorIndicator - }; - BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) { - var distOp = new DistanceOp(g1, g2, minDist); - this._minDistanceFound = distOp.distance(); - if (this._minDistanceFound < minDist) { - this._isValid = false; - var pts = distOp.nearestPoints(); - this._errorLocation = distOp.nearestPoints()[1]; - this._errorIndicator = g1.getFactory().createLineString(pts); - this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )'; - } - }; - BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () { - var bufCurve = this._result.getBoundary(); - this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance); - if (!this._isValid) { return null } - this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance); - }; - BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () { - return this._errorLocation - }; - BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) { - var lines = new ArrayList(); - var lineExtracter = new LinearComponentExtracter(lines); - var polys = PolygonExtracter.getPolygons(g); - for (var i = polys.iterator(); i.hasNext();) { - var poly = i.next(); - poly.apply(lineExtracter); - } - return g.getFactory().buildGeometry(lines) - }; - BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () { - return this._errMsg - }; - BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferDistanceValidator.prototype.getClass = function getClass () { - return BufferDistanceValidator - }; - staticAccessors$37.VERBOSE.get = function () { return false }; - staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 }; - - Object.defineProperties( BufferDistanceValidator, staticAccessors$37 ); - - var BufferResultValidator = function BufferResultValidator (input, distance, result) { - this._isValid = true; - this._errorMsg = null; - this._errorLocation = null; - this._errorIndicator = null; - this._input = input || null; - this._distance = distance || null; - this._result = result || null; - }; - - var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } }; - BufferResultValidator.prototype.isValid = function isValid () { - this.checkPolygonal(); - if (!this._isValid) { return this._isValid } - this.checkExpectedEmpty(); - if (!this._isValid) { return this._isValid } - this.checkEnvelope(); - if (!this._isValid) { return this._isValid } - this.checkArea(); - if (!this._isValid) { return this._isValid } - this.checkDistance(); - return this._isValid - }; - BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () { - if (this._distance < 0.0) { return null } - var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC; - if (padding === 0.0) { padding = 0.001; } - var expectedEnv = new Envelope(this._input.getEnvelopeInternal()); - expectedEnv.expandBy(this._distance); - var bufEnv = new Envelope(this._result.getEnvelopeInternal()); - bufEnv.expandBy(padding); - if (!bufEnv.contains(expectedEnv)) { - this._isValid = false; - this._errorMsg = 'Buffer envelope is incorrect'; - this._errorIndicator = this._input.getFactory().toGeometry(bufEnv); - } - this.report('Envelope'); - }; - BufferResultValidator.prototype.checkDistance = function checkDistance () { - var distValid = new BufferDistanceValidator(this._input, this._distance, this._result); - if (!distValid.isValid()) { - this._isValid = false; - this._errorMsg = distValid.getErrorMessage(); - this._errorLocation = distValid.getErrorLocation(); - this._errorIndicator = distValid.getErrorIndicator(); - } - this.report('Distance'); - }; - BufferResultValidator.prototype.checkArea = function checkArea () { - var inputArea = this._input.getArea(); - var resultArea = this._result.getArea(); - if (this._distance > 0.0 && inputArea > resultArea) { - this._isValid = false; - this._errorMsg = 'Area of positive buffer is smaller than input'; - this._errorIndicator = this._result; - } - if (this._distance < 0.0 && inputArea < resultArea) { - this._isValid = false; - this._errorMsg = 'Area of negative buffer is larger than input'; - this._errorIndicator = this._result; - } - this.report('Area'); - }; - BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () { - if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; } - this._errorMsg = 'Result is not polygonal'; - this._errorIndicator = this._result; - this.report('Polygonal'); - }; - BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () { - return this._errorIndicator - }; - BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () { - return this._errorLocation - }; - BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () { - if (this._input.getDimension() >= 2) { return null } - if (this._distance > 0.0) { return null } - if (!this._result.isEmpty()) { - this._isValid = false; - this._errorMsg = 'Result is non-empty'; - this._errorIndicator = this._result; - } - this.report('ExpectedEmpty'); - }; - BufferResultValidator.prototype.report = function report (checkName) { - if (!BufferResultValidator.VERBOSE) { return null } - System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED')); - }; - BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () { - return this._errorMsg - }; - BufferResultValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - BufferResultValidator.prototype.getClass = function getClass () { - return BufferResultValidator - }; - BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) { - var validator = new BufferResultValidator(g, distance, result); - if (!validator.isValid()) { return validator.getErrorMessage() } - return null - }; - BufferResultValidator.isValid = function isValid (g, distance, result) { - var validator = new BufferResultValidator(g, distance, result); - if (validator.isValid()) { return true } - return false - }; - staticAccessors$40.VERBOSE.get = function () { return false }; - staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 }; - - Object.defineProperties( BufferResultValidator, staticAccessors$40 ); - - // operation.buffer - - var BasicSegmentString = function BasicSegmentString () { - this._pts = null; - this._data = null; - var pts = arguments[0]; - var data = arguments[1]; - this._pts = pts; - this._data = data; - }; - BasicSegmentString.prototype.getCoordinates = function getCoordinates () { - return this._pts - }; - BasicSegmentString.prototype.size = function size () { - return this._pts.length - }; - BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) { - return this._pts[i] - }; - BasicSegmentString.prototype.isClosed = function isClosed () { - return this._pts[0].equals(this._pts[this._pts.length - 1]) - }; - BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) { - if (index === this._pts.length - 1) { return -1 } - return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1)) - }; - BasicSegmentString.prototype.setData = function setData (data) { - this._data = data; - }; - BasicSegmentString.prototype.getData = function getData () { - return this._data - }; - BasicSegmentString.prototype.toString = function toString () { - return WKTWriter.toLineString(new CoordinateArraySequence(this._pts)) - }; - BasicSegmentString.prototype.interfaces_ = function interfaces_ () { - return [SegmentString] - }; - BasicSegmentString.prototype.getClass = function getClass () { - return BasicSegmentString - }; - - var InteriorIntersectionFinder = function InteriorIntersectionFinder () { - this._findAllIntersections = false; - this._isCheckEndSegmentsOnly = false; - this._li = null; - this._interiorIntersection = null; - this._intSegments = null; - this._intersections = new ArrayList(); - this._intersectionCount = 0; - this._keepIntersections = true; - var li = arguments[0]; - this._li = li; - this._interiorIntersection = null; - }; - InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () { - return this._interiorIntersection - }; - InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) { - this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly; - }; - InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () { - return this._intSegments - }; - InteriorIntersectionFinder.prototype.count = function count () { - return this._intersectionCount - }; - InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () { - return this._intersections - }; - InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) { - this._findAllIntersections = findAllIntersections; - }; - InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) { - this._keepIntersections = keepIntersections; - }; - InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) { - if (!this._findAllIntersections && this.hasIntersection()) { return null } - if (e0 === e1 && segIndex0 === segIndex1) { return null } - if (this._isCheckEndSegmentsOnly) { - var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1); - if (!isEndSegPresent) { return null } - } - var p00 = e0.getCoordinates()[segIndex0]; - var p01 = e0.getCoordinates()[segIndex0 + 1]; - var p10 = e1.getCoordinates()[segIndex1]; - var p11 = e1.getCoordinates()[segIndex1 + 1]; - this._li.computeIntersection(p00, p01, p10, p11); - if (this._li.hasIntersection()) { - if (this._li.isInteriorIntersection()) { - this._intSegments = new Array(4).fill(null); - this._intSegments[0] = p00; - this._intSegments[1] = p01; - this._intSegments[2] = p10; - this._intSegments[3] = p11; - this._interiorIntersection = this._li.getIntersection(0); - if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); } - this._intersectionCount++; - } - } - }; - InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) { - if (index === 0) { return true } - if (index >= segStr.size() - 2) { return true } - return false - }; - InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () { - return this._interiorIntersection !== null - }; - InteriorIntersectionFinder.prototype.isDone = function isDone () { - if (this._findAllIntersections) { return false } - return this._interiorIntersection !== null - }; - InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () { - return [SegmentIntersector] - }; - InteriorIntersectionFinder.prototype.getClass = function getClass () { - return InteriorIntersectionFinder - }; - InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) { - var finder = new InteriorIntersectionFinder(li); - finder.setFindAllIntersections(true); - return finder - }; - InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) { - return new InteriorIntersectionFinder(li) - }; - InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) { - var finder = new InteriorIntersectionFinder(li); - finder.setFindAllIntersections(true); - finder.setKeepIntersections(false); - return finder - }; - - var FastNodingValidator = function FastNodingValidator () { - this._li = new RobustLineIntersector(); - this._segStrings = null; - this._findAllIntersections = false; - this._segInt = null; - this._isValid = true; - var segStrings = arguments[0]; - this._segStrings = segStrings; - }; - FastNodingValidator.prototype.execute = function execute () { - if (this._segInt !== null) { return null } - this.checkInteriorIntersections(); - }; - FastNodingValidator.prototype.getIntersections = function getIntersections () { - return this._segInt.getIntersections() - }; - FastNodingValidator.prototype.isValid = function isValid () { - this.execute(); - return this._isValid - }; - FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) { - this._findAllIntersections = findAllIntersections; - }; - FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () { - this._isValid = true; - this._segInt = new InteriorIntersectionFinder(this._li); - this._segInt.setFindAllIntersections(this._findAllIntersections); - var noder = new MCIndexNoder(); - noder.setSegmentIntersector(this._segInt); - noder.computeNodes(this._segStrings); - if (this._segInt.hasIntersection()) { - this._isValid = false; - return null - } - }; - FastNodingValidator.prototype.checkValid = function checkValid () { - this.execute(); - if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) } - }; - FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () { - if (this._isValid) { return 'no intersections found' } - var intSegs = this._segInt.getIntersectionSegments(); - return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3]) - }; - FastNodingValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - FastNodingValidator.prototype.getClass = function getClass () { - return FastNodingValidator - }; - FastNodingValidator.computeIntersections = function computeIntersections (segStrings) { - var nv = new FastNodingValidator(segStrings); - nv.setFindAllIntersections(true); - nv.isValid(); - return nv.getIntersections() - }; - - var EdgeNodingValidator = function EdgeNodingValidator () { - this._nv = null; - var edges = arguments[0]; - this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges)); - }; - EdgeNodingValidator.prototype.checkValid = function checkValid () { - this._nv.checkValid(); - }; - EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeNodingValidator.prototype.getClass = function getClass () { - return EdgeNodingValidator - }; - EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) { - var segStrings = new ArrayList(); - for (var i = edges.iterator(); i.hasNext();) { - var e = i.next(); - segStrings.add(new BasicSegmentString(e.getCoordinates(), e)); - } - return segStrings - }; - EdgeNodingValidator.checkValid = function checkValid (edges) { - var validator = new EdgeNodingValidator(edges); - validator.checkValid(); - }; - - var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) { - this._mapOp = mapOp; - }; - GeometryCollectionMapper.prototype.map = function map (gc) { - var this$1 = this; - - var mapped = new ArrayList(); - for (var i = 0; i < gc.getNumGeometries(); i++) { - var g = this$1._mapOp.map(gc.getGeometryN(i)); - if (!g.isEmpty()) { mapped.add(g); } - } - return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped)) - }; - GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryCollectionMapper.prototype.getClass = function getClass () { - return GeometryCollectionMapper - }; - GeometryCollectionMapper.map = function map (gc, op) { - var mapper = new GeometryCollectionMapper(op); - return mapper.map(gc) - }; - - var LineBuilder = function LineBuilder () { - this._op = null; - this._geometryFactory = null; - this._ptLocator = null; - this._lineEdgesList = new ArrayList(); - this._resultLineList = new ArrayList(); - var op = arguments[0]; - var geometryFactory = arguments[1]; - var ptLocator = arguments[2]; - this._op = op; - this._geometryFactory = geometryFactory; - this._ptLocator = ptLocator; - }; - LineBuilder.prototype.collectLines = function collectLines (opCode) { - var this$1 = this; - - for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) { - var de = it.next(); - this$1.collectLineEdge(de, opCode, this$1._lineEdgesList); - this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList); - } - }; - LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) { - var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex)); - e.getLabel().setLocation(targetIndex, loc); - }; - LineBuilder.prototype.build = function build (opCode) { - this.findCoveredLineEdges(); - this.collectLines(opCode); - this.buildLines(opCode); - return this._resultLineList - }; - LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) { - var label = de.getLabel(); - var e = de.getEdge(); - if (de.isLineEdge()) { - if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) { - edges.add(e); - de.setVisitedEdge(true); - } - } - }; - LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () { - var this$1 = this; - - for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().findCoveredLineEdges(); - } - for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) { - var de = it.next(); - var e = de.getEdge(); - if (de.isLineEdge() && !e.isCoveredSet()) { - var isCovered = this$1._op.isCoveredByA(de.getCoordinate()); - e.setCovered(isCovered); - } - } - }; - LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) { - var this$1 = this; - - for (var it = edgesList.iterator(); it.hasNext();) { - var e = it.next(); - var label = e.getLabel(); - if (e.isIsolated()) { - if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); } - } - } - }; - LineBuilder.prototype.buildLines = function buildLines (opCode) { - var this$1 = this; - - for (var it = this._lineEdgesList.iterator(); it.hasNext();) { - var e = it.next(); - // const label = e.getLabel() - var line = this$1._geometryFactory.createLineString(e.getCoordinates()); - this$1._resultLineList.add(line); - e.setInResult(true); - } - }; - LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) { - var label = de.getLabel(); - if (de.isLineEdge()) { return null } - if (de.isVisited()) { return null } - if (de.isInteriorAreaEdge()) { return null } - if (de.getEdge().isInResult()) { return null } - Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult()); - if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) { - edges.add(de.getEdge()); - de.setVisitedEdge(true); - } - }; - LineBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - LineBuilder.prototype.getClass = function getClass () { - return LineBuilder - }; - - var PointBuilder = function PointBuilder () { - this._op = null; - this._geometryFactory = null; - this._resultPointList = new ArrayList(); - var op = arguments[0]; - var geometryFactory = arguments[1]; - // const ptLocator = arguments[2] - this._op = op; - this._geometryFactory = geometryFactory; - }; - PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) { - var coord = n.getCoordinate(); - if (!this._op.isCoveredByLA(coord)) { - var pt = this._geometryFactory.createPoint(coord); - this._resultPointList.add(pt); - } - }; - PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) { - var this$1 = this; - - for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) { - var n = nodeit.next(); - if (n.isInResult()) { continue } - if (n.isIncidentEdgeInResult()) { continue } - if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) { - var label = n.getLabel(); - if (OverlayOp.isResultOfOp(label, opCode)) { - this$1.filterCoveredNodeToPoint(n); - } - } - } - }; - PointBuilder.prototype.build = function build (opCode) { - this.extractNonCoveredResultNodes(opCode); - return this._resultPointList - }; - PointBuilder.prototype.interfaces_ = function interfaces_ () { - return [] - }; - PointBuilder.prototype.getClass = function getClass () { - return PointBuilder - }; - - var GeometryTransformer = function GeometryTransformer () { - this._inputGeom = null; - this._factory = null; - this._pruneEmptyGeometry = true; - this._preserveGeometryCollectionType = true; - this._preserveCollections = false; - this._preserveType = false; - }; - GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) { - return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom)) - }; - GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) { - var this$1 = this; - - var isAllValidLinearRings = true; - var shell = this.transformLinearRing(geom.getExteriorRing(), geom); - if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; } - var holes = new ArrayList(); - for (var i = 0; i < geom.getNumInteriorRing(); i++) { - var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom); - if (hole === null || hole.isEmpty()) { - continue - } - if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; } - holes.add(hole); - } - if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else { - var components = new ArrayList(); - if (shell !== null) { components.add(shell); } - components.addAll(holes); - return this._factory.buildGeometry(components) - } - }; - GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) { - return this._factory.getCoordinateSequenceFactory().create(coords) - }; - GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () { - return this._inputGeom - }; - GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) { - var this$1 = this; - - var transGeomList = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom); - if (transformGeom === null) { continue } - if (transformGeom.isEmpty()) { continue } - transGeomList.add(transformGeom); - } - return this._factory.buildGeometry(transGeomList) - }; - GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) { - return this.copy(coords) - }; - GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) { - return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom)) - }; - GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) { - var this$1 = this; - - var transGeomList = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom); - if (transformGeom === null) { continue } - if (transformGeom.isEmpty()) { continue } - transGeomList.add(transformGeom); - } - return this._factory.buildGeometry(transGeomList) - }; - GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) { - var this$1 = this; - - var transGeomList = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom); - if (transformGeom === null) { continue } - if (transformGeom.isEmpty()) { continue } - transGeomList.add(transformGeom); - } - return this._factory.buildGeometry(transGeomList) - }; - GeometryTransformer.prototype.copy = function copy (seq) { - return seq.copy() - }; - GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) { - var this$1 = this; - - var transGeomList = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var transformGeom = this$1.transform(geom.getGeometryN(i)); - if (transformGeom === null) { continue } - if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue } - transGeomList.add(transformGeom); - } - if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) } - return this._factory.buildGeometry(transGeomList) - }; - GeometryTransformer.prototype.transform = function transform (inputGeom) { - this._inputGeom = inputGeom; - this._factory = inputGeom.getFactory(); - if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) } - if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) } - if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) } - if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) } - if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) } - if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) } - if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) } - if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) } - throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName()) - }; - GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) { - var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom); - if (seq === null) { return this._factory.createLinearRing(null) } - var seqSize = seq.size(); - if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) } - return this._factory.createLinearRing(seq) - }; - GeometryTransformer.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryTransformer.prototype.getClass = function getClass () { - return GeometryTransformer - }; - - var LineStringSnapper = function LineStringSnapper () { - this._snapTolerance = 0.0; - this._srcPts = null; - this._seg = new LineSegment(); - this._allowSnappingToSourceVertices = false; - this._isClosed = false; - if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') { - var srcLine = arguments[0]; - var snapTolerance = arguments[1]; - LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance); - } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') { - var srcPts = arguments[0]; - var snapTolerance$1 = arguments[1]; - this._srcPts = srcPts; - this._isClosed = LineStringSnapper.isClosed(srcPts); - this._snapTolerance = snapTolerance$1; - } - }; - LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) { - var this$1 = this; - - var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size(); - for (var i = 0; i < end; i++) { - var srcPt = srcCoords.get(i); - var snapVert = this$1.findSnapForVertex(srcPt, snapPts); - if (snapVert !== null) { - srcCoords.set(i, new Coordinate(snapVert)); - if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); } - } - } - }; - LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) { - var this$1 = this; - - for (var i = 0; i < snapPts.length; i++) { - if (pt.equals2D(snapPts[i])) { return null } - if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] } - } - return null - }; - LineStringSnapper.prototype.snapTo = function snapTo (snapPts) { - var coordList = new CoordinateList(this._srcPts); - this.snapVertices(coordList, snapPts); - this.snapSegments(coordList, snapPts); - var newPts = coordList.toCoordinateArray(); - return newPts - }; - LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) { - var this$1 = this; - - if (snapPts.length === 0) { return null } - var distinctPtCount = snapPts.length; - if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; } - for (var i = 0; i < distinctPtCount; i++) { - var snapPt = snapPts[i]; - var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords); - if (index >= 0) { - srcCoords.add(index + 1, new Coordinate(snapPt), false); - } - } - }; - LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) { - var this$1 = this; - - var minDist = Double.MAX_VALUE; - var snapIndex = -1; - for (var i = 0; i < srcCoords.size() - 1; i++) { - this$1._seg.p0 = srcCoords.get(i); - this$1._seg.p1 = srcCoords.get(i + 1); - if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) { - if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 } - } - var dist = this$1._seg.distance(snapPt); - if (dist < this$1._snapTolerance && dist < minDist) { - minDist = dist; - snapIndex = i; - } - } - return snapIndex - }; - LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) { - this._allowSnappingToSourceVertices = allowSnappingToSourceVertices; - }; - LineStringSnapper.prototype.interfaces_ = function interfaces_ () { - return [] - }; - LineStringSnapper.prototype.getClass = function getClass () { - return LineStringSnapper - }; - LineStringSnapper.isClosed = function isClosed (pts) { - if (pts.length <= 1) { return false } - return pts[0].equals2D(pts[pts.length - 1]) - }; - - var GeometrySnapper = function GeometrySnapper (srcGeom) { - this._srcGeom = srcGeom || null; - }; - - var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } }; - GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) { - var snapPts = this.extractTargetCoordinates(snapGeom); - var snapTrans = new SnapTransformer(snapTolerance, snapPts); - return snapTrans.transform(this._srcGeom) - }; - GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) { - var snapPts = this.extractTargetCoordinates(this._srcGeom); - var snapTrans = new SnapTransformer(snapTolerance, snapPts, true); - var snappedGeom = snapTrans.transform(this._srcGeom); - var result = snappedGeom; - if (cleanResult && hasInterface(result, Polygonal)) { - result = snappedGeom.buffer(0); - } - return result - }; - GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) { - var minSegLen = this.computeMinimumSegmentLength(ringPts); - var snapTol = minSegLen / 10; - return snapTol - }; - GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) { - var ptSet = new TreeSet(); - var pts = g.getCoordinates(); - for (var i = 0; i < pts.length; i++) { - ptSet.add(pts[i]); - } - return ptSet.toArray(new Array(0).fill(null)) - }; - GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) { - var minSegLen = Double.MAX_VALUE; - for (var i = 0; i < pts.length - 1; i++) { - var segLen = pts[i].distance(pts[i + 1]); - if (segLen < minSegLen) { minSegLen = segLen; } - } - return minSegLen - }; - GeometrySnapper.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometrySnapper.prototype.getClass = function getClass () { - return GeometrySnapper - }; - GeometrySnapper.snap = function snap (g0, g1, snapTolerance) { - var snapGeom = new Array(2).fill(null); - var snapper0 = new GeometrySnapper(g0); - snapGeom[0] = snapper0.snapTo(g1, snapTolerance); - var snapper1 = new GeometrySnapper(g1); - snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance); - return snapGeom - }; - GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () { - if (arguments.length === 1) { - var g = arguments[0]; - var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g); - var pm = g.getPrecisionModel(); - if (pm.getType() === PrecisionModel.FIXED) { - var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415; - if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; } - } - return snapTolerance - } else if (arguments.length === 2) { - var g0 = arguments[0]; - var g1 = arguments[1]; - return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1)) - } - }; - GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) { - var env = g.getEnvelopeInternal(); - var minDimension = Math.min(env.getHeight(), env.getWidth()); - var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR; - return snapTol - }; - GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) { - var snapper0 = new GeometrySnapper(geom); - return snapper0.snapToSelf(snapTolerance, cleanResult) - }; - staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 }; - - Object.defineProperties( GeometrySnapper, staticAccessors$41 ); - - var SnapTransformer = (function (GeometryTransformer$$1) { - function SnapTransformer (snapTolerance, snapPts, isSelfSnap) { - GeometryTransformer$$1.call(this); - this._snapTolerance = snapTolerance || null; - this._snapPts = snapPts || null; - this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false; - } - - if ( GeometryTransformer$$1 ) SnapTransformer.__proto__ = GeometryTransformer$$1; - SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype ); - SnapTransformer.prototype.constructor = SnapTransformer; - SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) { - var snapper = new LineStringSnapper(srcPts, this._snapTolerance); - snapper.setAllowSnappingToSourceVertices(this._isSelfSnap); - return snapper.snapTo(snapPts) - }; - SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) { - var srcPts = coords.toCoordinateArray(); - var newPts = this.snapLine(srcPts, this._snapPts); - return this._factory.getCoordinateSequenceFactory().create(newPts) - }; - SnapTransformer.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SnapTransformer.prototype.getClass = function getClass () { - return SnapTransformer - }; - - return SnapTransformer; - }(GeometryTransformer)); - - var CommonBits = function CommonBits () { - this._isFirst = true; - this._commonMantissaBitsCount = 53; - this._commonBits = 0; - this._commonSignExp = null; - }; - CommonBits.prototype.getCommon = function getCommon () { - return Double.longBitsToDouble(this._commonBits) - }; - CommonBits.prototype.add = function add (num) { - var numBits = Double.doubleToLongBits(num); - if (this._isFirst) { - this._commonBits = numBits; - this._commonSignExp = CommonBits.signExpBits(this._commonBits); - this._isFirst = false; - return null - } - var numSignExp = CommonBits.signExpBits(numBits); - if (numSignExp !== this._commonSignExp) { - this._commonBits = 0; - return null - } - this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits); - this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount)); - }; - CommonBits.prototype.toString = function toString () { - if (arguments.length === 1) { - var bits = arguments[0]; - var x = Double.longBitsToDouble(bits); - var numStr = Double.toBinaryString(bits); - var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr; - var bitStr = padStr.substring(padStr.length - 64); - var str = bitStr.substring(0, 1) + ' ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]'; - return str - } - }; - CommonBits.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CommonBits.prototype.getClass = function getClass () { - return CommonBits - }; - CommonBits.getBit = function getBit (bits, i) { - var mask = 1 << i; - return (bits & mask) !== 0 ? 1 : 0 - }; - CommonBits.signExpBits = function signExpBits (num) { - return num >> 52 - }; - CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) { - var invMask = (1 << nBits) - 1; - var mask = ~invMask; - var zeroed = bits & mask; - return zeroed - }; - CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) { - var count = 0; - for (var i = 52; i >= 0; i--) { - if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count } - count++; - } - return 52 - }; - - var CommonBitsRemover = function CommonBitsRemover () { - this._commonCoord = null; - this._ccFilter = new CommonCoordinateFilter(); - }; - - var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } }; - CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) { - var trans = new Translater(this._commonCoord); - geom.apply(trans); - geom.geometryChanged(); - }; - CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) { - if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom } - var invCoord = new Coordinate(this._commonCoord); - invCoord.x = -invCoord.x; - invCoord.y = -invCoord.y; - var trans = new Translater(invCoord); - geom.apply(trans); - geom.geometryChanged(); - return geom - }; - CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () { - return this._commonCoord - }; - CommonBitsRemover.prototype.add = function add (geom) { - geom.apply(this._ccFilter); - this._commonCoord = this._ccFilter.getCommonCoordinate(); - }; - CommonBitsRemover.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CommonBitsRemover.prototype.getClass = function getClass () { - return CommonBitsRemover - }; - staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter }; - staticAccessors$42.Translater.get = function () { return Translater }; - - Object.defineProperties( CommonBitsRemover, staticAccessors$42 ); - - var CommonCoordinateFilter = function CommonCoordinateFilter () { - this._commonBitsX = new CommonBits(); - this._commonBitsY = new CommonBits(); - }; - CommonCoordinateFilter.prototype.filter = function filter (coord) { - this._commonBitsX.add(coord.x); - this._commonBitsY.add(coord.y); - }; - CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () { - return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon()) - }; - CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () { - return [CoordinateFilter] - }; - CommonCoordinateFilter.prototype.getClass = function getClass () { - return CommonCoordinateFilter - }; - - var Translater = function Translater () { - this.trans = null; - var trans = arguments[0]; - this.trans = trans; - }; - Translater.prototype.filter = function filter (seq, i) { - var xp = seq.getOrdinate(i, 0) + this.trans.x; - var yp = seq.getOrdinate(i, 1) + this.trans.y; - seq.setOrdinate(i, 0, xp); - seq.setOrdinate(i, 1, yp); - }; - Translater.prototype.isDone = function isDone () { - return false - }; - Translater.prototype.isGeometryChanged = function isGeometryChanged () { - return true - }; - Translater.prototype.interfaces_ = function interfaces_ () { - return [CoordinateSequenceFilter] - }; - Translater.prototype.getClass = function getClass () { - return Translater - }; - - var SnapOverlayOp = function SnapOverlayOp (g1, g2) { - this._geom = new Array(2).fill(null); - this._snapTolerance = null; - this._cbr = null; - this._geom[0] = g1; - this._geom[1] = g2; - this.computeSnapTolerance(); - }; - SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) { - var snapper0 = new GeometrySnapper(geom); - var snapGeom = snapper0.snapTo(geom, this._snapTolerance); - return snapGeom - }; - SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) { - this._cbr = new CommonBitsRemover(); - this._cbr.add(geom[0]); - this._cbr.add(geom[1]); - var remGeom = new Array(2).fill(null); - remGeom[0] = this._cbr.removeCommonBits(geom[0].copy()); - remGeom[1] = this._cbr.removeCommonBits(geom[1].copy()); - return remGeom - }; - SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) { - this._cbr.addCommonBits(geom); - return geom - }; - SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) { - var prepGeom = this.snap(this._geom); - var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode); - return this.prepareResult(result) - }; - SnapOverlayOp.prototype.checkValid = function checkValid (g) { - if (!g.isValid()) { - System.out.println('Snapped geometry is invalid'); - } - }; - SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () { - this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]); - }; - SnapOverlayOp.prototype.snap = function snap (geom) { - var remGeom = this.removeCommonBits(geom); - var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance); - return snapGeom - }; - SnapOverlayOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SnapOverlayOp.prototype.getClass = function getClass () { - return SnapOverlayOp - }; - SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) { - var op = new SnapOverlayOp(g0, g1); - return op.getResultGeometry(opCode) - }; - SnapOverlayOp.union = function union (g0, g1) { - return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION) - }; - SnapOverlayOp.intersection = function intersection (g0, g1) { - return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION) - }; - SnapOverlayOp.symDifference = function symDifference (g0, g1) { - return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE) - }; - SnapOverlayOp.difference = function difference (g0, g1) { - return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE) - }; - - var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) { - this._geom = new Array(2).fill(null); - this._geom[0] = g1; - this._geom[1] = g2; - }; - SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) { - var result = null; - var isSuccess = false; - var savedException = null; - try { - result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode); - var isValid = true; - if (isValid) { isSuccess = true; } - } catch (ex) { - if (ex instanceof RuntimeException) { - savedException = ex; - } else { throw ex } - } finally {} - if (!isSuccess) { - try { - result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode); - } catch (ex) { - if (ex instanceof RuntimeException) { - throw savedException - } else { throw ex } - } finally {} - } - return result - }; - SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SnapIfNeededOverlayOp.prototype.getClass = function getClass () { - return SnapIfNeededOverlayOp - }; - SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) { - var op = new SnapIfNeededOverlayOp(g0, g1); - return op.getResultGeometry(opCode) - }; - SnapIfNeededOverlayOp.union = function union (g0, g1) { - return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION) - }; - SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) { - return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION) - }; - SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) { - return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE) - }; - SnapIfNeededOverlayOp.difference = function difference (g0, g1) { - return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE) - }; - - var MonotoneChain$2 = function MonotoneChain () { - this.mce = null; - this.chainIndex = null; - var mce = arguments[0]; - var chainIndex = arguments[1]; - this.mce = mce; - this.chainIndex = chainIndex; - }; - MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) { - this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si); - }; - MonotoneChain$2.prototype.interfaces_ = function interfaces_ () { - return [] - }; - MonotoneChain$2.prototype.getClass = function getClass () { - return MonotoneChain$2 - }; - - var SweepLineEvent = function SweepLineEvent () { - this._label = null; - this._xValue = null; - this._eventType = null; - this._insertEvent = null; - this._deleteEventIndex = null; - this._obj = null; - if (arguments.length === 2) { - var x = arguments[0]; - var insertEvent = arguments[1]; - this._eventType = SweepLineEvent.DELETE; - this._xValue = x; - this._insertEvent = insertEvent; - } else if (arguments.length === 3) { - var label = arguments[0]; - var x$1 = arguments[1]; - var obj = arguments[2]; - this._eventType = SweepLineEvent.INSERT; - this._label = label; - this._xValue = x$1; - this._obj = obj; - } - }; - - var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } }; - SweepLineEvent.prototype.isDelete = function isDelete () { - return this._eventType === SweepLineEvent.DELETE - }; - SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) { - this._deleteEventIndex = deleteEventIndex; - }; - SweepLineEvent.prototype.getObject = function getObject () { - return this._obj - }; - SweepLineEvent.prototype.compareTo = function compareTo (o) { - var pe = o; - if (this._xValue < pe._xValue) { return -1 } - if (this._xValue > pe._xValue) { return 1 } - if (this._eventType < pe._eventType) { return -1 } - if (this._eventType > pe._eventType) { return 1 } - return 0 - }; - SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () { - return this._insertEvent - }; - SweepLineEvent.prototype.isInsert = function isInsert () { - return this._eventType === SweepLineEvent.INSERT - }; - SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) { - if (this._label === null) { return false } - return this._label === ev._label - }; - SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () { - return this._deleteEventIndex - }; - SweepLineEvent.prototype.interfaces_ = function interfaces_ () { - return [Comparable] - }; - SweepLineEvent.prototype.getClass = function getClass () { - return SweepLineEvent - }; - staticAccessors$43.INSERT.get = function () { return 1 }; - staticAccessors$43.DELETE.get = function () { return 2 }; - - Object.defineProperties( SweepLineEvent, staticAccessors$43 ); - - var EdgeSetIntersector = function EdgeSetIntersector () {}; - - EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () { - return [] - }; - EdgeSetIntersector.prototype.getClass = function getClass () { - return EdgeSetIntersector - }; - - var SegmentIntersector$2 = function SegmentIntersector () { - this._hasIntersection = false; - this._hasProper = false; - this._hasProperInterior = false; - this._properIntersectionPoint = null; - this._li = null; - this._includeProper = null; - this._recordIsolated = null; - this._isSelfIntersection = null; - this._numIntersections = 0; - this.numTests = 0; - this._bdyNodes = null; - this._isDone = false; - this._isDoneWhenProperInt = false; - var li = arguments[0]; - var includeProper = arguments[1]; - var recordIsolated = arguments[2]; - this._li = li; - this._includeProper = includeProper; - this._recordIsolated = recordIsolated; - }; - SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) { - if (e0 === e1) { - if (this._li.getIntersectionNum() === 1) { - if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true } - if (e0.isClosed()) { - var maxSegIndex = e0.getNumPoints() - 1; - if ((segIndex0 === 0 && segIndex1 === maxSegIndex) || - (segIndex1 === 0 && segIndex0 === maxSegIndex)) { - return true - } - } - } - } - return false - }; - SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () { - return this._properIntersectionPoint - }; - SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) { - this._isDoneWhenProperInt = isDoneWhenProperInt; - }; - SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () { - return this._hasProperInterior - }; - SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) { - for (var i = bdyNodes.iterator(); i.hasNext();) { - var node = i.next(); - var pt = node.getCoordinate(); - if (li.isIntersection(pt)) { return true } - } - return false - }; - SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () { - return this._hasProper - }; - SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () { - return this._hasIntersection - }; - SegmentIntersector$2.prototype.isDone = function isDone () { - return this._isDone - }; - SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) { - if (bdyNodes === null) { return false } - if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true } - if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true } - return false - }; - SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) { - this._bdyNodes = new Array(2).fill(null); - this._bdyNodes[0] = bdyNodes0; - this._bdyNodes[1] = bdyNodes1; - }; - SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) { - if (e0 === e1 && segIndex0 === segIndex1) { return null } - this.numTests++; - var p00 = e0.getCoordinates()[segIndex0]; - var p01 = e0.getCoordinates()[segIndex0 + 1]; - var p10 = e1.getCoordinates()[segIndex1]; - var p11 = e1.getCoordinates()[segIndex1 + 1]; - this._li.computeIntersection(p00, p01, p10, p11); - if (this._li.hasIntersection()) { - if (this._recordIsolated) { - e0.setIsolated(false); - e1.setIsolated(false); - } - this._numIntersections++; - if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) { - this._hasIntersection = true; - if (this._includeProper || !this._li.isProper()) { - e0.addIntersections(this._li, segIndex0, 0); - e1.addIntersections(this._li, segIndex1, 1); - } - if (this._li.isProper()) { - this._properIntersectionPoint = this._li.getIntersection(0).copy(); - this._hasProper = true; - if (this._isDoneWhenProperInt) { - this._isDone = true; - } - if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; } - } - } - } - }; - SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SegmentIntersector$2.prototype.getClass = function getClass () { - return SegmentIntersector$2 - }; - SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) { - return Math.abs(i1 - i2) === 1 - }; - - var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) { - function SimpleMCSweepLineIntersector () { - EdgeSetIntersector$$1.call(this); - this.events = new ArrayList(); - this.nOverlaps = null; - } - - if ( EdgeSetIntersector$$1 ) SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1; - SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype ); - SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector; - SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () { - var this$1 = this; - - Collections.sort(this.events); - for (var i = 0; i < this.events.size(); i++) { - var ev = this$1.events.get(i); - if (ev.isDelete()) { - ev.getInsertEvent().setDeleteEventIndex(i); - } - } - }; - SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () { - var this$1 = this; - - if (arguments.length === 1) { - var si = arguments[0]; - this.nOverlaps = 0; - this.prepareEvents(); - for (var i = 0; i < this.events.size(); i++) { - var ev = this$1.events.get(i); - if (ev.isInsert()) { - this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si); - } - if (si.isDone()) { - break - } - } - } else if (arguments.length === 3) { - if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) { - var edges0 = arguments[0]; - var edges1 = arguments[1]; - var si$1 = arguments[2]; - this.addEdges(edges0, edges0); - this.addEdges(edges1, edges1); - this.computeIntersections(si$1); - } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) { - var edges = arguments[0]; - var si$2 = arguments[1]; - var testAllSegments = arguments[2]; - if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); } - this.computeIntersections(si$2); - } - } - }; - SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) { - var this$1 = this; - - var mce = edge.getMonotoneChainEdge(); - var startIndex = mce.getStartIndexes(); - for (var i = 0; i < startIndex.length - 1; i++) { - var mc = new MonotoneChain$2(mce, i); - var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc); - this$1.events.add(insertEvent); - this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent)); - } - }; - SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) { - var this$1 = this; - - var mc0 = ev0.getObject(); - for (var i = start; i < end; i++) { - var ev1 = this$1.events.get(i); - if (ev1.isInsert()) { - var mc1 = ev1.getObject(); - if (!ev0.isSameLabel(ev1)) { - mc0.computeIntersections(mc1, si); - this$1.nOverlaps++; - } - } - } - }; - SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () { - var this$1 = this; - - if (arguments.length === 1) { - var edges = arguments[0]; - for (var i = edges.iterator(); i.hasNext();) { - var edge = i.next(); - this$1.addEdge(edge, edge); - } - } else if (arguments.length === 2) { - var edges$1 = arguments[0]; - var edgeSet = arguments[1]; - for (var i$1 = edges$1.iterator(); i$1.hasNext();) { - var edge$1 = i$1.next(); - this$1.addEdge(edge$1, edgeSet); - } - } - }; - SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SimpleMCSweepLineIntersector.prototype.getClass = function getClass () { - return SimpleMCSweepLineIntersector - }; - - return SimpleMCSweepLineIntersector; - }(EdgeSetIntersector)); - - var IntervalRTreeNode = function IntervalRTreeNode () { - this._min = Double.POSITIVE_INFINITY; - this._max = Double.NEGATIVE_INFINITY; - }; - - var staticAccessors$45 = { NodeComparator: { configurable: true } }; - IntervalRTreeNode.prototype.getMin = function getMin () { - return this._min - }; - IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) { - if (this._min > queryMax || this._max < queryMin) { return false } - return true - }; - IntervalRTreeNode.prototype.getMax = function getMax () { - return this._max - }; - IntervalRTreeNode.prototype.toString = function toString () { - return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0)) - }; - IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () { - return [] - }; - IntervalRTreeNode.prototype.getClass = function getClass () { - return IntervalRTreeNode - }; - staticAccessors$45.NodeComparator.get = function () { return NodeComparator }; - - Object.defineProperties( IntervalRTreeNode, staticAccessors$45 ); - - var NodeComparator = function NodeComparator () {}; - - NodeComparator.prototype.compare = function compare (o1, o2) { - var n1 = o1; - var n2 = o2; - var mid1 = (n1._min + n1._max) / 2; - var mid2 = (n2._min + n2._max) / 2; - if (mid1 < mid2) { return -1 } - if (mid1 > mid2) { return 1 } - return 0 - }; - NodeComparator.prototype.interfaces_ = function interfaces_ () { - return [Comparator] - }; - NodeComparator.prototype.getClass = function getClass () { - return NodeComparator - }; - - var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) { - function IntervalRTreeLeafNode () { - IntervalRTreeNode$$1.call(this); - this._item = null; - var min = arguments[0]; - var max = arguments[1]; - var item = arguments[2]; - this._min = min; - this._max = max; - this._item = item; - } - - if ( IntervalRTreeNode$$1 ) IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1; - IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype ); - IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode; - IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) { - if (!this.intersects(queryMin, queryMax)) { return null } - visitor.visitItem(this._item); - }; - IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () { - return [] - }; - IntervalRTreeLeafNode.prototype.getClass = function getClass () { - return IntervalRTreeLeafNode - }; - - return IntervalRTreeLeafNode; - }(IntervalRTreeNode)); - - var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) { - function IntervalRTreeBranchNode () { - IntervalRTreeNode$$1.call(this); - this._node1 = null; - this._node2 = null; - var n1 = arguments[0]; - var n2 = arguments[1]; - this._node1 = n1; - this._node2 = n2; - this.buildExtent(this._node1, this._node2); - } - - if ( IntervalRTreeNode$$1 ) IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1; - IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype ); - IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode; - IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) { - this._min = Math.min(n1._min, n2._min); - this._max = Math.max(n1._max, n2._max); - }; - IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) { - if (!this.intersects(queryMin, queryMax)) { - return null - } - if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); } - if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); } - }; - IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () { - return [] - }; - IntervalRTreeBranchNode.prototype.getClass = function getClass () { - return IntervalRTreeBranchNode - }; - - return IntervalRTreeBranchNode; - }(IntervalRTreeNode)); - - var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () { - this._leaves = new ArrayList(); - this._root = null; - this._level = 0; - }; - SortedPackedIntervalRTree.prototype.buildTree = function buildTree () { - var this$1 = this; - - Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator()); - var src = this._leaves; - var temp = null; - var dest = new ArrayList(); - while (true) { - this$1.buildLevel(src, dest); - if (dest.size() === 1) { return dest.get(0) } - temp = src; - src = dest; - dest = temp; - } - }; - SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) { - if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') } - this._leaves.add(new IntervalRTreeLeafNode(min, max, item)); - }; - SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) { - this.init(); - this._root.query(min, max, visitor); - }; - SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () { - if (this._root !== null) { return null } - this._root = this.buildTree(); - }; - SortedPackedIntervalRTree.prototype.printNode = function printNode (node) { - System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level))); - }; - SortedPackedIntervalRTree.prototype.init = function init () { - if (this._root !== null) { return null } - this.buildRoot(); - }; - SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) { - this._level++; - dest.clear(); - for (var i = 0; i < src.size(); i += 2) { - var n1 = src.get(i); - var n2 = i + 1 < src.size() ? src.get(i) : null; - if (n2 === null) { - dest.add(n1); - } else { - var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1)); - dest.add(node); - } - } - }; - SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () { - return [] - }; - SortedPackedIntervalRTree.prototype.getClass = function getClass () { - return SortedPackedIntervalRTree - }; - - var ArrayListVisitor = function ArrayListVisitor () { - this._items = new ArrayList(); - }; - ArrayListVisitor.prototype.visitItem = function visitItem (item) { - this._items.add(item); - }; - ArrayListVisitor.prototype.getItems = function getItems () { - return this._items - }; - ArrayListVisitor.prototype.interfaces_ = function interfaces_ () { - return [ItemVisitor] - }; - ArrayListVisitor.prototype.getClass = function getClass () { - return ArrayListVisitor - }; - - var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () { - this._index = null; - var g = arguments[0]; - if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') } - this._index = new IntervalIndexedGeometry(g); - }; - - var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } }; - IndexedPointInAreaLocator.prototype.locate = function locate (p) { - var rcc = new RayCrossingCounter(p); - var visitor = new SegmentVisitor(rcc); - this._index.query(p.y, p.y, visitor); - return rcc.getLocation() - }; - IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () { - return [PointOnGeometryLocator] - }; - IndexedPointInAreaLocator.prototype.getClass = function getClass () { - return IndexedPointInAreaLocator - }; - staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor }; - staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry }; - - Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 ); - - var SegmentVisitor = function SegmentVisitor () { - this._counter = null; - var counter = arguments[0]; - this._counter = counter; - }; - SegmentVisitor.prototype.visitItem = function visitItem (item) { - var seg = item; - this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1)); - }; - SegmentVisitor.prototype.interfaces_ = function interfaces_ () { - return [ItemVisitor] - }; - SegmentVisitor.prototype.getClass = function getClass () { - return SegmentVisitor - }; - - var IntervalIndexedGeometry = function IntervalIndexedGeometry () { - this._index = new SortedPackedIntervalRTree(); - var geom = arguments[0]; - this.init(geom); - }; - IntervalIndexedGeometry.prototype.init = function init (geom) { - var this$1 = this; - - var lines = LinearComponentExtracter.getLines(geom); - for (var i = lines.iterator(); i.hasNext();) { - var line = i.next(); - var pts = line.getCoordinates(); - this$1.addLine(pts); - } - }; - IntervalIndexedGeometry.prototype.addLine = function addLine (pts) { - var this$1 = this; - - for (var i = 1; i < pts.length; i++) { - var seg = new LineSegment(pts[i - 1], pts[i]); - var min = Math.min(seg.p0.y, seg.p1.y); - var max = Math.max(seg.p0.y, seg.p1.y); - this$1._index.insert(min, max, seg); - } - }; - IntervalIndexedGeometry.prototype.query = function query () { - if (arguments.length === 2) { - var min = arguments[0]; - var max = arguments[1]; - var visitor = new ArrayListVisitor(); - this._index.query(min, max, visitor); - return visitor.getItems() - } else if (arguments.length === 3) { - var min$1 = arguments[0]; - var max$1 = arguments[1]; - var visitor$1 = arguments[2]; - this._index.query(min$1, max$1, visitor$1); - } - }; - IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () { - return [] - }; - IntervalIndexedGeometry.prototype.getClass = function getClass () { - return IntervalIndexedGeometry - }; - - var GeometryGraph = (function (PlanarGraph$$1) { - function GeometryGraph () { - PlanarGraph$$1.call(this); - this._parentGeom = null; - this._lineEdgeMap = new HashMap(); - this._boundaryNodeRule = null; - this._useBoundaryDeterminationRule = true; - this._argIndex = null; - this._boundaryNodes = null; - this._hasTooFewPoints = false; - this._invalidPoint = null; - this._areaPtLocator = null; - this._ptLocator = new PointLocator(); - if (arguments.length === 2) { - var argIndex = arguments[0]; - var parentGeom = arguments[1]; - var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE; - this._argIndex = argIndex; - this._parentGeom = parentGeom; - this._boundaryNodeRule = boundaryNodeRule; - if (parentGeom !== null) { - this.add(parentGeom); - } - } else if (arguments.length === 3) { - var argIndex$1 = arguments[0]; - var parentGeom$1 = arguments[1]; - var boundaryNodeRule$1 = arguments[2]; - this._argIndex = argIndex$1; - this._parentGeom = parentGeom$1; - this._boundaryNodeRule = boundaryNodeRule$1; - if (parentGeom$1 !== null) { - this.add(parentGeom$1); - } - } - } - - if ( PlanarGraph$$1 ) GeometryGraph.__proto__ = PlanarGraph$$1; - GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype ); - GeometryGraph.prototype.constructor = GeometryGraph; - GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) { - var n = this._nodes.addNode(coord); - var lbl = n.getLabel(); - var boundaryCount = 1; - var loc = Location.NONE; - loc = lbl.getLocation(argIndex, Position.ON); - if (loc === Location.BOUNDARY) { boundaryCount++; } - var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount); - lbl.setLocation(argIndex, newLoc); - }; - GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () { - if (arguments.length === 2) { - var li = arguments[0]; - var computeRingSelfNodes = arguments[1]; - return this.computeSelfNodes(li, computeRingSelfNodes, false) - } else if (arguments.length === 3) { - var li$1 = arguments[0]; - var computeRingSelfNodes$1 = arguments[1]; - var isDoneIfProperInt = arguments[2]; - var si = new SegmentIntersector$2(li$1, true, false); - si.setIsDoneIfProperInt(isDoneIfProperInt); - var esi = this.createEdgeSetIntersector(); - var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon; - var computeAllSegments = computeRingSelfNodes$1 || !isRings; - esi.computeIntersections(this._edges, si, computeAllSegments); - this.addSelfIntersectionNodes(this._argIndex); - return si - } - }; - GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) { - for (var i = this._edges.iterator(); i.hasNext();) { - var e = i.next(); - e.eiList.addSplitEdges(edgelist); - } - }; - GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) { - var si = new SegmentIntersector$2(li, includeProper, true); - si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes()); - var esi = this.createEdgeSetIntersector(); - esi.computeIntersections(this._edges, g._edges, si); - return si - }; - GeometryGraph.prototype.getGeometry = function getGeometry () { - return this._parentGeom - }; - GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () { - return this._boundaryNodeRule - }; - GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () { - return this._hasTooFewPoints - }; - GeometryGraph.prototype.addPoint = function addPoint () { - if (arguments[0] instanceof Point$1) { - var p = arguments[0]; - var coord = p.getCoordinate(); - this.insertPoint(this._argIndex, coord, Location.INTERIOR); - } else if (arguments[0] instanceof Coordinate) { - var pt = arguments[0]; - this.insertPoint(this._argIndex, pt, Location.INTERIOR); - } - }; - GeometryGraph.prototype.addPolygon = function addPolygon (p) { - var this$1 = this; - - this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR); - for (var i = 0; i < p.getNumInteriorRing(); i++) { - var hole = p.getInteriorRingN(i); - this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR); - } - }; - GeometryGraph.prototype.addEdge = function addEdge (e) { - this.insertEdge(e); - var coord = e.getCoordinates(); - this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY); - this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY); - }; - GeometryGraph.prototype.addLineString = function addLineString (line) { - var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates()); - if (coord.length < 2) { - this._hasTooFewPoints = true; - this._invalidPoint = coord[0]; - return null - } - var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR)); - this._lineEdgeMap.put(line, e); - this.insertEdge(e); - Assert.isTrue(coord.length >= 2, 'found LineString with single point'); - this.insertBoundaryPoint(this._argIndex, coord[0]); - this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]); - }; - GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () { - return this._invalidPoint - }; - GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () { - var coll = this.getBoundaryNodes(); - var pts = new Array(coll.size()).fill(null); - var i = 0; - for (var it = coll.iterator(); it.hasNext();) { - var node = it.next(); - pts[i++] = node.getCoordinate().copy(); - } - return pts - }; - GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () { - if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); } - return this._boundaryNodes - }; - GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) { - if (this.isBoundaryNode(argIndex, coord)) { return null } - if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); } - }; - GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) { - if (lr.isEmpty()) { return null } - var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates()); - if (coord.length < 4) { - this._hasTooFewPoints = true; - this._invalidPoint = coord[0]; - return null - } - var left = cwLeft; - var right = cwRight; - if (CGAlgorithms.isCCW(coord)) { - left = cwRight; - right = cwLeft; - } - var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right)); - this._lineEdgeMap.put(lr, e); - this.insertEdge(e); - this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY); - }; - GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) { - var n = this._nodes.addNode(coord); - var lbl = n.getLabel(); - if (lbl === null) { - n._label = new Label(argIndex, onLocation); - } else { lbl.setLocation(argIndex, onLocation); } - }; - GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () { - return new SimpleMCSweepLineIntersector() - }; - GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) { - var this$1 = this; - - for (var i = this._edges.iterator(); i.hasNext();) { - var e = i.next(); - var eLoc = e.getLabel().getLocation(argIndex); - for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) { - var ei = eiIt.next(); - this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc); - } - } - }; - GeometryGraph.prototype.add = function add () { - if (arguments.length === 1) { - var g = arguments[0]; - if (g.isEmpty()) { return null } - if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; } - if (g instanceof Polygon) { this.addPolygon(g); } - else if (g instanceof LineString) { this.addLineString(g); } - else if (g instanceof Point$1) { this.addPoint(g); } - else if (g instanceof MultiPoint) { this.addCollection(g); } - else if (g instanceof MultiLineString) { this.addCollection(g); } - else if (g instanceof MultiPolygon) { this.addCollection(g); } - else if (g instanceof GeometryCollection) { this.addCollection(g); } - else { throw new Error(g.getClass().getName()) } - } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) } - }; - GeometryGraph.prototype.addCollection = function addCollection (gc) { - var this$1 = this; - - for (var i = 0; i < gc.getNumGeometries(); i++) { - var g = gc.getGeometryN(i); - this$1.add(g); - } - }; - GeometryGraph.prototype.locate = function locate (pt) { - if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) { - if (this._areaPtLocator === null) { - this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom); - } - return this._areaPtLocator.locate(pt) - } - return this._ptLocator.locate(pt, this._parentGeom) - }; - GeometryGraph.prototype.findEdge = function findEdge () { - if (arguments.length === 1) { - var line = arguments[0]; - return this._lineEdgeMap.get(line) - } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) } - }; - GeometryGraph.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryGraph.prototype.getClass = function getClass () { - return GeometryGraph - }; - GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) { - return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR - }; - - return GeometryGraph; - }(PlanarGraph)); - - var GeometryGraphOp = function GeometryGraphOp () { - this._li = new RobustLineIntersector(); - this._resultPrecisionModel = null; - this._arg = null; - if (arguments.length === 1) { - var g0 = arguments[0]; - this.setComputationPrecision(g0.getPrecisionModel()); - this._arg = new Array(1).fill(null); - this._arg[0] = new GeometryGraph(0, g0); - } else if (arguments.length === 2) { - var g0$1 = arguments[0]; - var g1 = arguments[1]; - var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE; - if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); } - this._arg = new Array(2).fill(null); - this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule); - this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule); - } else if (arguments.length === 3) { - var g0$2 = arguments[0]; - var g1$1 = arguments[1]; - var boundaryNodeRule$1 = arguments[2]; - if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); } - this._arg = new Array(2).fill(null); - this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1); - this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1); - } - }; - GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) { - return this._arg[i].getGeometry() - }; - GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) { - this._resultPrecisionModel = pm; - this._li.setPrecisionModel(this._resultPrecisionModel); - }; - GeometryGraphOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryGraphOp.prototype.getClass = function getClass () { - return GeometryGraphOp - }; - - // operation.geometrygraph - - var GeometryMapper = function GeometryMapper () {}; - - GeometryMapper.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryMapper.prototype.getClass = function getClass () { - return GeometryMapper - }; - GeometryMapper.map = function map () { - if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) { - var geom = arguments[0]; - var op = arguments[1]; - var mapped = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var g = op.map(geom.getGeometryN(i)); - if (g !== null) { mapped.add(g); } - } - return geom.getFactory().buildGeometry(mapped) - } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) { - var geoms = arguments[0]; - var op$1 = arguments[1]; - var mapped$1 = new ArrayList(); - for (var i$1 = geoms.iterator(); i$1.hasNext();) { - var g$1 = i$1.next(); - var gr = op$1.map(g$1); - if (gr !== null) { mapped$1.add(gr); } - } - return mapped$1 - } - }; - GeometryMapper.MapOp = function MapOp () {}; - - var OverlayOp = (function (GeometryGraphOp) { - function OverlayOp () { - var g0 = arguments[0]; - var g1 = arguments[1]; - GeometryGraphOp.call(this, g0, g1); - this._ptLocator = new PointLocator(); - this._geomFact = null; - this._resultGeom = null; - this._graph = null; - this._edgeList = new EdgeList(); - this._resultPolyList = new ArrayList(); - this._resultLineList = new ArrayList(); - this._resultPointList = new ArrayList(); - this._graph = new PlanarGraph(new OverlayNodeFactory()); - this._geomFact = g0.getFactory(); - } - - if ( GeometryGraphOp ) OverlayOp.__proto__ = GeometryGraphOp; - OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype ); - OverlayOp.prototype.constructor = OverlayOp; - OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) { - var existingEdge = this._edgeList.findEqualEdge(e); - if (existingEdge !== null) { - var existingLabel = existingEdge.getLabel(); - var labelToMerge = e.getLabel(); - if (!existingEdge.isPointwiseEqual(e)) { - labelToMerge = new Label(e.getLabel()); - labelToMerge.flip(); - } - var depth = existingEdge.getDepth(); - if (depth.isNull()) { - depth.add(existingLabel); - } - depth.add(labelToMerge); - existingLabel.merge(labelToMerge); - } else { - this._edgeList.add(e); - } - }; - OverlayOp.prototype.getGraph = function getGraph () { - return this._graph - }; - OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () { - for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) { - var de = it.next(); - var sym = de.getSym(); - if (de.isInResult() && sym.isInResult()) { - de.setInResult(false); - sym.setInResult(false); - } - } - }; - OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) { - if (this.isCovered(coord, this._resultLineList)) { return true } - if (this.isCovered(coord, this._resultPolyList)) { return true } - return false - }; - OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) { - var geomList = new ArrayList(); - geomList.addAll(resultPointList); - geomList.addAll(resultLineList); - geomList.addAll(resultPolyList); - if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) } - return this._geomFact.buildGeometry(geomList) - }; - OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () { - for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().mergeSymLabels(); - } - }; - OverlayOp.prototype.isCovered = function isCovered (coord, geomList) { - var this$1 = this; - - for (var it = geomList.iterator(); it.hasNext();) { - var geom = it.next(); - var loc = this$1._ptLocator.locate(coord, geom); - if (loc !== Location.EXTERIOR) { return true } - } - return false - }; - OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () { - var newEdges = new ArrayList(); - for (var it = this._edgeList.iterator(); it.hasNext();) { - var e = it.next(); - if (e.isCollapsed()) { - it.remove(); - newEdges.add(e.getCollapsedEdge()); - } - } - this._edgeList.addAll(newEdges); - }; - OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () { - for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - var lbl = node.getEdges().getLabel(); - node.getLabel().merge(lbl); - } - }; - OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) { - this.computeOverlay(overlayOpCode); - return this._resultGeom - }; - OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) { - var this$1 = this; - - for (var i = edges.iterator(); i.hasNext();) { - var e = i.next(); - this$1.insertUniqueEdge(e); - } - }; - OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) { - this.copyPoints(0); - this.copyPoints(1); - this._arg[0].computeSelfNodes(this._li, false); - this._arg[1].computeSelfNodes(this._li, false); - this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true); - var baseSplitEdges = new ArrayList(); - this._arg[0].computeSplitEdges(baseSplitEdges); - this._arg[1].computeSplitEdges(baseSplitEdges); - // const splitEdges = baseSplitEdges - this.insertUniqueEdges(baseSplitEdges); - this.computeLabelsFromDepths(); - this.replaceCollapsedEdges(); - EdgeNodingValidator.checkValid(this._edgeList.getEdges()); - this._graph.addEdges(this._edgeList.getEdges()); - this.computeLabelling(); - this.labelIncompleteNodes(); - this.findResultAreaEdges(opCode); - this.cancelDuplicateResultEdges(); - var polyBuilder = new PolygonBuilder(this._geomFact); - polyBuilder.add(this._graph); - this._resultPolyList = polyBuilder.getPolygons(); - var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator); - this._resultLineList = lineBuilder.build(opCode); - var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator); - this._resultPointList = pointBuilder.build(opCode); - this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode); - }; - OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) { - var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry()); - n.getLabel().setLocation(targetIndex, loc); - }; - OverlayOp.prototype.copyPoints = function copyPoints (argIndex) { - var this$1 = this; - - for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) { - var graphNode = i.next(); - var newNode = this$1._graph.addNode(graphNode.getCoordinate()); - newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex)); - } - }; - OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) { - for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) { - var de = it.next(); - var label = de.getLabel(); - if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) { - de.setInResult(true); - } - } - }; - OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () { - for (var it = this._edgeList.iterator(); it.hasNext();) { - var e = it.next(); - var lbl = e.getLabel(); - var depth = e.getDepth(); - if (!depth.isNull()) { - depth.normalize(); - for (var i = 0; i < 2; i++) { - if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) { - if (depth.getDelta(i) === 0) { - lbl.toLine(i); - } else { - Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized'); - lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT)); - Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized'); - lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT)); - } - } - } - } - } - }; - OverlayOp.prototype.computeLabelling = function computeLabelling () { - var this$1 = this; - - for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) { - var node = nodeit.next(); - node.getEdges().computeLabelling(this$1._arg); - } - this.mergeSymLabels(); - this.updateNodeLabelling(); - }; - OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () { - var this$1 = this; - - // let nodeCount = 0 - for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) { - var n = ni.next(); - var label = n.getLabel(); - if (n.isIsolated()) { - // nodeCount++ - if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); } - } - n.getEdges().updateLabelling(label); - } - }; - OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) { - if (this.isCovered(coord, this._resultPolyList)) { return true } - return false - }; - OverlayOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OverlayOp.prototype.getClass = function getClass () { - return OverlayOp - }; - - return OverlayOp; - }(GeometryGraphOp)); - - OverlayOp.overlayOp = function (geom0, geom1, opCode) { - var gov = new OverlayOp(geom0, geom1); - var geomOv = gov.getResultGeometry(opCode); - return geomOv - }; - OverlayOp.intersection = function (g, other) { - if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) } - if (g.isGeometryCollection()) { - var g2 = other; - return GeometryCollectionMapper.map(g, { - interfaces_: function () { - return [GeometryMapper.MapOp] - }, - map: function (g) { - return g.intersection(g2) - } - }) - } - g.checkNotGeometryCollection(g); - g.checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION) - }; - OverlayOp.symDifference = function (g, other) { - if (g.isEmpty() || other.isEmpty()) { - if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) } - if (g.isEmpty()) { return other.copy() } - if (other.isEmpty()) { return g.copy() } - } - g.checkNotGeometryCollection(g); - g.checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE) - }; - OverlayOp.resultDimension = function (opCode, g0, g1) { - var dim0 = g0.getDimension(); - var dim1 = g1.getDimension(); - var resultDimension = -1; - switch (opCode) { - case OverlayOp.INTERSECTION: - resultDimension = Math.min(dim0, dim1); - break - case OverlayOp.UNION: - resultDimension = Math.max(dim0, dim1); - break - case OverlayOp.DIFFERENCE: - resultDimension = dim0; - break - case OverlayOp.SYMDIFFERENCE: - resultDimension = Math.max(dim0, dim1); - break - } - return resultDimension - }; - OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) { - var result = null; - switch (OverlayOp.resultDimension(overlayOpCode, a, b)) { - case -1: - result = geomFact.createGeometryCollection(new Array(0).fill(null)); - break - case 0: - result = geomFact.createPoint(); - break - case 1: - result = geomFact.createLineString(); - break - case 2: - result = geomFact.createPolygon(); - break - } - return result - }; - OverlayOp.difference = function (g, other) { - if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) } - if (other.isEmpty()) { return g.copy() } - g.checkNotGeometryCollection(g); - g.checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE) - }; - OverlayOp.isResultOfOp = function () { - if (arguments.length === 2) { - var label = arguments[0]; - var opCode = arguments[1]; - var loc0 = label.getLocation(0); - var loc1 = label.getLocation(1); - return OverlayOp.isResultOfOp(loc0, loc1, opCode) - } else if (arguments.length === 3) { - var loc0$1 = arguments[0]; - var loc1$1 = arguments[1]; - var overlayOpCode = arguments[2]; - if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; } - if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; } - switch (overlayOpCode) { - case OverlayOp.INTERSECTION: - return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR - case OverlayOp.UNION: - return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR - case OverlayOp.DIFFERENCE: - return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR - case OverlayOp.SYMDIFFERENCE: - return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR) - } - return false - } - }; - OverlayOp.INTERSECTION = 1; - OverlayOp.UNION = 2; - OverlayOp.DIFFERENCE = 3; - OverlayOp.SYMDIFFERENCE = 4; - - var FuzzyPointLocator = function FuzzyPointLocator () { - this._g = null; - this._boundaryDistanceTolerance = null; - this._linework = null; - this._ptLocator = new PointLocator(); - this._seg = new LineSegment(); - var g = arguments[0]; - var boundaryDistanceTolerance = arguments[1]; - this._g = g; - this._boundaryDistanceTolerance = boundaryDistanceTolerance; - this._linework = this.extractLinework(g); - }; - FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) { - var this$1 = this; - - for (var i = 0; i < this._linework.getNumGeometries(); i++) { - var line = this$1._linework.getGeometryN(i); - var seq = line.getCoordinateSequence(); - for (var j = 0; j < seq.size() - 1; j++) { - seq.getCoordinate(j, this$1._seg.p0); - seq.getCoordinate(j + 1, this$1._seg.p1); - var dist = this$1._seg.distance(pt); - if (dist <= this$1._boundaryDistanceTolerance) { return true } - } - } - return false - }; - FuzzyPointLocator.prototype.getLocation = function getLocation (pt) { - if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY } - return this._ptLocator.locate(pt, this._g) - }; - FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) { - var extracter = new PolygonalLineworkExtracter(); - g.apply(extracter); - var linework = extracter.getLinework(); - var lines = GeometryFactory.toLineStringArray(linework); - return g.getFactory().createMultiLineString(lines) - }; - FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - FuzzyPointLocator.prototype.getClass = function getClass () { - return FuzzyPointLocator - }; - - var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () { - this._linework = null; - this._linework = new ArrayList(); - }; - PolygonalLineworkExtracter.prototype.getLinework = function getLinework () { - return this._linework - }; - PolygonalLineworkExtracter.prototype.filter = function filter (g) { - var this$1 = this; - - if (g instanceof Polygon) { - var poly = g; - this._linework.add(poly.getExteriorRing()); - for (var i = 0; i < poly.getNumInteriorRing(); i++) { - this$1._linework.add(poly.getInteriorRingN(i)); - } - } - }; - PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () { - return [GeometryFilter] - }; - PolygonalLineworkExtracter.prototype.getClass = function getClass () { - return PolygonalLineworkExtracter - }; - - var OffsetPointGenerator = function OffsetPointGenerator () { - this._g = null; - this._doLeft = true; - this._doRight = true; - var g = arguments[0]; - this._g = g; - }; - OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) { - var this$1 = this; - - var pts = line.getCoordinates(); - for (var i = 0; i < pts.length - 1; i++) { - this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts); - } - }; - OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) { - this._doLeft = doLeft; - this._doRight = doRight; - }; - OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) { - var this$1 = this; - - var offsetPts = new ArrayList(); - var lines = LinearComponentExtracter.getLines(this._g); - for (var i = lines.iterator(); i.hasNext();) { - var line = i.next(); - this$1.extractPoints(line, offsetDistance, offsetPts); - } - return offsetPts - }; - OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) { - var dx = p1.x - p0.x; - var dy = p1.y - p0.y; - var len = Math.sqrt(dx * dx + dy * dy); - var ux = offsetDistance * dx / len; - var uy = offsetDistance * dy / len; - var midX = (p1.x + p0.x) / 2; - var midY = (p1.y + p0.y) / 2; - if (this._doLeft) { - var offsetLeft = new Coordinate(midX - uy, midY + ux); - offsetPts.add(offsetLeft); - } - if (this._doRight) { - var offsetRight = new Coordinate(midX + uy, midY - ux); - offsetPts.add(offsetRight); - } - }; - OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OffsetPointGenerator.prototype.getClass = function getClass () { - return OffsetPointGenerator - }; - - var OverlayResultValidator = function OverlayResultValidator () { - this._geom = null; - this._locFinder = null; - this._location = new Array(3).fill(null); - this._invalidLocation = null; - this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE; - this._testCoords = new ArrayList(); - var a = arguments[0]; - var b = arguments[1]; - var result = arguments[2]; - this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b); - this._geom = [a, b, result]; - this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)]; - }; - - var staticAccessors$46 = { TOLERANCE: { configurable: true } }; - OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) { - System.out.println('Overlay result invalid - A:' + Location.toLocationSymbol(location[0]) + ' B:' + Location.toLocationSymbol(location[1]) + ' expected:' + (expectedInterior ? 'i' : 'e') + ' actual:' + Location.toLocationSymbol(location[2])); - }; - OverlayResultValidator.prototype.isValid = function isValid (overlayOp) { - this.addTestPts(this._geom[0]); - this.addTestPts(this._geom[1]); - var isValid = this.checkValid(overlayOp); - return isValid - }; - OverlayResultValidator.prototype.checkValid = function checkValid () { - var this$1 = this; - - if (arguments.length === 1) { - var overlayOp = arguments[0]; - for (var i = 0; i < this._testCoords.size(); i++) { - var pt = this$1._testCoords.get(i); - if (!this$1.checkValid(overlayOp, pt)) { - this$1._invalidLocation = pt; - return false - } - } - return true - } else if (arguments.length === 2) { - var overlayOp$1 = arguments[0]; - var pt$1 = arguments[1]; - this._location[0] = this._locFinder[0].getLocation(pt$1); - this._location[1] = this._locFinder[1].getLocation(pt$1); - this._location[2] = this._locFinder[2].getLocation(pt$1); - if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true } - return this.isValidResult(overlayOp$1, this._location) - } - }; - OverlayResultValidator.prototype.addTestPts = function addTestPts (g) { - var ptGen = new OffsetPointGenerator(g); - this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance)); - }; - OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) { - var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp); - var resultInInterior = location[2] === Location.INTERIOR; - var isValid = !(expectedInterior ^ resultInInterior); - if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); } - return isValid - }; - OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () { - return this._invalidLocation - }; - OverlayResultValidator.prototype.interfaces_ = function interfaces_ () { - return [] - }; - OverlayResultValidator.prototype.getClass = function getClass () { - return OverlayResultValidator - }; - OverlayResultValidator.hasLocation = function hasLocation (location, loc) { - for (var i = 0; i < 3; i++) { - if (location[i] === loc) { return true } - } - return false - }; - OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) { - return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1)) - }; - OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) { - var validator = new OverlayResultValidator(a, b, result); - return validator.isValid(overlayOp) - }; - staticAccessors$46.TOLERANCE.get = function () { return 0.000001 }; - - Object.defineProperties( OverlayResultValidator, staticAccessors$46 ); - - // operation.overlay - - var GeometryCombiner = function GeometryCombiner (geoms) { - this._geomFactory = null; - this._skipEmpty = false; - this._inputGeoms = null; - this._geomFactory = GeometryCombiner.extractFactory(geoms); - this._inputGeoms = geoms; - }; - GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) { - var this$1 = this; - - if (geom === null) { return null } - for (var i = 0; i < geom.getNumGeometries(); i++) { - var elemGeom = geom.getGeometryN(i); - if (this$1._skipEmpty && elemGeom.isEmpty()) { continue } - elems.add(elemGeom); - } - }; - GeometryCombiner.prototype.combine = function combine () { - var this$1 = this; - - var elems = new ArrayList(); - for (var i = this._inputGeoms.iterator(); i.hasNext();) { - var g = i.next(); - this$1.extractElements(g, elems); - } - if (elems.size() === 0) { - if (this._geomFactory !== null) { - return this._geomFactory.createGeometryCollection(null) - } - return null - } - return this._geomFactory.buildGeometry(elems) - }; - GeometryCombiner.prototype.interfaces_ = function interfaces_ () { - return [] - }; - GeometryCombiner.prototype.getClass = function getClass () { - return GeometryCombiner - }; - GeometryCombiner.combine = function combine () { - if (arguments.length === 1) { - var geoms = arguments[0]; - var combiner = new GeometryCombiner(geoms); - return combiner.combine() - } else if (arguments.length === 2) { - var g0 = arguments[0]; - var g1 = arguments[1]; - var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1)); - return combiner$1.combine() - } else if (arguments.length === 3) { - var g0$1 = arguments[0]; - var g1$1 = arguments[1]; - var g2 = arguments[2]; - var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2)); - return combiner$2.combine() - } - }; - GeometryCombiner.extractFactory = function extractFactory (geoms) { - if (geoms.isEmpty()) { return null } - return geoms.iterator().next().getFactory() - }; - GeometryCombiner.createList = function createList () { - if (arguments.length === 2) { - var obj0 = arguments[0]; - var obj1 = arguments[1]; - var list = new ArrayList(); - list.add(obj0); - list.add(obj1); - return list - } else if (arguments.length === 3) { - var obj0$1 = arguments[0]; - var obj1$1 = arguments[1]; - var obj2 = arguments[2]; - var list$1 = new ArrayList(); - list$1.add(obj0$1); - list$1.add(obj1$1); - list$1.add(obj2); - return list$1 - } - }; - - var CascadedPolygonUnion = function CascadedPolygonUnion () { - this._inputPolys = null; - this._geomFactory = null; - var polys = arguments[0]; - this._inputPolys = polys; - if (this._inputPolys === null) { this._inputPolys = new ArrayList(); } - }; - - var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } }; - CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) { - var this$1 = this; - - var geoms = new ArrayList(); - for (var i = geomTree.iterator(); i.hasNext();) { - var o = i.next(); - var geom = null; - if (hasInterface(o, List)) { - geom = this$1.unionTree(o); - } else if (o instanceof Geometry) { - geom = o; - } - geoms.add(geom); - } - return geoms - }; - CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) { - var intersectingGeoms = new ArrayList(); - for (var i = 0; i < geom.getNumGeometries(); i++) { - var elem = geom.getGeometryN(i); - if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); } - } - return this._geomFactory.buildGeometry(intersectingGeoms) - }; - CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) { - var g0Env = g0.getEnvelopeInternal(); - var g1Env = g1.getEnvelopeInternal(); - if (!g0Env.intersects(g1Env)) { - var combo = GeometryCombiner.combine(g0, g1); - return combo - } - if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) } - var commonEnv = g0Env.intersection(g1Env); - return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv) - }; - CascadedPolygonUnion.prototype.union = function union () { - if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') } - if (this._inputPolys.isEmpty()) { return null } - this._geomFactory = this._inputPolys.iterator().next().getFactory(); - var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY); - for (var i = this._inputPolys.iterator(); i.hasNext();) { - var item = i.next(); - index.insert(item.getEnvelopeInternal(), item); - } - this._inputPolys = null; - var itemTree = index.itemsTree(); - var unionAll = this.unionTree(itemTree); - return unionAll - }; - CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () { - if (arguments.length === 1) { - var geoms = arguments[0]; - return this.binaryUnion(geoms, 0, geoms.size()) - } else if (arguments.length === 3) { - var geoms$1 = arguments[0]; - var start = arguments[1]; - var end = arguments[2]; - if (end - start <= 1) { - var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start); - return this.unionSafe(g0, null) - } else if (end - start === 2) { - return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1)) - } else { - var mid = Math.trunc((end + start) / 2); - var g0$1 = this.binaryUnion(geoms$1, start, mid); - var g1 = this.binaryUnion(geoms$1, mid, end); - return this.unionSafe(g0$1, g1) - } - } - }; - CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) { - var union = null; - for (var i = geoms.iterator(); i.hasNext();) { - var g = i.next(); - if (union === null) { union = g.copy(); } else { union = union.union(g); } - } - return union - }; - CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) { - if (g0 === null && g1 === null) { return null } - if (g0 === null) { return g1.copy() } - if (g1 === null) { return g0.copy() } - return this.unionOptimized(g0, g1) - }; - CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) { - return CascadedPolygonUnion.restrictToPolygons(g0.union(g1)) - }; - CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) { - var geoms = this.reduceToGeometries(geomTree); - var union = this.binaryUnion(geoms); - return union - }; - CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) { - var disjointPolys = new ArrayList(); - var g0Int = this.extractByEnvelope(common, g0, disjointPolys); - var g1Int = this.extractByEnvelope(common, g1, disjointPolys); - var union = this.unionActual(g0Int, g1Int); - disjointPolys.add(union); - var overallUnion = GeometryCombiner.combine(disjointPolys); - return overallUnion - }; - CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () { - if (arguments.length === 1) { - var geoms = arguments[0]; - var factory = geoms.get(0).getFactory(); - var gColl = factory.buildGeometry(geoms); - var unionAll = gColl.buffer(0.0); - return unionAll - } else if (arguments.length === 2) { - var g0 = arguments[0]; - var g1 = arguments[1]; - var factory$1 = g0.getFactory(); - var gColl$1 = factory$1.createGeometryCollection([g0, g1]); - var unionAll$1 = gColl$1.buffer(0.0); - return unionAll$1 - } - }; - CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () { - return [] - }; - CascadedPolygonUnion.prototype.getClass = function getClass () { - return CascadedPolygonUnion - }; - CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) { - if (hasInterface(g, Polygonal)) { - return g - } - var polygons = PolygonExtracter.getPolygons(g); - if (polygons.size() === 1) { return polygons.get(0) } - return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons)) - }; - CascadedPolygonUnion.getGeometry = function getGeometry (list, index) { - if (index >= list.size()) { return null } - return list.get(index) - }; - CascadedPolygonUnion.union = function union (polys) { - var op = new CascadedPolygonUnion(polys); - return op.union() - }; - staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 }; - - Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 ); - - var UnionOp = function UnionOp () {}; - - UnionOp.prototype.interfaces_ = function interfaces_ () { - return [] - }; - UnionOp.prototype.getClass = function getClass () { - return UnionOp - }; - UnionOp.union = function union (g, other) { - if (g.isEmpty() || other.isEmpty()) { - if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) } - if (g.isEmpty()) { return other.copy() } - if (other.isEmpty()) { return g.copy() } - } - g.checkNotGeometryCollection(g); - g.checkNotGeometryCollection(other); - return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION) - }; - - /** - * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth. - */ - - /** - * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}. - * - * @name feature - * @param {Geometry} geometry input geometry - * @param {Object} [properties={}] an Object of key-value pairs to add as properties - * @param {Object} [options={}] Optional Parameters - * @param {Array} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature - * @param {string|number} [options.id] Identifier associated with the Feature - * @returns {Feature} a GeoJSON Feature - * @example - * var geometry = { - * "type": "Point", - * "coordinates": [110, 50] - * }; - * - * var feature = turf.feature(geometry); - * - * //=feature - */ - function feature$1(geometry, properties, options) { - // Optional Parameters - options = options || {}; - if (!isObject$4(options)) throw new Error('options is invalid'); - var bbox = options.bbox; - var id = options.id; - - // Validation - if (geometry === undefined) throw new Error('geometry is required'); - if (properties && properties.constructor !== Object) throw new Error('properties must be an Object'); - if (bbox) validateBBox(bbox); - if (id) validateId(id); - - // Main - var feat = {type: 'Feature'}; - if (id) feat.id = id; - if (bbox) feat.bbox = bbox; - feat.properties = properties || {}; - feat.geometry = geometry; - return feat; - } - - /** - * isNumber - * - * @param {*} num Number to validate - * @returns {boolean} true/false - * @example - * turf.isNumber(123) - * //=true - * turf.isNumber('foo') - * //=false - */ - function isNumber$1(num) { - return !isNaN(num) && num !== null && !Array.isArray(num); - } - - /** - * isObject - * - * @param {*} input variable to validate - * @returns {boolean} true/false - * @example - * turf.isObject({elevation: 10}) - * //=true - * turf.isObject('foo') - * //=false - */ - function isObject$4(input) { - return (!!input) && (input.constructor === Object); - } - - /** - * Validate BBox - * - * @private - * @param {Array} bbox BBox to validate - * @returns {void} - * @throws Error if BBox is not valid - * @example - * validateBBox([-180, -40, 110, 50]) - * //=OK - * validateBBox([-180, -40]) - * //=Error - * validateBBox('Foo') - * //=Error - * validateBBox(5) - * //=Error - * validateBBox(null) - * //=Error - * validateBBox(undefined) - * //=Error - */ - function validateBBox(bbox) { - if (!bbox) throw new Error('bbox is required'); - if (!Array.isArray(bbox)) throw new Error('bbox must be an Array'); - if (bbox.length !== 4 && bbox.length !== 6) throw new Error('bbox must be an Array of 4 or 6 numbers'); - bbox.forEach(function (num) { - if (!isNumber$1(num)) throw new Error('bbox must only contain numbers'); - }); - } - - /** - * Validate Id - * - * @private - * @param {string|number} id Id to validate - * @returns {void} - * @throws Error if Id is not valid - * @example - * validateId([-180, -40, 110, 50]) - * //=Error - * validateId([-180, -40]) - * //=Error - * validateId('Foo') - * //=OK - * validateId(5) - * //=OK - * validateId(null) - * //=Error - * validateId(undefined) - * //=Error - */ - function validateId(id) { - if (!id) throw new Error('id is required'); - if (['string', 'number'].indexOf(typeof id) === -1) throw new Error('id must be a number or a string'); - } - - /** - * Callback for geomEach - * - * @callback geomEachCallback - * @param {Geometry} currentGeometry The current Geometry being processed. - * @param {number} featureIndex The current index of the Feature being processed. - * @param {Object} featureProperties The current Feature Properties being processed. - * @param {Array} featureBBox The current Feature BBox being processed. - * @param {number|string} featureId The current Feature Id being processed. - */ - - /** - * Iterate over each geometry in any GeoJSON object, similar to Array.forEach() - * - * @name geomEach - * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object - * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) - * @returns {void} - * @example - * var features = turf.featureCollection([ - * turf.point([26, 37], {foo: 'bar'}), - * turf.point([36, 53], {hello: 'world'}) - * ]); - * - * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { - * //=currentGeometry - * //=featureIndex - * //=featureProperties - * //=featureBBox - * //=featureId - * }); - */ - function geomEach(geojson, callback) { - var i, j, g, geometry, stopG, - geometryMaybeCollection, - isGeometryCollection, - featureProperties, - featureBBox, - featureId, - featureIndex = 0, - isFeatureCollection = geojson.type === 'FeatureCollection', - isFeature = geojson.type === 'Feature', - stop = isFeatureCollection ? geojson.features.length : 1; - - // This logic may look a little weird. The reason why it is that way - // is because it's trying to be fast. GeoJSON supports multiple kinds - // of objects at its root: FeatureCollection, Features, Geometries. - // This function has the responsibility of handling all of them, and that - // means that some of the `for` loops you see below actually just don't apply - // to certain inputs. For instance, if you give this just a - // Point geometry, then both loops are short-circuited and all we do - // is gradually rename the input until it's called 'geometry'. - // - // This also aims to allocate as few resources as possible: just a - // few numbers and booleans, rather than any temporary arrays as would - // be required with the normalization approach. - for (i = 0; i < stop; i++) { - - geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry : - (isFeature ? geojson.geometry : geojson)); - featureProperties = (isFeatureCollection ? geojson.features[i].properties : - (isFeature ? geojson.properties : {})); - featureBBox = (isFeatureCollection ? geojson.features[i].bbox : - (isFeature ? geojson.bbox : undefined)); - featureId = (isFeatureCollection ? geojson.features[i].id : - (isFeature ? geojson.id : undefined)); - isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false; - stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1; - - for (g = 0; g < stopG; g++) { - geometry = isGeometryCollection ? - geometryMaybeCollection.geometries[g] : geometryMaybeCollection; - - // Handle null Geometry - if (geometry === null) { - if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) return false; - continue; - } - switch (geometry.type) { - case 'Point': - case 'LineString': - case 'MultiPoint': - case 'Polygon': - case 'MultiLineString': - case 'MultiPolygon': { - if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) return false; - break; - } - case 'GeometryCollection': { - for (j = 0; j < geometry.geometries.length; j++) { - if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) return false; - } - break; - } - default: - throw new Error('Unknown Geometry Type'); - } - } - // Only increase `featureIndex` per each feature - featureIndex++; - } - } - - /** - * Callback for geomReduce - * - * The first time the callback function is called, the values provided as arguments depend - * on whether the reduce method has an initialValue argument. - * - * If an initialValue is provided to the reduce method: - * - The previousValue argument is initialValue. - * - The currentValue argument is the value of the first element present in the array. - * - * If an initialValue is not provided: - * - The previousValue argument is the value of the first element present in the array. - * - The currentValue argument is the value of the second element present in the array. - * - * @callback geomReduceCallback - * @param {*} previousValue The accumulated value previously returned in the last invocation - * of the callback, or initialValue, if supplied. - * @param {Geometry} currentGeometry The current Geometry being processed. - * @param {number} featureIndex The current index of the Feature being processed. - * @param {Object} featureProperties The current Feature Properties being processed. - * @param {Array} featureBBox The current Feature BBox being processed. - * @param {number|string} featureId The current Feature Id being processed. - */ - - /** - * Reduce geometry in any GeoJSON object, similar to Array.reduce(). - * - * @name geomReduce - * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object - * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) - * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. - * @returns {*} The value that results from the reduction. - * @example - * var features = turf.featureCollection([ - * turf.point([26, 37], {foo: 'bar'}), - * turf.point([36, 53], {hello: 'world'}) - * ]); - * - * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { - * //=previousValue - * //=currentGeometry - * //=featureIndex - * //=featureProperties - * //=featureBBox - * //=featureId - * return currentGeometry - * }); - */ - function geomReduce(geojson, callback, initialValue) { - var previousValue = initialValue; - geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) { - if (featureIndex === 0 && initialValue === undefined) previousValue = currentGeometry; - else previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); - }); - return previousValue; - } - - /** - * Callback for flattenEach - * - * @callback flattenEachCallback - * @param {Feature} currentFeature The current flattened feature being processed. - * @param {number} featureIndex The current index of the Feature being processed. - * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed. - */ - - /** - * Iterate over flattened features in any GeoJSON object, similar to - * Array.forEach. - * - * @name flattenEach - * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object - * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex) - * @example - * var features = turf.featureCollection([ - * turf.point([26, 37], {foo: 'bar'}), - * turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'}) - * ]); - * - * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) { - * //=currentFeature - * //=featureIndex - * //=multiFeatureIndex - * }); - */ - function flattenEach(geojson, callback) { - geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) { - // Callback for single geometry - var type = (geometry === null) ? null : geometry.type; - switch (type) { - case null: - case 'Point': - case 'LineString': - case 'Polygon': - if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) return false; - return; - } - - var geomType; - - // Callback for multi-geometry - switch (type) { - case 'MultiPoint': - geomType = 'Point'; - break; - case 'MultiLineString': - geomType = 'LineString'; - break; - case 'MultiPolygon': - geomType = 'Polygon'; - break; - } - - for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) { - var coordinate = geometry.coordinates[multiFeatureIndex]; - var geom = { - type: geomType, - coordinates: coordinate - }; - if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) return false; - } - }); - } - - /** - * Takes one or more features and returns their area in square meters. - * - * @name area - * @param {GeoJSON} geojson input GeoJSON feature(s) - * @returns {number} area in square meters - * @example - * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]); - * - * var area = turf.area(polygon); - * - * //addToMap - * var addToMap = [polygon] - * polygon.properties.area = area - */ - function area(geojson) { - return geomReduce(geojson, function (value, geom) { - return value + calculateArea(geom); - }, 0); - } - - var RADIUS$1 = 6378137; - // var FLATTENING_DENOM = 298.257223563; - // var FLATTENING = 1 / FLATTENING_DENOM; - // var POLAR_RADIUS = RADIUS * (1 - FLATTENING); - - /** - * Calculate Area - * - * @private - * @param {GeoJSON} geojson GeoJSON - * @returns {number} area - */ - function calculateArea(geojson) { - var area = 0, i; - switch (geojson.type) { - case 'Polygon': - return polygonArea$1(geojson.coordinates); - case 'MultiPolygon': - for (i = 0; i < geojson.coordinates.length; i++) { - area += polygonArea$1(geojson.coordinates[i]); - } - return area; - case 'Point': - case 'MultiPoint': - case 'LineString': - case 'MultiLineString': - return 0; - case 'GeometryCollection': - for (i = 0; i < geojson.geometries.length; i++) { - area += calculateArea(geojson.geometries[i]); - } - return area; - } - } - - function polygonArea$1(coords) { - var area = 0; - if (coords && coords.length > 0) { - area += Math.abs(ringArea$1(coords[0])); - for (var i = 1; i < coords.length; i++) { - area -= Math.abs(ringArea$1(coords[i])); - } - } - return area; - } - - /** - * @private - * Calculate the approximate area of the polygon were it projected onto the earth. - * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative. - * - * Reference: - * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion - * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 - * - * @param {Array>} coords Ring Coordinates - * @returns {number} The approximate signed geodesic area of the polygon in square meters. - */ - function ringArea$1(coords) { - var p1; - var p2; - var p3; - var lowerIndex; - var middleIndex; - var upperIndex; - var i; - var area = 0; - var coordsLength = coords.length; - - if (coordsLength > 2) { - for (i = 0; i < coordsLength; i++) { - if (i === coordsLength - 2) { // i = N-2 - lowerIndex = coordsLength - 2; - middleIndex = coordsLength - 1; - upperIndex = 0; - } else if (i === coordsLength - 1) { // i = N-1 - lowerIndex = coordsLength - 1; - middleIndex = 0; - upperIndex = 1; - } else { // i = 0 to N-3 - lowerIndex = i; - middleIndex = i + 1; - upperIndex = i + 2; - } - p1 = coords[lowerIndex]; - p2 = coords[middleIndex]; - p3 = coords[upperIndex]; - area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1])); - } - - area = area * RADIUS$1 * RADIUS$1 / 2; - } - - return area; - } - - function rad$1(_) { - return _ * Math.PI / 180; - } - - /** - * Get Geometry from Feature or Geometry Object - * - * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object - * @returns {Geometry|null} GeoJSON Geometry Object - * @throws {Error} if geojson is not a Feature or Geometry Object - * @example - * var point = { - * "type": "Feature", - * "properties": {}, - * "geometry": { - * "type": "Point", - * "coordinates": [110, 40] - * } - * } - * var geom = turf.getGeom(point) - * //={"type": "Point", "coordinates": [110, 40]} - */ - function getGeom(geojson) { - if (!geojson) throw new Error('geojson is required'); - if (geojson.geometry !== undefined) return geojson.geometry; - if (geojson.coordinates || geojson.geometries) return geojson; - throw new Error('geojson must be a valid Feature or Geometry Object'); - } - - /** - * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first. - * - * @name difference - * @param {Feature} polygon1 input Polygon feature - * @param {Feature} polygon2 Polygon feature to difference from polygon1 - * @returns {Feature|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`) - * @example - * var polygon1 = turf.polygon([[ - * [128, -26], - * [141, -26], - * [141, -21], - * [128, -21], - * [128, -26] - * ]], { - * "fill": "#F00", - * "fill-opacity": 0.1 - * }); - * var polygon2 = turf.polygon([[ - * [126, -28], - * [140, -28], - * [140, -20], - * [126, -20], - * [126, -28] - * ]], { - * "fill": "#00F", - * "fill-opacity": 0.1 - * }); - * - * var difference = turf.difference(polygon1, polygon2); - * - * //addToMap - * var addToMap = [polygon1, polygon2, difference]; - */ - function difference(polygon1, polygon2) { - var geom1 = getGeom(polygon1); - var geom2 = getGeom(polygon2); - var properties = polygon1.properties || {}; - - // Issue #721 - JSTS can't handle empty polygons - geom1 = removeEmptyPolygon(geom1); - geom2 = removeEmptyPolygon(geom2); - if (!geom1) return null; - if (!geom2) return feature$1(geom1, properties); - - // JSTS difference operation - var reader = new GeoJSONReader(); - var a = reader.read(geom1); - var b = reader.read(geom2); - var differenced = OverlayOp.difference(a, b); - if (differenced.isEmpty()) return null; - var writer = new GeoJSONWriter(); - var geom = writer.write(differenced); - - return feature$1(geom, properties); - } - - /** - * Detect Empty Polygon - * - * @private - * @param {Geometry} geom Geometry Object - * @returns {Geometry|null} removed any polygons with no areas - */ - function removeEmptyPolygon(geom) { - switch (geom.type) { - case 'Polygon': - if (area(geom) > 1) return geom; - return null; - case 'MultiPolygon': - var coordinates = []; - flattenEach(geom, function (feature$$1) { - if (area(feature$$1) > 1) coordinates.push(feature$$1.geometry.coordinates); - }); - if (coordinates.length) return {type: 'MultiPolygon', coordinates: coordinates}; - } - } - - /** - * Takes two or more {@link Polygon|polygons} and returns a combined polygon. If the input polygons are not contiguous, this function returns a {@link MultiPolygon} feature. - * - * @name union - * @param {...Feature} A polygon to combine - * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature - * @example - * var poly1 = turf.polygon([[ - * [-82.574787, 35.594087], - * [-82.574787, 35.615581], - * [-82.545261, 35.615581], - * [-82.545261, 35.594087], - * [-82.574787, 35.594087] - * ]], {"fill": "#0f0"}); - * var poly2 = turf.polygon([[ - * [-82.560024, 35.585153], - * [-82.560024, 35.602602], - * [-82.52964, 35.602602], - * [-82.52964, 35.585153], - * [-82.560024, 35.585153] - * ]], {"fill": "#00f"}); - * - * var union = turf.union(poly1, poly2); - * - * //addToMap - * var addToMap = [poly1, poly2, union]; - */ - function union$1() { - var reader = new GeoJSONReader(); - var result = reader.read(JSON.stringify(arguments[0].geometry)); - - for (var i = 1; i < arguments.length; i++) { - result = UnionOp.union(result, reader.read(JSON.stringify(arguments[i].geometry))); - } - - var writer = new GeoJSONWriter(); - result = writer.write(result); - - return { - type: 'Feature', - geometry: result, - properties: arguments[0].properties - }; - } - - // Reduce an array of locations into a single GeoJSON feature - function _locationReducer(accumulator, location) { - /* eslint-disable no-console, no-invalid-this */ - let result; - try { - let resolved = this.resolveLocation(location); - if (!resolved || !resolved.feature) { - console.warn(`Warning: Couldn't resolve location "${location}"`); - return accumulator; - } - result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature); - } catch (e) { - console.warn(`Warning: Error resolving location "${location}"`); - console.warn(e); - result = accumulator; - } - - return result; - /* eslint-enable no-console, no-invalid-this */ - } - - - - function _cloneDeep(obj) { - return JSON.parse(JSON.stringify(obj)); - } - - - class LocationConflation { - - // constructor - // - // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later. - // Each feature must have a filename-like `id`, for example: `something.geojson` - // - // { - // "type": "FeatureCollection" - // "features": [ - // { - // "type": "Feature", - // "id": "philly_metro.geojson", - // "properties": { … }, - // "geometry": { … } - // } - // ] - // } - constructor(fc) { - this._cache = {}; - - // process input FeatureCollection - if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) { - fc.features.forEach(feature => { - feature.properties = feature.properties || {}; - let props = feature.properties; - - // get `id` from either `id` or `properties` - let id = feature.id || props.id; - if (!id || !/^\S+\.geojson$/i.test(id)) return; - - // ensure id exists and is lowercase - id = id.toLowerCase(); - feature.id = id; - props.id = id; - - // ensure area property exists - if (!props.area) { - const area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km² - props.area = Number(area.toFixed(2)); - } - - this._cache[id] = feature; - }); - } - - // Replace CountryCoder world geometry to have a polygon covering the world. - let world = _cloneDeep(feature('Q2')); - world.geometry = { - type: 'Polygon', - coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]] - }; - world.id = 'Q2'; - world.properties.id = 'Q2'; - world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km² - this._cache.Q2 = world; - } - - - // validateLocation - // - // Pass a `location` identifier - // Returns a result like - // { - // type: 'point', 'geojson', or 'countrycoder' - // location: the queried location - // id: a unique identifier - // } - // or `null` if the location is invalid - // - validateLocation(location) { - if (Array.isArray(location)) { // a [lon,lat] coordinate pair? - if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) && - location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90 - ) { - const id = '[' + location.toString() + ']'; - return { type: 'point', location: location, id: id }; - } - - } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { // a .geojson filename? - const id = location.toLowerCase(); - if (this._cache[id]) { - return { type: 'geojson', location: location, id: id }; - } - - } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value? - const feature$1 = feature(location); - if (feature$1) { - // Use wikidata QID as the identifier, since that seems to be the only - // property that everything in CountryCoder is guaranteed to have. - const id = feature$1.properties.wikidata; - return { type: 'countrycoder', location: location, id: id }; - } - } - - return null; - } - - - // resolveLocation - // - // Pass a `location` identifier - // Returns a result like - // { - // type: 'point', 'geojson', or 'countrycoder' - // location: the queried location - // id: a unique identifier - // feature: the geojson feature - // } - // or `null` if the location is invalid - // - resolveLocation(location) { - const valid = this.validateLocation(location); - if (!valid) return null; - - // return a result from cache if we can - if (this._cache[valid.id]) { - return Object.assign(valid, { feature: this._cache[valid.id] }); - } - - // a [lon,lat] coordinate pair? - if (valid.type === 'point') { - const RADIUS = 25000; // meters - const EDGES = 10; - const PRECISION = 3; - const area = Math.PI * RADIUS * RADIUS / 1e6; // m² to km² - const feature = this._cache[valid.id] = geojsonPrecision({ - type: 'Feature', - id: valid.id, - properties: { id: valid.id, area: Number(area.toFixed(2)) }, - geometry: circleToPolygon(location, RADIUS, EDGES) - }, PRECISION); - return Object.assign(valid, { feature: feature }); - - // a .geojson filename? - } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') { - let feature$1 = _cloneDeep(feature(valid.id)); - let props = feature$1.properties; - - // -> This block of code is weird and requires some explanation. <- - // CountryCoder includes higher level features which are made up of members. - // These features don't have their own geometry, but CountryCoder provides an - // `aggregateFeature` method to combine these members into a MultiPolygon. - // BUT, when we try to actually work with these aggregated MultiPolygons, - // Turf/JSTS gets crashy because of topography bugs. - // SO, we'll aggregate the features ourselves by unioning them together. - // This approach also has the benefit of removing all the internal boaders and - // simplifying the regional polygons a lot. - if (Array.isArray(props.members)) { - let seed = feature$1.geometry ? feature$1 : null; - let aggregate = props.members.reduce(_locationReducer.bind(this), seed); - feature$1.geometry = aggregate.geometry; - } - - // ensure area property exists - if (!props.area) { - const area = geojsonArea.geometry(feature$1.geometry) / 1e6; // m² to km² - props.area = Number(area.toFixed(2)); - } - - // ensure id property exists - feature$1.id = valid.id; - props.id = valid.id; - - this._cache[valid.id] = feature$1; - return Object.assign(valid, { feature: feature$1 }); - } - - return null; - } - - - // resolveLocationSet - // - // Pass a `locationSet` Object like: - // `{ include: [ Array ], exclude: [ Array ] }` - // Returns a stable identifier string of the form: - // "+[included]-[excluded]" - // - resolveLocationSet(locationSet) { - locationSet = locationSet || {}; - const resolve = this.resolveLocation.bind(this); - let include = (locationSet.include || []).map(resolve).filter(Boolean); - let exclude = (locationSet.exclude || []).map(resolve).filter(Boolean); - - if (!include.length) { - include = [resolve('Q2')]; // default to 'the world' - } - - // return quickly if it's a single included location.. - if (include.length === 1 && exclude.length === 0) { - return include[0].feature; - } - - // generate stable identifier - include.sort(sortFeatures); - let id = '+[' + include.map(d => d.id).join(',') + ']'; - if (exclude.length) { - exclude.sort(sortFeatures); - id += '-[' + exclude.map(d => d.id).join(',') + ']'; - } - - // return cached? - if (this._cache[id]) { - return this._cache[id]; - } - - // calculate unions - let includeGeoJSON = include.map(d => d.location).reduce(_locationReducer.bind(this), null); - let excludeGeoJSON = exclude.map(d => d.location).reduce(_locationReducer.bind(this), null); - - // calculate difference, update area and return result - let resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON; - const area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km² - resultGeoJSON.id = id; - resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) }; - - return this._cache[id] = resultGeoJSON; - - - // Sorting the location lists is ok because they end up unioned together. - // This sorting makes it possible to generate a deterministic id. - function sortFeatures(a, b) { - const rank = { countrycoder: 1, geojson: 2, point: 3 }; - const aRank = rank[a.type]; - const bRank = rank[b.type]; - - return (aRank > bRank) ? 1 - : (aRank < bRank) ? -1 - : a.id.localeCompare(b.id); - } - } - - - cache() { - return this._cache; - } - } - - let _oci = null; - - function uiSuccess(context) { - const MAXEVENTS = 2; - const dispatch$1 = dispatch('cancel'); - let _changeset; - let _location; - ensureOSMCommunityIndex(); // start fetching the data - - - function ensureOSMCommunityIndex() { - const data = _mainFileFetcher; - return Promise.all([ data.get('oci_resources'), data.get('oci_features') ]) - .then(vals => { - if (_oci) return _oci; - - const ociResources = vals[0].resources; - const loco = new LocationConflation(vals[1]); - let ociFeatures = {}; - - Object.values(ociResources).forEach(resource => { - const feature = loco.resolveLocationSet(resource.locationSet); - let ociFeature = ociFeatures[feature.id]; - if (!ociFeature) { - ociFeature = JSON.parse(JSON.stringify(feature)); // deep clone - ociFeature.properties.resourceIDs = new Set(); - ociFeatures[feature.id] = ociFeature; - } - ociFeature.properties.resourceIDs.add(resource.id); - }); - - return _oci = { - features: ociFeatures, - resources: ociResources, - query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) }) - }; - }); - } - - - // string-to-date parsing in JavaScript is weird - function parseEventDate(when) { - if (!when) return; - - let raw = when.trim(); - if (!raw) return; - - if (!/Z$/.test(raw)) { // if no trailing 'Z', add one - raw += 'Z'; // this forces date to be parsed as a UTC date - } - - const parsed = new Date(raw); - return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone - } - - - function success(selection) { - let header = selection - .append('div') - .attr('class', 'header fillL'); - - header - .append('h3') - .text(_t('success.just_edited')); - - header - .append('button') - .attr('class', 'close') - .on('click', () => dispatch$1.call('cancel')) - .call(svgIcon('#iD-icon-close')); - - let body = selection - .append('div') - .attr('class', 'body save-success fillL'); - - let summary = body - .append('div') - .attr('class', 'save-summary'); - - summary - .append('h3') - .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location })); - - summary - .append('p') - .text(_t('success.help_html')) - .append('a') - .attr('class', 'link-out') - .attr('target', '_blank') - .attr('tabindex', -1) - .attr('href', _t('success.help_link_url')) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(_t('success.help_link_text')); - - let osm = context.connection(); - if (!osm) return; - - let changesetURL = osm.changesetURL(_changeset.id); - - let table = summary - .append('table') - .attr('class', 'summary-table'); - - let row = table - .append('tr') - .attr('class', 'summary-row'); - - row - .append('td') - .attr('class', 'cell-icon summary-icon') - .append('a') - .attr('target', '_blank') - .attr('href', changesetURL) - .append('svg') - .attr('class', 'logo-small') - .append('use') - .attr('xlink:href', '#iD-logo-osm'); - - let summaryDetail = row - .append('td') - .attr('class', 'cell-detail summary-detail'); - - summaryDetail - .append('a') - .attr('class', 'cell-detail summary-view-on-osm') - .attr('target', '_blank') - .attr('href', changesetURL) - .text(_t('success.view_on_osm')); - - summaryDetail - .append('div') - .html(_t('success.changeset_id', { - changeset_id: `${_changeset.id}` - })); - - - // Get OSM community index features intersecting the map.. - ensureOSMCommunityIndex() - .then(oci => { - let communities = []; - const properties = oci.query(context.map().center(), true) || []; - - // Gather the communities from the result - properties.forEach(props => { - const resourceIDs = Array.from(props.resourceIDs); - resourceIDs.forEach(resourceID => { - const resource = oci.resources[resourceID]; - communities.push({ - area: props.area || Infinity, - order: resource.order || 0, - resource: resource - }); - }); - }); - - // sort communities by feature area ascending, community order descending - communities.sort((a, b) => a.area - b.area || b.order - a.order); - - body - .call(showCommunityLinks, communities.map(c => c.resource)); - }); - } - - - function showCommunityLinks(selection, resources) { - let communityLinks = selection - .append('div') - .attr('class', 'save-communityLinks'); - - communityLinks - .append('h3') - .text(_t('success.like_osm')); - - let table = communityLinks - .append('table') - .attr('class', 'community-table'); - - let row = table.selectAll('.community-row') - .data(resources); - - let rowEnter = row.enter() - .append('tr') - .attr('class', 'community-row'); - - rowEnter - .append('td') - .attr('class', 'cell-icon community-icon') - .append('a') - .attr('target', '_blank') - .attr('href', d => d.url) - .append('svg') - .attr('class', 'logo-small') - .append('use') - .attr('xlink:href', d => `#community-${d.type}`); - - let communityDetail = rowEnter - .append('td') - .attr('class', 'cell-detail community-detail'); - - communityDetail - .each(showCommunityDetails); - - communityLinks - .append('div') - .attr('class', 'community-missing') - .text(_t('success.missing')) - .append('a') - .attr('class', 'link-out') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .attr('href', 'https://github.com/osmlab/osm-community-index/issues') - .append('span') - .text(_t('success.tell_us')); - } - - - function showCommunityDetails(d) { - let selection = select(this); - let communityID = d.id; - let replacements = { - url: linkify(d.url), - signupUrl: linkify(d.signupUrl || d.url) - }; - - selection - .append('div') - .attr('class', 'community-name') - .append('a') - .attr('target', '_blank') - .attr('href', d.url) - .text(_t(`community.${d.id}.name`)); - - let descriptionHTML = _t(`community.${d.id}.description`, replacements); - - if (d.type === 'reddit') { // linkify subreddits #4997 - descriptionHTML = descriptionHTML - .replace(/(\/r\/\w*\/*)/i, match => linkify(d.url, match)); - } - - selection - .append('div') - .attr('class', 'community-description') - .html(descriptionHTML); - - if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) { - selection - .append('div') - .call(uiDisclosure(context, `community-more-${d.id}`, false) - .expanded(false) - .updatePreference(false) - .title(_t('success.more')) - .content(showMore) - ); - } - - let nextEvents = (d.events || []) - .map(event => { - event.date = parseEventDate(event.when); - return event; - }) - .filter(event => { // date is valid and future (or today) - const t = event.date.getTime(); - const now = (new Date()).setHours(0,0,0,0); - return !isNaN(t) && t >= now; - }) - .sort((a, b) => { // sort by date ascending - return a.date < b.date ? -1 : a.date > b.date ? 1 : 0; - }) - .slice(0, MAXEVENTS); // limit number of events shown - - if (nextEvents.length) { - selection - .append('div') - .call(uiDisclosure(context, `community-events-${d.id}`, false) - .expanded(false) - .updatePreference(false) - .title(_t('success.events')) - .content(showNextEvents) - ) - .select('.hide-toggle') - .append('span') - .attr('class', 'badge-text') - .text(nextEvents.length); - } - - - function showMore(selection) { - let more = selection.selectAll('.community-more') - .data([0]); - - let moreEnter = more.enter() - .append('div') - .attr('class', 'community-more'); - - if (d.extendedDescription) { - moreEnter - .append('div') - .attr('class', 'community-extended-description') - .html(_t(`community.${d.id}.extendedDescription`, replacements)); - } - - if (d.languageCodes && d.languageCodes.length) { - const languageList = d.languageCodes - .map(code => _mainLocalizer.languageName(code)) - .join(', '); - - moreEnter - .append('div') - .attr('class', 'community-languages') - .text(_t('success.languages', { languages: languageList })); - } - } - - - function showNextEvents(selection) { - let events = selection - .append('div') - .attr('class', 'community-events'); - - let item = events.selectAll('.community-event') - .data(nextEvents); - - let itemEnter = item.enter() - .append('div') - .attr('class', 'community-event'); - - itemEnter - .append('div') - .attr('class', 'community-event-name') - .append('a') - .attr('target', '_blank') - .attr('href', d => d.url) - .text(d => { - let name = d.name; - if (d.i18n && d.id) { - name = _t(`community.${communityID}.events.${d.id}.name`, { default: name }); - } - return name; - }); - - itemEnter - .append('div') - .attr('class', 'community-event-when') - .text(d => { - let options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' }; - if (d.date.getHours() || d.date.getMinutes()) { // include time if it has one - options.hour = 'numeric'; - options.minute = 'numeric'; - } - return d.date.toLocaleString(_mainLocalizer.localeCode(), options); - }); - - itemEnter - .append('div') - .attr('class', 'community-event-where') - .text(d => { - let where = d.where; - if (d.i18n && d.id) { - where = _t(`community.${communityID}.events.${d.id}.where`, { default: where }); - } - return where; - }); - - itemEnter - .append('div') - .attr('class', 'community-event-description') - .text(d => { - let description = d.description; - if (d.i18n && d.id) { - description = _t(`community.${communityID}.events.${d.id}.description`, { default: description }); - } - return description; - }); - } - - - function linkify(url, text) { - text = text || url; - return `${text}`; - } - } - - - success.changeset = function(val) { - if (!arguments.length) return _changeset; - _changeset = val; - return success; - }; - - - success.location = function(val) { - if (!arguments.length) return _location; - _location = val; - return success; - }; - - - return utilRebind(success, dispatch$1, 'on'); - } - - function modeSave(context) { - var mode = { id: 'save' }; - var keybinding = utilKeybinding('modeSave'); - - var commit = uiCommit(context) - .on('cancel', cancel); - var _conflictsUi; // uiConflicts - - var _location; - var _success; - - var uploader = context.uploader() - .on('saveStarted.modeSave', function() { - keybindingOff(); - }) - // fire off some async work that we want to be ready later - .on('willAttemptUpload.modeSave', prepareForSuccess) - .on('progressChanged.modeSave', showProgress) - .on('resultNoChanges.modeSave', function() { - cancel(); - }) - .on('resultErrors.modeSave', showErrors) - .on('resultConflicts.modeSave', showConflicts) - .on('resultSuccess.modeSave', showSuccess); - - - function cancel() { - context.enter(modeBrowse(context)); - } - - - function showProgress(num, total) { - var modal = context.container().select('.loading-modal .modal-section'); - var progress = modal.selectAll('.progress') - .data([0]); - - // enter/update - progress.enter() - .append('div') - .attr('class', 'progress') - .merge(progress) - .text(_t('save.conflict_progress', { num: num, total: total })); - } - - - function showConflicts(changeset, conflicts, origChanges) { - - var selection = context.container() - .select('.sidebar') - .append('div') - .attr('class','sidebar-component'); - - context.container().selectAll('.main-content') - .classed('active', true) - .classed('inactive', false); - - _conflictsUi = uiConflicts(context) - .conflictList(conflicts) - .origChanges(origChanges) - .on('cancel', function() { - context.container().selectAll('.main-content') - .classed('active', false) - .classed('inactive', true); - selection.remove(); - keybindingOn(); - - uploader.cancelConflictResolution(); - }) - .on('save', function() { - context.container().selectAll('.main-content') - .classed('active', false) - .classed('inactive', true); - selection.remove(); - - uploader.processResolvedConflicts(changeset); - }); - - selection.call(_conflictsUi); - } - - - function showErrors(errors) { - keybindingOn(); - - var selection = uiConfirm(context.container()); - selection - .select('.modal-section.header') - .append('h3') - .text(_t('save.error')); - - addErrors(selection, errors); - selection.okButton(); - } - - - function addErrors(selection, data) { - var message = selection - .select('.modal-section.message-text'); - - var items = message - .selectAll('.error-container') - .data(data); - - var enter = items.enter() - .append('div') - .attr('class', 'error-container'); - - enter - .append('a') - .attr('class', 'error-description') - .attr('href', '#') - .classed('hide-toggle', true) - .text(function(d) { return d.msg || _t('save.unknown_error_details'); }) - .on('click', function() { - event.preventDefault(); - - var error = select(this); - var detail = select(this.nextElementSibling); - var exp = error.classed('expanded'); - - detail.style('display', exp ? 'none' : 'block'); - error.classed('expanded', !exp); - }); - - var details = enter - .append('div') - .attr('class', 'error-detail-container') - .style('display', 'none'); - - details - .append('ul') - .attr('class', 'error-detail-list') - .selectAll('li') - .data(function(d) { return d.details || []; }) - .enter() - .append('li') - .attr('class', 'error-detail-item') - .text(function(d) { return d; }); - - items.exit() - .remove(); - } - - - function showSuccess(changeset) { - commit.reset(); - - var ui = _success - .changeset(changeset) - .location(_location) - .on('cancel', function() { context.ui().sidebar.hide(); }); - - context.enter(modeBrowse(context).sidebar(ui)); - } - - - function keybindingOn() { - select(document) - .call(keybinding.on('⎋', cancel, true)); - } - - - function keybindingOff() { - select(document) - .call(keybinding.unbind); - } - - - // Reverse geocode current map location so we can display a message on - // the success screen like "Thank you for editing around place, region." - function prepareForSuccess() { - _success = uiSuccess(context); - _location = null; - if (!services.geocoder) return; - - services.geocoder.reverse(context.map().center(), function(err, result) { - if (err || !result || !result.address) return; - - var addr = result.address; - var place = (addr && (addr.town || addr.city || addr.county)) || ''; - var region = (addr && (addr.state || addr.country)) || ''; - var separator = (place && region) ? _t('success.thank_you_where.separator') : ''; - - _location = _t('success.thank_you_where.format', - { place: place, separator: separator, region: region } - ); - }); - } - - - mode.selectedIDs = function() { - return _conflictsUi ? _conflictsUi.shownEntityIds() : []; - }; - - - mode.enter = function() { - // Show sidebar - context.ui().sidebar.expand(); - - function done() { - context.ui().sidebar.show(commit); - } - - keybindingOn(); - - context.container().selectAll('.main-content') - .classed('active', false) - .classed('inactive', true); - - var osm = context.connection(); - if (!osm) { - cancel(); - return; - } - - if (osm.authenticated()) { - done(); - } else { - osm.authenticate(function(err) { - if (err) { - cancel(); - } else { - done(); - } - }); - } - }; - - - mode.exit = function() { - - keybindingOff(); - - context.container().selectAll('.main-content') - .classed('active', true) - .classed('inactive', false); - - context.ui().sidebar.hide(); - }; - - return mode; - } - - function uiToolOldDrawModes(context) { - - var tool = { - id: 'old_modes', - label: _t('toolbar.add_feature') - }; - - var modes = [ - modeAddPoint(context, { - title: _t('modes.add_point.title'), - button: 'point', - description: _t('modes.add_point.description'), - preset: _mainPresetIndex.item('point'), - key: '1' - }), - modeAddLine(context, { - title: _t('modes.add_line.title'), - button: 'line', - description: _t('modes.add_line.description'), - preset: _mainPresetIndex.item('line'), - key: '2' - }), - modeAddArea(context, { - title: _t('modes.add_area.title'), - button: 'area', - description: _t('modes.add_area.description'), - preset: _mainPresetIndex.item('area'), - key: '3' - }) - ]; - - - function enabled() { - return osmEditable(); - } - - function osmEditable() { - return context.editable(); - } - - modes.forEach(function(mode) { - context.keybinding().on(mode.key, function() { - if (!enabled()) return; - - if (mode.id === context.mode().id) { - context.enter(modeBrowse(context)); - } else { - context.enter(mode); - } - }); - }); - - tool.render = function(selection) { - - var wrap = selection - .append('div') - .attr('class', 'joined') - .style('display', 'flex'); - - var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true }); - - context.map() - .on('move.modes', debouncedUpdate) - .on('drawn.modes', debouncedUpdate); - - context - .on('enter.modes', update); - - update(); - - - function update() { - - var buttons = wrap.selectAll('button.add-button') - .data(modes, function(d) { return d.id; }); - - // exit - buttons.exit() - .remove(); - - // enter - var buttonsEnter = buttons.enter() - .append('button') - .attr('class', function(d) { return d.id + ' add-button bar-button'; }) - .on('click.mode-buttons', function(d) { - if (!enabled()) return; - - // When drawing, ignore accidental clicks on mode buttons - #4042 - var currMode = context.mode().id; - if (/^draw/.test(currMode)) return; - - if (d.id === currMode) { - context.enter(modeBrowse(context)); - } else { - context.enter(d); - } - }) - .call(uiTooltip() - .placement('bottom') - .title(function(d) { return d.description; }) - .keys(function(d) { return [d.key]; }) - .scrollContainer(context.container().select('.top-toolbar')) - ); - - buttonsEnter - .each(function(d) { - select(this) - .call(svgIcon('#iD-icon-' + d.button)); - }); - - buttonsEnter - .append('span') - .attr('class', 'label') - .text(function(mode) { return mode.title; }); - - // if we are adding/removing the buttons, check if toolbar has overflowed - if (buttons.enter().size() || buttons.exit().size()) { - context.ui().checkOverflow('.top-toolbar', true); - } - - // update - buttons = buttons - .merge(buttonsEnter) - .classed('disabled', function(d) { return !enabled(); }) - .classed('active', function(d) { return context.mode() && context.mode().button === d.button; }); - } - }; - - return tool; - } - - function uiToolNotes(context) { - - var tool = { - id: 'notes', - label: _t('modes.add_note.label') - }; - - var mode = modeAddNote(context); - - function enabled() { - return notesEnabled() && notesEditable(); - } - - function notesEnabled() { - var noteLayer = context.layers().layer('notes'); - return noteLayer && noteLayer.enabled(); - } - - function notesEditable() { - var mode = context.mode(); - return context.map().notesEditable() && mode && mode.id !== 'save'; - } - - context.keybinding().on(mode.key, function() { - if (!enabled()) return; - - if (mode.id === context.mode().id) { - context.enter(modeBrowse(context)); - } else { - context.enter(mode); - } - }); - - tool.render = function(selection) { - - - var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true }); - - context.map() - .on('move.notes', debouncedUpdate) - .on('drawn.notes', debouncedUpdate); - - context - .on('enter.notes', update); - - update(); - - - function update() { - var showNotes = notesEnabled(); - var data = showNotes ? [mode] : []; - - var buttons = selection.selectAll('button.add-button') - .data(data, function(d) { return d.id; }); - - // exit - buttons.exit() - .remove(); - - // enter - var buttonsEnter = buttons.enter() - .append('button') - .attr('tabindex', -1) - .attr('class', function(d) { return d.id + ' add-button bar-button'; }) - .on('click.notes', function(d) { - if (!enabled()) return; - - // When drawing, ignore accidental clicks on mode buttons - #4042 - var currMode = context.mode().id; - if (/^draw/.test(currMode)) return; - - if (d.id === currMode) { - context.enter(modeBrowse(context)); - } else { - context.enter(d); - } - }) - .call(uiTooltip() - .placement('bottom') - .title(function(d) { return d.description; }) - .keys(function(d) { return [d.key]; }) - .scrollContainer(context.container().select('.top-toolbar')) - ); - - buttonsEnter - .each(function(d) { - select(this) - .call(svgIcon(d.icon || '#iD-icon-' + d.button)); - }); - - // if we are adding/removing the buttons, check if toolbar has overflowed - if (buttons.enter().size() || buttons.exit().size()) { - context.ui().checkOverflow('.top-toolbar', true); - } - - // update - buttons = buttons - .merge(buttonsEnter) - .classed('disabled', function(d) { return !enabled(); }) - .classed('active', function(d) { return context.mode() && context.mode().button === d.button; }); - } - }; - - tool.uninstall = function() { - context - .on('enter.editor.notes', null) - .on('exit.editor.notes', null) - .on('enter.notes', null); - - context.map() - .on('move.notes', null) - .on('drawn.notes', null); - }; - - return tool; - } - - function uiToolSave(context) { - - var tool = { - id: 'save', - label: _t('save.title') - }; - - var button = null; - var tooltipBehavior = null; - var history = context.history(); - var key = uiCmd('⌘S'); - var _numChanges = 0; - - function isSaving() { - var mode = context.mode(); - return mode && mode.id === 'save'; - } - - function isDisabled() { - return _numChanges === 0 || isSaving(); - } - - function save() { - event.preventDefault(); - if (!context.inIntro() && !isSaving() && history.hasChanges()) { - context.enter(modeSave(context)); - } - } - - function bgColor() { - var step; - if (_numChanges === 0) { - return null; - } else if (_numChanges <= 50) { - step = _numChanges / 50; - return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow - } else { - step = Math.min((_numChanges - 50) / 50, 1.0); - return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red - } - } - - function updateCount() { - var val = history.difference().summary().length; - if (val === _numChanges) return; - - _numChanges = val; - - if (tooltipBehavior) { - tooltipBehavior - .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes')) - .keys([key]); - } - - if (button) { - button - .classed('disabled', isDisabled()) - .style('background', bgColor()); - - button.select('span.count') - .text(_numChanges); - } - } - - - tool.render = function(selection) { - tooltipBehavior = uiTooltip() - .placement('bottom') - .title(_t('save.no_changes')) - .keys([key]) - .scrollContainer(context.container().select('.top-toolbar')); - - var lastPointerUpType; - - button = selection - .append('button') - .attr('class', 'save disabled bar-button') - .on('pointerup', function() { - lastPointerUpType = event.pointerType; - }) - .on('click', function() { - event.preventDefault(); - - save(); - - if (_numChanges === 0 && ( - lastPointerUpType === 'touch' || - lastPointerUpType === 'pen') - ) { - // there are no tooltips for touch interactions so flash feedback instead - context.ui().flash - .duration(2000) - .iconName('#iD-icon-save') - .iconClass('disabled') - .text(_t('save.no_changes'))(); - } - lastPointerUpType = null; - }) - .call(tooltipBehavior); - - button - .call(svgIcon('#iD-icon-save')); - - button - .append('span') - .attr('class', 'count') - .attr('aria-hidden', 'true') - .text('0'); - - updateCount(); - - - context.keybinding() - .on(key, save, true); - - - context.history() - .on('change.save', updateCount); - - context - .on('enter.save', function() { - if (button) { - button - .classed('disabled', isDisabled()); - - if (isSaving()) { - button.call(tooltipBehavior.hide); - } - } - }); - }; - - - tool.uninstall = function() { - context.keybinding() - .off(key, true); - - context.history() - .on('change.save', null); - - context - .on('enter.save', null); - - button = null; - tooltipBehavior = null; - }; - - return tool; - } - - function uiToolSidebarToggle(context) { - - var tool = { - id: 'sidebar_toggle', - label: _t('toolbar.inspect') - }; - - tool.render = function(selection) { - selection - .append('button') - .attr('class', 'bar-button') - .on('click', function() { - context.ui().sidebar.toggle(); - }) - .call(uiTooltip() - .placement('bottom') - .title(_t('sidebar.tooltip')) - .keys([_t('sidebar.key')]) - .scrollContainer(context.container().select('.top-toolbar')) - ) - .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left'))); - }; - - return tool; - } - - function uiToolUndoRedo(context) { - - var tool = { - id: 'undo_redo', - label: _t('toolbar.undo_redo') - }; - - var commands = [{ - id: 'undo', - cmd: uiCmd('⌘Z'), - action: function() { - context.undo(); - }, - annotation: function() { - return context.history().undoAnnotation(); - }, - icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo') - }, { - id: 'redo', - cmd: uiCmd('⌘⇧Z'), - action: function() { - context.redo(); - }, - annotation: function() { - return context.history().redoAnnotation(); - }, - icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo') - }]; - - - function editable() { - return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */); - } - - - tool.render = function(selection) { - var tooltipBehavior = uiTooltip() - .placement('bottom') - .title(function (d) { - return d.annotation() ? - _t(d.id + '.tooltip', { action: d.annotation() }) : - _t(d.id + '.nothing'); - }) - .keys(function(d) { - return [d.cmd]; - }) - .scrollContainer(context.container().select('.top-toolbar')); - - var lastPointerUpType; - - var buttons = selection.selectAll('button') - .data(commands) - .enter() - .append('button') - .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; }) - .on('pointerup', function() { - // `pointerup` is always called before `click` - lastPointerUpType = event.pointerType; - }) - .on('click', function(d) { - event.preventDefault(); - - var annotation = d.annotation(); - - if (editable() && annotation) { - d.action(); - } - - if (editable() && ( - lastPointerUpType === 'touch' || - lastPointerUpType === 'pen') - ) { - // there are no tooltips for touch interactions so flash feedback instead - - var text = annotation ? - _t(d.id + '.tooltip', { action: annotation }) : - _t(d.id + '.nothing'); - context.ui().flash - .duration(2000) - .iconName('#' + d.icon) - .iconClass(annotation ? '' : 'disabled') - .text(text)(); - } - lastPointerUpType = null; - }) - .call(tooltipBehavior); - - buttons.each(function(d) { - select(this) - .call(svgIcon('#' + d.icon)); - }); - - context.keybinding() - .on(commands[0].cmd, function() { - event.preventDefault(); - if (editable()) commands[0].action(); - }) - .on(commands[1].cmd, function() { - event.preventDefault(); - if (editable()) commands[1].action(); - }); - - - var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true }); - - context.map() - .on('move.undo_redo', debouncedUpdate) - .on('drawn.undo_redo', debouncedUpdate); - - context.history() - .on('change.undo_redo', function(difference) { - if (difference) update(); - }); - - context - .on('enter.undo_redo', update); - - - function update() { - buttons - .classed('disabled', function(d) { - return !editable() || !d.annotation(); - }) - .each(function() { - var selection = select(this); - if (!selection.select('.tooltip.in').empty()) { - selection.call(tooltipBehavior.updateContent); - } - }); - } - }; - - tool.uninstall = function() { - context.keybinding() - .off(commands[0].cmd) - .off(commands[1].cmd); - - context.map() - .on('move.undo_redo', null) - .on('drawn.undo_redo', null); - - context.history() - .on('change.undo_redo', null); - - context - .on('enter.undo_redo', null); - }; - - return tool; - } - - function uiTopToolbar(context) { - - var sidebarToggle = uiToolSidebarToggle(context), - modes = uiToolOldDrawModes(context), - notes = uiToolNotes(context), - undoRedo = uiToolUndoRedo(context), - save = uiToolSave(context); - - function notesEnabled() { - var noteLayer = context.layers().layer('notes'); - return noteLayer && noteLayer.enabled(); - } - - function topToolbar(bar) { - - bar.on('wheel.topToolbar', function() { - if (!event.deltaX) { - // translate vertical scrolling into horizontal scrolling in case - // the user doesn't have an input device that can scroll horizontally - bar.node().scrollLeft += event.deltaY; - } - }); - - var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true }); - context.layers() - .on('change.topToolbar', debouncedUpdate); - - update(); - - function update() { - - var tools = [ - sidebarToggle, - 'spacer', - modes - ]; - - tools.push('spacer'); - - if (notesEnabled()) { - tools = tools.concat([notes, 'spacer']); - } - - tools = tools.concat([undoRedo, save]); - - var toolbarItems = bar.selectAll('.toolbar-item') - .data(tools, function(d) { - return d.id || d; - }); - - toolbarItems.exit() - .each(function(d) { - if (d.uninstall) { - d.uninstall(); - } - }) - .remove(); - - var itemsEnter = toolbarItems - .enter() - .append('div') - .attr('class', function(d) { - var classes = 'toolbar-item ' + (d.id || d).replace('_', '-'); - if (d.klass) classes += ' ' + d.klass; - return classes; - }); - - var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; }); - - actionableItems - .append('div') - .attr('class', 'item-content') - .each(function(d) { - select(this).call(d.render, bar); - }); - - actionableItems - .append('div') - .attr('class', 'item-label') - .text(function(d) { - return d.label; - }); - } - - } - - return topToolbar; - } - - // these are module variables so they are preserved through a ui.restart() - var sawVersion = null; - var isNewVersion = false; - var isNewUser = false; - - - function uiVersion(context) { - - var currVersion = context.version; - var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/); - - if (sawVersion === null && matchedVersion !== null) { - if (corePreferences('sawVersion')) { - isNewUser = false; - isNewVersion = corePreferences('sawVersion') !== currVersion; - } else { - isNewUser = true; - isNewVersion = true; - } - corePreferences('sawVersion', currVersion); - sawVersion = currVersion; - } - - return function(selection) { - selection - .append('a') - .attr('target', '_blank') - .attr('href', 'https://github.com/openstreetmap/iD') - .text(currVersion); - - // only show new version indicator to users that have used iD before - if (isNewVersion && !isNewUser) { - selection - .append('div') - .attr('class', 'badge') - .append('a') - .attr('target', '_blank') - .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new') - .call(svgIcon('#maki-gift-11')) - .call(uiTooltip() - .title(_t('version.whats_new', { version: currVersion })) - .placement('top') - ); - } - }; - } - - function uiZoom(context) { - - var zooms = [{ - id: 'zoom-in', - icon: 'iD-icon-plus', - title: _t('zoom.in'), - action: zoomIn, - disabled: function() { - return !context.map().canZoomIn(); - }, - disabledTitle: _t('zoom.disabled.in'), - key: '+' - }, { - id: 'zoom-out', - icon: 'iD-icon-minus', - title: _t('zoom.out'), - action: zoomOut, - disabled: function() { - return !context.map().canZoomOut(); - }, - disabledTitle: _t('zoom.disabled.out'), - key: '-' - }]; - - function zoomIn() { - event.preventDefault(); - context.map().zoomIn(); - } - - function zoomOut() { - event.preventDefault(); - context.map().zoomOut(); - } - - function zoomInFurther() { - event.preventDefault(); - context.map().zoomInFurther(); - } - - function zoomOutFurther() { - event.preventDefault(); - context.map().zoomOutFurther(); - } - - return function(selection) { - var tooltipBehavior = uiTooltip() - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - .title(function(d) { - if (d.disabled()) { - return d.disabledTitle; - } - return d.title; - }) - .keys(function(d) { - return [d.key]; - }); - - var lastPointerUpType; - - var buttons = selection.selectAll('button') - .data(zooms) - .enter() - .append('button') - .attr('class', function(d) { return d.id; }) - .on('pointerup.editor', function() { - lastPointerUpType = event.pointerType; - }) - .on('click.editor', function(d) { - if (!d.disabled()) { - d.action(); - } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') { - context.ui().flash - .duration(2000) - .iconName('#' + d.icon) - .iconClass('disabled') - .text(d.disabledTitle)(); - } - lastPointerUpType = null; - }) - .call(tooltipBehavior); - - buttons.each(function(d) { - select(this) - .call(svgIcon('#' + d.icon, 'light')); - }); - - ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) { - context.keybinding().on([key], zoomIn); - context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther); - }); - - ['_', '-', 'ffminus', 'dash'].forEach(function(key) { - context.keybinding().on([key], zoomOut); - context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther); - }); - - function updateButtonStates() { - buttons - .classed('disabled', function(d) { - return d.disabled(); - }) - .each(function() { - var selection = select(this); - if (!selection.select('.tooltip.in').empty()) { - selection.call(tooltipBehavior.updateContent); - } - }); - } - - updateButtonStates(); - - context.map().on('move.uiZoom', updateButtonStates); - }; - } - - function uiZoomToSelection(context) { - - function isDisabled() { - var mode = context.mode(); - return !mode || !mode.zoomToSelected; - } - - var _lastPointerUpType; - - function pointerup() { - _lastPointerUpType = event.pointerType; - } - - function click() { - event.preventDefault(); - - if (isDisabled()) { - if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') { - context.ui().flash - .duration(2000) - .iconName('#iD-icon-framed-dot') - .iconClass('disabled') - .text(_t('inspector.zoom_to.no_selection'))(); - } - } else { - var mode = context.mode(); - if (mode && mode.zoomToSelected) { - mode.zoomToSelected(); - } - } - - _lastPointerUpType = null; - } - - return function(selection) { - - var tooltipBehavior = uiTooltip() - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - .title(function() { - if (isDisabled()) { - return _t('inspector.zoom_to.no_selection'); - } - return _t('inspector.zoom_to.title'); - }) - .keys([_t('inspector.zoom_to.key')]); - - var button = selection - .append('button') - .on('pointerup', pointerup) - .on('click', click) - .call(svgIcon('#iD-icon-framed-dot', 'light')) - .call(tooltipBehavior); - - function setEnabledState() { - button.classed('disabled', isDisabled()); - if (!button.select('.tooltip.in').empty()) { - button.call(tooltipBehavior.updateContent); - } - } - - context.on('enter.uiZoomToSelection', setEnabledState); - - setEnabledState(); - }; - } - - function uiPane(id, context) { - - var _key; - var _title = ''; - var _description = ''; - var _iconName = ''; - var _sections; // array of uiSection objects - - var _paneSelection = select(null); - - var _paneTooltip; - - var pane = { - id: id - }; - - pane.title = function(val) { - if (!arguments.length) return _title; - _title = val; - return pane; - }; - - pane.key = function(val) { - if (!arguments.length) return _key; - _key = val; - return pane; - }; - - pane.description = function(val) { - if (!arguments.length) return _description; - _description = val; - return pane; - }; - - pane.iconName = function(val) { - if (!arguments.length) return _iconName; - _iconName = val; - return pane; - }; - - pane.sections = function(val) { - if (!arguments.length) return _sections; - _sections = val; - return pane; - }; - - pane.selection = function() { - return _paneSelection; - }; - - function hidePane() { - context.ui().togglePanes(); - } - - pane.togglePane = function() { - if (event) event.preventDefault(); - _paneTooltip.hide(); - context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined); - }; - - pane.renderToggleButton = function(selection) { - - if (!_paneTooltip) { - _paneTooltip = uiTooltip() - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - .title(_description) - .keys([_key]); - } - - selection - .append('button') - .on('click', pane.togglePane) - .call(svgIcon('#' + _iconName, 'light')) - .call(_paneTooltip); - }; - - pane.renderContent = function(selection) { - // override to fully customize content - - if (_sections) { - _sections.forEach(function(section) { - selection.call(section.render); - }); - } - }; - - pane.renderPane = function(selection) { - - _paneSelection = selection - .append('div') - .attr('class', 'fillL map-pane hide ' + id + '-pane') - .attr('pane', id); - - var heading = _paneSelection - .append('div') - .attr('class', 'pane-heading'); - - heading - .append('h2') - .text(_title); - - heading - .append('button') - .on('click', hidePane) - .call(svgIcon('#iD-icon-close')); - - - _paneSelection - .append('div') - .attr('class', 'pane-content') - .call(pane.renderContent); - - if (_key) { - context.keybinding() - .on(_key, pane.togglePane); - } - }; - - return pane; - } - - function uiSectionBackgroundDisplayOptions(context) { - - var section = uiSection('background-display-options', context) - .title(_t('background.display_options')) - .disclosureContent(renderDisclosureContent); - - var _detected = utilDetect(); - var _storedOpacity = corePreferences('background-opacity'); - var _minVal = 0.25; - var _maxVal = _detected.cssfilters ? 2 : 1; - - var _sliders = _detected.cssfilters - ? ['brightness', 'contrast', 'saturation', 'sharpness'] - : ['brightness']; - - var _options = { - brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1), - contrast: 1, - saturation: 1, - sharpness: 1 - }; - - function clamp(x, min, max) { - return Math.max(min, Math.min(x, max)); - } - - function updateValue(d, val) { - if (!val && event && event.target) { - val = event.target.value; - } - - val = clamp(val, _minVal, _maxVal); - - _options[d] = val; - context.background()[d](val); - - if (d === 'brightness') { - corePreferences('background-opacity', val); - } - - section.reRender(); - } - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.display-options-container') - .data([0]); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'display-options-container controls-list'); - - // add slider controls - var slidersEnter = containerEnter.selectAll('.display-control') - .data(_sliders) - .enter() - .append('div') - .attr('class', function(d) { return 'display-control display-control-' + d; }); - - slidersEnter - .append('h5') - .text(function(d) { return _t('background.' + d); }) - .append('span') - .attr('class', function(d) { return 'display-option-value display-option-value-' + d; }); - - slidersEnter - .append('input') - .attr('class', function(d) { return 'display-option-input display-option-input-' + d; }) - .attr('type', 'range') - .attr('min', _minVal) - .attr('max', _maxVal) - .attr('step', '0.05') - .on('input', function(d) { - var val = select(this).property('value'); - updateValue(d, val); - }); - - slidersEnter - .append('button') - .attr('title', _t('background.reset')) - .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; }) - .on('click', function(d) { - if (event.button !== 0) return; - updateValue(d, 1); - }) - .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); - - // reset all button - containerEnter - .append('a') - .attr('class', 'display-option-resetlink') - .attr('href', '#') - .text(_t('background.reset_all')) - .on('click', function() { - for (var i = 0; i < _sliders.length; i++) { - updateValue(_sliders[i],1); - } - }); - - // update - container = containerEnter - .merge(container); - - container.selectAll('.display-option-input') - .property('value', function(d) { return _options[d]; }); - - container.selectAll('.display-option-value') - .text(function(d) { return Math.floor(_options[d] * 100) + '%'; }); - - container.selectAll('.display-option-reset') - .classed('disabled', function(d) { return _options[d] === 1; }); - - // first time only, set brightness if needed - if (containerEnter.size() && _options.brightness !== 1) { - context.background().brightness(_options.brightness); - } - } - - return section; - } - - function uiSettingsCustomBackground() { - var dispatch$1 = dispatch('change'); - - function render(selection) { - // keep separate copies of original and current settings - var _origSettings = { - template: corePreferences('background-custom-template') - }; - var _currSettings = { - template: corePreferences('background-custom-template') - }; - - var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; - var modal = uiConfirm(selection).okButton(); - - modal - .classed('settings-modal settings-custom-background', true); - - modal.select('.modal-section.header') - .append('h3') - .text(_t('settings.custom_background.header')); - - - var textSection = modal.select('.modal-section.message-text'); - - var instructions = - `${_t('settings.custom_background.instructions.info')}\n` + - '\n' + - `#### ${_t('settings.custom_background.instructions.wms.tokens_label')}\n` + - `* ${_t('settings.custom_background.instructions.wms.tokens.proj')}\n` + - `* ${_t('settings.custom_background.instructions.wms.tokens.wkid')}\n` + - `* ${_t('settings.custom_background.instructions.wms.tokens.dimensions')}\n` + - `* ${_t('settings.custom_background.instructions.wms.tokens.bbox')}\n` + - '\n' + - `#### ${_t('settings.custom_background.instructions.tms.tokens_label')}\n` + - `* ${_t('settings.custom_background.instructions.tms.tokens.xyz')}\n` + - `* ${_t('settings.custom_background.instructions.tms.tokens.flipped_y')}\n` + - `* ${_t('settings.custom_background.instructions.tms.tokens.switch')}\n` + - `* ${_t('settings.custom_background.instructions.tms.tokens.quadtile')}\n` + - `* ${_t('settings.custom_background.instructions.tms.tokens.scale_factor')}\n` + - '\n' + - `#### ${_t('settings.custom_background.instructions.example')}\n` + - `\`${example}\``; - - textSection - .append('div') - .attr('class', 'instructions-template') - .html(marked_1(instructions)); - - textSection - .append('textarea') - .attr('class', 'field-template') - .attr('placeholder', _t('settings.custom_background.template.placeholder')) - .call(utilNoAuto) - .property('value', _currSettings.template); - - - // insert a cancel button - var buttonSection = modal.select('.modal-section.buttons'); - - buttonSection - .insert('button', '.ok-button') - .attr('class', 'button cancel-button secondary-action') - .text(_t('confirm.cancel')); - - - buttonSection.select('.cancel-button') - .on('click.cancel', clickCancel); - - buttonSection.select('.ok-button') - .attr('disabled', isSaveDisabled) - .on('click.save', clickSave); - - - function isSaveDisabled() { - return null; - } - - - // restore the original template - function clickCancel() { - textSection.select('.field-template').property('value', _origSettings.template); - corePreferences('background-custom-template', _origSettings.template); - this.blur(); - modal.close(); - } - - // accept the current template - function clickSave() { - _currSettings.template = textSection.select('.field-template').property('value'); - corePreferences('background-custom-template', _currSettings.template); - this.blur(); - modal.close(); - dispatch$1.call('change', this, _currSettings); - } - } - - return utilRebind(render, dispatch$1, 'on'); - } - - function uiSectionBackgroundList(context) { - - var _backgroundList = select(null); - - var _customSource = context.background().findSource('custom'); - - var _settingsCustomBackground = uiSettingsCustomBackground() - .on('change', customChanged); - - var section = uiSection('background-list', context) - .title(_t('background.backgrounds')) - .disclosureContent(renderDisclosureContent); - - function previousBackgroundID() { - return corePreferences('background-last-used-toggle'); - } - - function renderDisclosureContent(selection) { - - // the background list - var container = selection.selectAll('.layer-background-list') - .data([0]); - - _backgroundList = container.enter() - .append('ul') - .attr('class', 'layer-list layer-background-list') - .attr('dir', 'auto') - .merge(container); - - - // add minimap toggle below list - var bgExtrasListEnter = selection.selectAll('.bg-extras-list') - .data([0]) - .enter() - .append('ul') - .attr('class', 'layer-list bg-extras-list'); - - var minimapLabelEnter = bgExtrasListEnter - .append('li') - .attr('class', 'minimap-toggle-item') - .append('label') - .call(uiTooltip() - .title(_t('background.minimap.tooltip')) - .keys([_t('background.minimap.key')]) - .placement('top') - ); - - minimapLabelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { - event.preventDefault(); - uiMapInMap.toggle(); - }); - - minimapLabelEnter - .append('span') - .text(_t('background.minimap.description')); - - - var panelLabelEnter = bgExtrasListEnter - .append('li') - .attr('class', 'background-panel-toggle-item') - .append('label') - .call(uiTooltip() - .title(_t('background.panel.tooltip')) - .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))]) - .placement('top') - ); - - panelLabelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { - event.preventDefault(); - context.ui().info.toggle('background'); - }); - - panelLabelEnter - .append('span') - .text(_t('background.panel.description')); - - var locPanelLabelEnter = bgExtrasListEnter - .append('li') - .attr('class', 'location-panel-toggle-item') - .append('label') - .call(uiTooltip() - .title(_t('background.location_panel.tooltip')) - .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))]) - .placement('top') - ); - - locPanelLabelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { - event.preventDefault(); - context.ui().info.toggle('location'); - }); - - locPanelLabelEnter - .append('span') - .text(_t('background.location_panel.description')); - - - // "Info / Report a Problem" link - selection.selectAll('.imagery-faq') - .data([0]) - .enter() - .append('div') - .attr('class', 'imagery-faq') - .append('a') - .attr('target', '_blank') - .call(svgIcon('#iD-icon-out-link', 'inline')) - .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery') - .append('span') - .text(_t('background.imagery_problem_faq')); - - _backgroundList - .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; }); - } - - function setTooltips(selection) { - selection.each(function(d, i, nodes) { - var item = select(this).select('label'); - var span = item.select('span'); - var placement = (i < nodes.length / 2) ? 'bottom' : 'top'; - var description = d.description(); - var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth')); - - item.call(uiTooltip().destroyAny); - - if (d.id === previousBackgroundID()) { - item.call(uiTooltip() - .placement(placement) - .title('

    ' + _t('background.switch') + '
    ') - .keys([uiCmd('⌘' + _t('background.key'))]) - ); - } else if (description || isOverflowing) { - item.call(uiTooltip() - .placement(placement) - .title(description || d.name()) - ); - } - }); - } - - function drawListItems(layerList, type, change, filter) { - var sources = context.background() - .sources(context.map().extent(), context.map().zoom(), true) - .filter(filter); - - var layerLinks = layerList.selectAll('li') - .data(sources, function(d) { return d.name(); }); - - layerLinks.exit() - .remove(); - - var enter = layerLinks.enter() - .append('li') - .classed('layer-custom', function(d) { return d.id === 'custom'; }) - .classed('best', function(d) { return d.best(); }); - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', type) - .attr('name', 'layers') - .on('change', change); - - label - .append('span') - .text(function(d) { return d.name(); }); - - enter.filter(function(d) { return d.id === 'custom'; }) - .append('button') - .attr('class', 'layer-browse') - .call(uiTooltip() - .title(_t('settings.custom_background.tooltip')) - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - ) - .on('click', editCustom) - .call(svgIcon('#iD-icon-more')); - - enter.filter(function(d) { return d.best(); }) - .append('div') - .attr('class', 'best') - .call(uiTooltip() - .title(_t('background.best_imagery')) - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - ) - .append('span') - .html('★'); - - - layerList.selectAll('li') - .sort(sortSources); - - layerList - .call(updateLayerSelections); - - - function sortSources(a, b) { - return a.best() && !b.best() ? -1 - : b.best() && !a.best() ? 1 - : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0; - } - } - - function updateLayerSelections(selection) { - function active(d) { - return context.background().showsLayer(d); - } - - selection.selectAll('li') - .classed('active', active) - .classed('switch', function(d) { return d.id === previousBackgroundID(); }) - .call(setTooltips) - .selectAll('input') - .property('checked', active); - } - - - function chooseBackground(d) { - if (d.id === 'custom' && !d.template()) { - return editCustom(); - } - - event.preventDefault(); - var previousBackground = context.background().baseLayerSource(); - corePreferences('background-last-used-toggle', previousBackground.id); - corePreferences('background-last-used', d.id); - context.background().baseLayerSource(d); - document.activeElement.blur(); - } - - - function customChanged(d) { - if (d && d.template) { - _customSource.template(d.template); - chooseBackground(_customSource); - } else { - _customSource.template(''); - chooseBackground(context.background().findSource('none')); - } - } - - - function editCustom() { - event.preventDefault(); - context.container() - .call(_settingsCustomBackground); - } - - - context.background() - .on('change.background_list', function() { - _backgroundList.call(updateLayerSelections); - }); - - context.map() - .on('move.background_list', - debounce(function() { - // layers in-view may have changed due to map move - window.requestIdleCallback(section.reRender); - }, 1000) - ); - - return section; - } - - function uiSectionBackgroundOffset(context) { - - var section = uiSection('background-offset', context) - .title(_t('background.fix_misalignment')) - .disclosureContent(renderDisclosureContent) - .expandedByDefault(false); - - var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; - - var _directions = [ - ['right', [0.5, 0]], - ['top', [0, -0.5]], - ['left', [-0.5, 0]], - ['bottom', [0, 0.5]] - ]; - - - function cancelEvent() { - event.stopPropagation(); - event.preventDefault(); - } - - - function updateValue() { - var meters = geoOffsetToMeters(context.background().offset()); - var x = +meters[0].toFixed(2); - var y = +meters[1].toFixed(2); - - context.container().selectAll('.nudge-inner-rect') - .select('input') - .classed('error', false) - .property('value', x + ', ' + y); - - context.container().selectAll('.nudge-reset') - .classed('disabled', function() { - return (x === 0 && y === 0); - }); - } - - - function resetOffset() { - context.background().offset([0, 0]); - updateValue(); - } - - - function nudge(d) { - context.background().nudge(d, context.map().zoom()); - updateValue(); - } - - - function pointerdownNudgeButton(d) { - var interval; - var timeout = window.setTimeout(function() { - interval = window.setInterval(nudge.bind(null, d), 100); - }, 500); - - function doneNudge() { - window.clearTimeout(timeout); - window.clearInterval(interval); - select(window) - .on(_pointerPrefix + 'up.buttonoffset', null, true) - .on(_pointerPrefix + 'down.buttonoffset', null, true); - } - - select(window) - .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true) - .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true); - - nudge(d); - } - - - function inputOffset() { - var input = select(this); - var d = input.node().value; - - if (d === '') return resetOffset(); - - d = d.replace(/;/g, ',').split(',').map(function(n) { - // if n is NaN, it will always get mapped to false. - return !isNaN(n) && n; - }); - - if (d.length !== 2 || !d[0] || !d[1]) { - input.classed('error', true); - return; - } - - context.background().offset(geoMetersToOffset(d)); - updateValue(); - } - - - function dragOffset() { - if (event.button !== 0) return; - - var origin = [event.clientX, event.clientY]; - - var pointerId = event.pointerId || 'mouse'; - - context.container() - .append('div') - .attr('class', 'nudge-surface'); - - select(window) - .on(_pointerPrefix + 'move.drag-bg-offset', pointermove) - .on(_pointerPrefix + 'up.drag-bg-offset', pointerup); - - if (_pointerPrefix === 'pointer') { - select(window) - .on('pointercancel.drag-bg-offset', pointerup); - } - - function pointermove() { - if (pointerId !== (event.pointerId || 'mouse')) return; - - var latest = [event.clientX, event.clientY]; - var d = [ - -(origin[0] - latest[0]) / 4, - -(origin[1] - latest[1]) / 4 - ]; - - origin = latest; - nudge(d); - } - - function pointerup() { - if (pointerId !== (event.pointerId || 'mouse')) return; - if (event.button !== 0) return; - - context.container().selectAll('.nudge-surface') - .remove(); - - select(window) - .on('.drag-bg-offset', null); - } - } - - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.nudge-container') - .data([0]); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'nudge-container cf'); - - containerEnter - .append('div') - .attr('class', 'nudge-instructions') - .text(_t('background.offset')); - - var nudgeEnter = containerEnter - .append('div') - .attr('class', 'nudge-outer-rect') - .on(_pointerPrefix + 'down', dragOffset); - - nudgeEnter - .append('div') - .attr('class', 'nudge-inner-rect') - .append('input') - .on('change', inputOffset); - - containerEnter - .append('div') - .selectAll('button') - .data(_directions).enter() - .append('button') - .attr('class', function(d) { return d[0] + ' nudge'; }) - .on('contextmenu', cancelEvent) - .on(_pointerPrefix + 'down', function(d) { - if (event.button !== 0) return; - pointerdownNudgeButton(d[1]); - }); - - containerEnter - .append('button') - .attr('title', _t('background.reset')) - .attr('class', 'nudge-reset disabled') - .on('contextmenu', cancelEvent) - .on('click', function() { - event.preventDefault(); - if (event.button !== 0) return; - resetOffset(); - }) - .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); - - updateValue(); - } - - context.background() - .on('change.backgroundOffset-update', updateValue); - - return section; - } - - function uiSectionOverlayList(context) { - - var section = uiSection('overlay-list', context) - .title(_t('background.overlays')) - .disclosureContent(renderDisclosureContent); - - var _overlayList = select(null); - - function setTooltips(selection) { - selection.each(function(d, i, nodes) { - var item = select(this).select('label'); - var span = item.select('span'); - var placement = (i < nodes.length / 2) ? 'bottom' : 'top'; - var description = d.description(); - var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth')); - - item.call(uiTooltip().destroyAny); - - if (description || isOverflowing) { - item.call(uiTooltip() - .placement(placement) - .title(description || d.name()) - ); - } - }); - } - - function updateLayerSelections(selection) { - function active(d) { - return context.background().showsLayer(d); - } - - selection.selectAll('li') - .classed('active', active) - .call(setTooltips) - .selectAll('input') - .property('checked', active); - } - - - function chooseOverlay(d) { - event.preventDefault(); - context.background().toggleOverlayLayer(d); - _overlayList.call(updateLayerSelections); - document.activeElement.blur(); - } - - function drawListItems(layerList, type, change, filter) { - var sources = context.background() - .sources(context.map().extent(), context.map().zoom(), true) - .filter(filter); - - var layerLinks = layerList.selectAll('li') - .data(sources, function(d) { return d.name(); }); - - layerLinks.exit() - .remove(); - - var enter = layerLinks.enter() - .append('li'); - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', type) - .attr('name', 'layers') - .on('change', change); - - label - .append('span') - .text(function(d) { return d.name(); }); - - - layerList.selectAll('li') - .sort(sortSources); - - layerList - .call(updateLayerSelections); - - - function sortSources(a, b) { - return a.best() && !b.best() ? -1 - : b.best() && !a.best() ? 1 - : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0; - } - } - - function renderDisclosureContent(selection) { - - var container = selection.selectAll('.layer-overlay-list') - .data([0]); - - _overlayList = container.enter() - .append('ul') - .attr('class', 'layer-list layer-overlay-list') - .attr('dir', 'auto') - .merge(container); - - _overlayList - .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; }); - } - - context.map() - .on('move.overlay_list', - debounce(function() { - // layers in-view may have changed due to map move - window.requestIdleCallback(section.reRender); - }, 1000) - ); - - return section; - } - - function uiPaneBackground(context) { - - var backgroundPane = uiPane('background', context) - .key(_t('background.key')) - .title(_t('background.title')) - .description(_t('background.description')) - .iconName('iD-icon-layers') - .sections([ - uiSectionBackgroundList(context), - uiSectionOverlayList(context), - uiSectionBackgroundDisplayOptions(context), - uiSectionBackgroundOffset(context) - ]); - - return backgroundPane; - } - - function uiPaneHelp(context) { - - var docKeys = [ - ['help', [ - 'welcome', - 'open_data_h', - 'open_data', - 'before_start_h', - 'before_start', - 'open_source_h', - 'open_source', - 'open_source_help' - ]], - ['overview', [ - 'navigation_h', - 'navigation_drag', - 'navigation_zoom', - 'features_h', - 'features', - 'nodes_ways' - ]], - ['editing', [ - 'select_h', - 'select_left_click', - 'select_right_click', - 'select_space', - 'multiselect_h', - 'multiselect', - 'multiselect_shift_click', - 'multiselect_lasso', - 'undo_redo_h', - 'undo_redo', - 'save_h', - 'save', - 'save_validation', - 'upload_h', - 'upload', - 'backups_h', - 'backups', - 'keyboard_h', - 'keyboard' - ]], - ['feature_editor', [ - 'intro', - 'definitions', - 'type_h', - 'type', - 'type_picker', - 'fields_h', - 'fields_all_fields', - 'fields_example', - 'fields_add_field', - 'tags_h', - 'tags_all_tags', - 'tags_resources' - ]], - ['points', [ - 'intro', - 'add_point_h', - 'add_point', - 'add_point_finish', - 'move_point_h', - 'move_point', - 'delete_point_h', - 'delete_point', - 'delete_point_command' - ]], - ['lines', [ - 'intro', - 'add_line_h', - 'add_line', - 'add_line_draw', - 'add_line_continue', - 'add_line_finish', - 'modify_line_h', - 'modify_line_dragnode', - 'modify_line_addnode', - 'connect_line_h', - 'connect_line', - 'connect_line_display', - 'connect_line_drag', - 'connect_line_tag', - 'disconnect_line_h', - 'disconnect_line_command', - 'move_line_h', - 'move_line_command', - 'move_line_connected', - 'delete_line_h', - 'delete_line', - 'delete_line_command' - ]], - ['areas', [ - 'intro', - 'point_or_area_h', - 'point_or_area', - 'add_area_h', - 'add_area_command', - 'add_area_draw', - 'add_area_continue', - 'add_area_finish', - 'square_area_h', - 'square_area_command', - 'modify_area_h', - 'modify_area_dragnode', - 'modify_area_addnode', - 'delete_area_h', - 'delete_area', - 'delete_area_command' - ]], - ['relations', [ - 'intro', - 'edit_relation_h', - 'edit_relation', - 'edit_relation_add', - 'edit_relation_delete', - 'maintain_relation_h', - 'maintain_relation', - 'relation_types_h', - 'multipolygon_h', - 'multipolygon', - 'multipolygon_create', - 'multipolygon_merge', - 'turn_restriction_h', - 'turn_restriction', - 'turn_restriction_field', - 'turn_restriction_editing', - 'route_h', - 'route', - 'route_add', - 'boundary_h', - 'boundary', - 'boundary_add' - ]], - ['notes', [ - 'intro', - 'add_note_h', - 'add_note', - 'place_note', - 'move_note', - 'update_note_h', - 'update_note', - 'save_note_h', - 'save_note' - ]], - ['imagery', [ - 'intro', - 'sources_h', - 'choosing', - 'sources', - 'offsets_h', - 'offset', - 'offset_change' - ]], - ['streetlevel', [ - 'intro', - 'using_h', - 'using', - 'photos', - 'viewer' - ]], - ['gps', [ - 'intro', - 'survey', - 'using_h', - 'using', - 'tracing', - 'upload' - ]], - ['qa', [ - 'intro', - 'tools_h', - 'tools', - 'issues_h', - 'issues' - ]] - ]; - - var headings = { - 'help.help.open_data_h': 3, - 'help.help.before_start_h': 3, - 'help.help.open_source_h': 3, - 'help.overview.navigation_h': 3, - 'help.overview.features_h': 3, - 'help.editing.select_h': 3, - 'help.editing.multiselect_h': 3, - 'help.editing.undo_redo_h': 3, - 'help.editing.save_h': 3, - 'help.editing.upload_h': 3, - 'help.editing.backups_h': 3, - 'help.editing.keyboard_h': 3, - 'help.feature_editor.type_h': 3, - 'help.feature_editor.fields_h': 3, - 'help.feature_editor.tags_h': 3, - 'help.points.add_point_h': 3, - 'help.points.move_point_h': 3, - 'help.points.delete_point_h': 3, - 'help.lines.add_line_h': 3, - 'help.lines.modify_line_h': 3, - 'help.lines.connect_line_h': 3, - 'help.lines.disconnect_line_h': 3, - 'help.lines.move_line_h': 3, - 'help.lines.delete_line_h': 3, - 'help.areas.point_or_area_h': 3, - 'help.areas.add_area_h': 3, - 'help.areas.square_area_h': 3, - 'help.areas.modify_area_h': 3, - 'help.areas.delete_area_h': 3, - 'help.relations.edit_relation_h': 3, - 'help.relations.maintain_relation_h': 3, - 'help.relations.relation_types_h': 2, - 'help.relations.multipolygon_h': 3, - 'help.relations.turn_restriction_h': 3, - 'help.relations.route_h': 3, - 'help.relations.boundary_h': 3, - 'help.notes.add_note_h': 3, - 'help.notes.update_note_h': 3, - 'help.notes.save_note_h': 3, - 'help.imagery.sources_h': 3, - 'help.imagery.offsets_h': 3, - 'help.streetlevel.using_h': 3, - 'help.gps.using_h': 3, - 'help.qa.tools_h': 3, - 'help.qa.issues_h': 3 - }; - - // For each section, squash all the texts into a single markdown document - var docs = docKeys.map(function(key) { - var helpkey = 'help.' + key[0]; - var helpPaneReplacements = { version: context.version }; - var text = key[1].reduce(function(all, part) { - var subkey = helpkey + '.' + part; - var depth = headings[subkey]; // is this subkey a heading? - var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s - return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n'; - }, ''); - - return { - title: _t(helpkey + '.title'), - html: marked_1(text.trim()) - // use keyboard key styling for shortcuts - .replace(//g, '') - .replace(/<\/code>/g, '<\/kbd>') - }; - }); - - var helpPane = uiPane('help', context) - .key(_t('help.key')) - .title(_t('help.title')) - .description(_t('help.title')) - .iconName('iD-icon-help'); - - helpPane.renderContent = function(content) { - - function clickHelp(d, i) { - var rtl = (_mainLocalizer.textDirection() === 'rtl'); - content.property('scrollTop', 0); - helpPane.selection().select('.pane-heading h2').html(d.title); - - body.html(d.html); - body.selectAll('a') - .attr('target', '_blank'); - menuItems.classed('selected', function(m) { - return m.title === d.title; - }); - - nav.html(''); - if (rtl) { - nav.call(drawNext).call(drawPrevious); - } else { - nav.call(drawPrevious).call(drawNext); - } - - - function drawNext(selection) { - if (i < docs.length - 1) { - var nextLink = selection - .append('a') - .attr('class', 'next') - .on('click', function() { - clickHelp(docs[i + 1], i + 1); - }); - - nextLink - .append('span') - .text(docs[i + 1].title) - .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline')); - } - } - - - function drawPrevious(selection) { - if (i > 0) { - var prevLink = selection - .append('a') - .attr('class', 'previous') - .on('click', function() { - clickHelp(docs[i - 1], i - 1); - }); - - prevLink - .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline')) - .append('span') - .text(docs[i - 1].title); - } - } - } - - - function clickWalkthrough() { - if (context.inIntro()) return; - context.container().call(uiIntro(context)); - context.ui().togglePanes(); - } - - - function clickShortcuts() { - context.container().call(uiShortcuts(context), true); - } - - var toc = content - .append('ul') - .attr('class', 'toc'); - - var menuItems = toc.selectAll('li') - .data(docs) - .enter() - .append('li') - .append('a') - .html(function(d) { return d.title; }) - .on('click', clickHelp); - - var shortcuts = toc - .append('li') - .attr('class', 'shortcuts') - .call(uiTooltip() - .title(_t('shortcuts.tooltip')) - .keys(['?']) - .placement('top') - ) - .append('a') - .on('click', clickShortcuts); - - shortcuts - .append('div') - .text(_t('shortcuts.title')); - - var walkthrough = toc - .append('li') - .attr('class', 'walkthrough') - .append('a') - .on('click', clickWalkthrough); - - walkthrough - .append('svg') - .attr('class', 'logo logo-walkthrough') - .append('use') - .attr('xlink:href', '#iD-logo-walkthrough'); - - walkthrough - .append('div') - .text(_t('splash.walkthrough')); - - - var helpContent = content - .append('div') - .attr('class', 'left-content'); - - var body = helpContent - .append('div') - .attr('class', 'body'); - - var nav = helpContent - .append('div') - .attr('class', 'nav'); - - clickHelp(docs[0], 0); - - }; - - return helpPane; - } - - function uiSectionValidationIssues(id, severity, context) { - - var _issues = []; - - var section = uiSection(id, context) - .title(function() { - if (!_issues) return ''; - var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length); - return _t('issues.' + severity + 's.list_title', { count: issueCountText }); - }) - .disclosureContent(renderDisclosureContent) - .shouldDisplay(function() { - return _issues && _issues.length; - }); - - function getOptions() { - return { - what: corePreferences('validate-what') || 'edited', - where: corePreferences('validate-where') || 'all' - }; - } - - // get and cache the issues to display, unordered - function reloadIssues() { - _issues = context.validator().getIssuesBySeverity(getOptions())[severity]; - } - - function renderDisclosureContent(selection) { - - var center = context.map().center(); - var graph = context.graph(); - - // sort issues by distance away from the center of the map - var issues = _issues.map(function withDistance(issue) { - var extent = issue.extent(graph); - var dist = extent ? geoSphericalDistance(center, extent.center()) : 0; - return Object.assign(issue, { dist: dist }); - }) - .sort(function byDistance(a, b) { - return a.dist - b.dist; - }); - - // cut off at 1000 - issues = issues.slice(0, 1000); - - //renderIgnoredIssuesReset(_warningsSelection); - - selection - .call(drawIssuesList, issues); - } - - function drawIssuesList(selection, issues) { - var list = selection.selectAll('.issues-list') - .data([0]); - - list = list.enter() - .append('ul') - .attr('class', 'layer-list issues-list ' + severity + 's-list') - .merge(list); - - - var items = list.selectAll('li') - .data(issues, function(d) { return d.id; }); - - // Exit - items.exit() - .remove(); - - // Enter - var itemsEnter = items.enter() - .append('li') - .attr('class', function (d) { return 'issue severity-' + d.severity; }) - .on('click', function(d) { - context.validator().focusIssue(d); - }) - .on('mouseover', function(d) { - utilHighlightEntities(d.entityIds, true, context); - }) - .on('mouseout', function(d) { - utilHighlightEntities(d.entityIds, false, context); - }); - - - var labelsEnter = itemsEnter - .append('div') - .attr('class', 'issue-label'); - - var textEnter = labelsEnter - .append('span') - .attr('class', 'issue-text'); - - textEnter - .append('span') - .attr('class', 'issue-icon') - .each(function(d) { - var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error'); - select(this) - .call(svgIcon(iconName)); - }); - - textEnter - .append('span') - .attr('class', 'issue-message'); - - /* - labelsEnter - .append('span') - .attr('class', 'issue-autofix') - .each(function(d) { - if (!d.autoFix) return; - - d3_select(this) - .append('button') - .attr('title', t('issues.fix_one.title')) - .datum(d.autoFix) // set button datum to the autofix - .attr('class', 'autofix action') - .on('click', function(d) { - d3_event.preventDefault(); - d3_event.stopPropagation(); - - var issuesEntityIDs = d.issue.entityIds; - utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context); - - context.perform.apply(context, d.autoArgs); - context.validator().validate(); - }) - .call(svgIcon('#iD-icon-wrench')); - }); - */ - - // Update - items = items - .merge(itemsEnter) - .order(); - - items.selectAll('.issue-message') - .text(function(d) { - return d.message(context); - }); - - /* - // autofix - var canAutoFix = issues.filter(function(issue) { return issue.autoFix; }); - - var autoFixAll = selection.selectAll('.autofix-all') - .data(canAutoFix.length ? [0] : []); - - // exit - autoFixAll.exit() - .remove(); - - // enter - var autoFixAllEnter = autoFixAll.enter() - .insert('div', '.issues-list') - .attr('class', 'autofix-all'); - - var linkEnter = autoFixAllEnter - .append('a') - .attr('class', 'autofix-all-link') - .attr('href', '#'); - - linkEnter - .append('span') - .attr('class', 'autofix-all-link-text') - .text(t('issues.fix_all.title')); - - linkEnter - .append('span') - .attr('class', 'autofix-all-link-icon') - .call(svgIcon('#iD-icon-wrench')); - - if (severity === 'warning') { - renderIgnoredIssuesReset(selection); - } - - // update - autoFixAll = autoFixAll - .merge(autoFixAllEnter); - - autoFixAll.selectAll('.autofix-all-link') - .on('click', function() { - context.pauseChangeDispatch(); - context.perform(actionNoop()); - canAutoFix.forEach(function(issue) { - var args = issue.autoFix.autoArgs.slice(); // copy - if (typeof args[args.length - 1] !== 'function') { - args.pop(); - } - args.push(t('issues.fix_all.annotation')); - context.replace.apply(context, args); - }); - context.resumeChangeDispatch(); - context.validator().validate(); - }); - */ - } - - context.validator().on('validated.uiSectionValidationIssues' + id, function() { - window.requestIdleCallback(function() { - reloadIssues(); - section.reRender(); - }); - }); - - context.map().on('move.uiSectionValidationIssues' + id, - debounce(function() { - window.requestIdleCallback(function() { - if (getOptions().where === 'visible') { - // must refetch issues if they are viewport-dependent - reloadIssues(); - } - // always reload list to re-sort-by-distance - section.reRender(); - }); - }, 1000) - ); - - return section; - } - - function uiSectionValidationOptions(context) { - - var section = uiSection('issues-options', context) - .content(renderContent); - - function renderContent(selection) { - - var container = selection.selectAll('.issues-options-container') - .data([0]); - - container = container.enter() - .append('div') - .attr('class', 'issues-options-container') - .merge(container); - - var data = [ - { key: 'what', values: ['edited', 'all'] }, - { key: 'where', values: ['visible', 'all'] } - ]; - - var options = container.selectAll('.issues-option') - .data(data, function(d) { return d.key; }); - - var optionsEnter = options.enter() - .append('div') - .attr('class', function(d) { return 'issues-option issues-option-' + d.key; }); - - optionsEnter - .append('div') - .attr('class', 'issues-option-title') - .text(function(d) { return _t('issues.options.' + d.key + '.title'); }); - - var valuesEnter = optionsEnter.selectAll('label') - .data(function(d) { - return d.values.map(function(val) { return { value: val, key: d.key }; }); - }) - .enter() - .append('label'); - - valuesEnter - .append('input') - .attr('type', 'radio') - .attr('name', function(d) { return 'issues-option-' + d.key; }) - .attr('value', function(d) { return d.value; }) - .property('checked', function(d) { return getOptions()[d.key] === d.value; }) - .on('change', function(d) { updateOptionValue(d.key, d.value); }); - - valuesEnter - .append('span') - .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); }); - } - - function getOptions() { - return { - what: corePreferences('validate-what') || 'edited', // 'all', 'edited' - where: corePreferences('validate-where') || 'all' // 'all', 'visible' - }; - } - - function updateOptionValue(d, val) { - if (!val && event && event.target) { - val = event.target.value; - } - - corePreferences('validate-' + d, val); - context.validator().validate(); - } - - return section; - } - - function uiSectionValidationRules(context) { - - var MINSQUARE = 0; - var MAXSQUARE = 20; - var DEFAULTSQUARE = 5; // see also unsquare_way.js - - var section = uiSection('issues-rules', context) - .disclosureContent(renderDisclosureContent) - .title(_t('issues.rules.title')); - - var _ruleKeys = context.validator().getRuleKeys() - .filter(function(key) { return key !== 'maprules'; }) - .sort(function(key1, key2) { - // alphabetize by localized title - return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1; - }); - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.issues-rulelist-container') - .data([0]); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'issues-rulelist-container'); - - containerEnter - .append('ul') - .attr('class', 'layer-list issue-rules-list'); - - var ruleLinks = containerEnter - .append('div') - .attr('class', 'issue-rules-links section-footer'); - - ruleLinks - .append('a') - .attr('class', 'issue-rules-link') - .attr('href', '#') - .text(_t('issues.enable_all')) - .on('click', function() { - context.validator().disableRules([]); - }); - - ruleLinks - .append('a') - .attr('class', 'issue-rules-link') - .attr('href', '#') - .text(_t('issues.disable_all')) - .on('click', function() { - context.validator().disableRules(_ruleKeys); - }); - - - // Update - container = container - .merge(containerEnter); - - container.selectAll('.issue-rules-list') - .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled); - } - - function drawListItems(selection, data, type, name, change, active) { - var items = selection.selectAll('li') - .data(data); - - // Exit - items.exit() - .remove(); - - // Enter - var enter = items.enter() - .append('li'); - - if (name === 'rule') { - enter - .call(uiTooltip() - .title(function(d) { return _t('issues.' + d + '.tip'); }) - .placement('top') - ); - } - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', type) - .attr('name', name) - .on('change', change); - - label - .append('span') - .html(function(d) { - var params = {}; - if (d === 'unsquare_way') { - params.val = ''; - } - return _t('issues.' + d + '.title', params); - }); - - // Update - items = items - .merge(enter); - - items - .classed('active', active) - .selectAll('input') - .property('checked', active) - .property('indeterminate', false); - - - // user-configurable square threshold - var degStr = corePreferences('validate-square-degrees'); - if (degStr === null) { - degStr = '' + DEFAULTSQUARE; - } - - var span = items.selectAll('.square-degrees'); - var input = span.selectAll('.square-degrees-input') - .data([0]); - - // enter / update - input.enter() - .append('input') - .attr('type', 'number') - .attr('min', '' + MINSQUARE) - .attr('max', '' + MAXSQUARE) - .attr('step', '0.5') - .attr('class', 'square-degrees-input') - .call(utilNoAuto) - .on('click', function () { - event.preventDefault(); - event.stopPropagation(); - this.select(); - }) - .on('keyup', function () { - if (event.keyCode === 13) { // enter - this.blur(); - this.select(); - } - }) - .on('blur', changeSquare) - .merge(input) - .property('value', degStr); - } - - function changeSquare() { - var input = select(this); - var degStr = utilGetSetValue(input).trim(); - var degNum = parseFloat(degStr, 10); - - if (!isFinite(degNum)) { - degNum = DEFAULTSQUARE; - } else if (degNum > MAXSQUARE) { - degNum = MAXSQUARE; - } else if (degNum < MINSQUARE) { - degNum = MINSQUARE; - } - - degNum = Math.round(degNum * 10 ) / 10; // round to 1 decimal - degStr = '' + degNum; - - input - .property('value', degStr); - - corePreferences('validate-square-degrees', degStr); - context.validator().reloadUnsquareIssues(); - } - - function isRuleEnabled(d) { - return context.validator().isRuleEnabled(d); - } - - function toggleRule(d) { - context.validator().toggleRule(d); - } - - context.validator().on('validated.uiSectionValidationRules', function() { - window.requestIdleCallback(section.reRender); - }); - - return section; - } - - function uiSectionValidationStatus(context) { - - var section = uiSection('issues-status', context) - .content(renderContent) - .shouldDisplay(function() { - var issues = context.validator().getIssues(getOptions()); - return issues.length === 0; - }); - - function getOptions() { - return { - what: corePreferences('validate-what') || 'edited', - where: corePreferences('validate-where') || 'all' - }; - } - - function renderContent(selection) { - - var box = selection.selectAll('.box') - .data([0]); - - var boxEnter = box.enter() - .append('div') - .attr('class', 'box'); - - boxEnter - .append('div') - .call(svgIcon('#iD-icon-apply', 'pre-text')); - - var noIssuesMessage = boxEnter - .append('span'); - - noIssuesMessage - .append('strong') - .attr('class', 'message'); - - noIssuesMessage - .append('br'); - - noIssuesMessage - .append('span') - .attr('class', 'details'); - - renderIgnoredIssuesReset(selection); - setNoIssuesText(selection); - } - - function renderIgnoredIssuesReset(selection) { - - var ignoredIssues = context.validator() - .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' }); - - var resetIgnored = selection.selectAll('.reset-ignored') - .data(ignoredIssues.length ? [0] : []); - - // exit - resetIgnored.exit() - .remove(); - - // enter - var resetIgnoredEnter = resetIgnored.enter() - .append('div') - .attr('class', 'reset-ignored section-footer'); - - resetIgnoredEnter - .append('a') - .attr('href', '#'); - - // update - resetIgnored = resetIgnored - .merge(resetIgnoredEnter); - - resetIgnored.select('a') - .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() })); - - resetIgnored.on('click', function() { - context.validator().resetIgnoredIssues(); - }); - } - - function setNoIssuesText(selection) { - - var opts = getOptions(); - - function checkForHiddenIssues(cases) { - for (var type in cases) { - var hiddenOpts = cases[type]; - var hiddenIssues = context.validator().getIssues(hiddenOpts); - if (hiddenIssues.length) { - selection.select('.box .details') - .text(_t( - 'issues.no_issues.hidden_issues.' + type, - { count: hiddenIssues.length.toString() } - )); - return; - } - } - selection.select('.box .details') - .text(_t('issues.no_issues.hidden_issues.none')); - } - - var messageType; - - if (opts.what === 'edited' && opts.where === 'visible') { - - messageType = 'edits_in_view'; - - checkForHiddenIssues({ - elsewhere: { what: 'edited', where: 'all' }, - everything_else: { what: 'all', where: 'visible' }, - disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' }, - everything_else_elsewhere: { what: 'all', where: 'all' }, - disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' }, - ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' }, - ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' } - }); - - } else if (opts.what === 'edited' && opts.where === 'all') { - - messageType = 'edits'; - - checkForHiddenIssues({ - everything_else: { what: 'all', where: 'all' }, - disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' }, - ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' } - }); - - } else if (opts.what === 'all' && opts.where === 'visible') { - - messageType = 'everything_in_view'; - - checkForHiddenIssues({ - elsewhere: { what: 'all', where: 'all' }, - disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' }, - disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' }, - ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' }, - ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' } - }); - } else if (opts.what === 'all' && opts.where === 'all') { - - messageType = 'everything'; - - checkForHiddenIssues({ - disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' }, - ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' } - }); - } - - if (opts.what === 'edited' && context.history().difference().summary().length === 0) { - messageType = 'no_edits'; - } - - selection.select('.box .message') - .text(_t('issues.no_issues.message.' + messageType)); - - } - - context.validator().on('validated.uiSectionValidationStatus', function() { - window.requestIdleCallback(section.reRender); - }); - - context.map().on('move.uiSectionValidationStatus', - debounce(function() { - window.requestIdleCallback(section.reRender); - }, 1000) - ); - - return section; - } - - function uiPaneIssues(context) { - - var issuesPane = uiPane('issues', context) - .key(_t('issues.key')) - .title(_t('issues.title')) - .description(_t('issues.title')) - .iconName('iD-icon-alert') - .sections([ - uiSectionValidationOptions(context), - uiSectionValidationStatus(context), - uiSectionValidationIssues('issues-errors', 'error', context), - uiSectionValidationIssues('issues-warnings', 'warning', context), - uiSectionValidationRules(context) - ]); - - return issuesPane; - } - - function uiSettingsCustomData(context) { - var dispatch$1 = dispatch('change'); - - function render(selection) { - var dataLayer = context.layers().layer('data'); - - // keep separate copies of original and current settings - var _origSettings = { - fileList: (dataLayer && dataLayer.fileList()) || null, - url: corePreferences('settings-custom-data-url') - }; - var _currSettings = { - fileList: (dataLayer && dataLayer.fileList()) || null, - url: corePreferences('settings-custom-data-url') - }; - - // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; - var modal = uiConfirm(selection).okButton(); - - modal - .classed('settings-modal settings-custom-data', true); - - modal.select('.modal-section.header') - .append('h3') - .text(_t('settings.custom_data.header')); - - - var textSection = modal.select('.modal-section.message-text'); - - textSection - .append('pre') - .attr('class', 'instructions-file') - .text(_t('settings.custom_data.file.instructions')); - - textSection - .append('input') - .attr('class', 'field-file') - .attr('type', 'file') - .property('files', _currSettings.fileList) // works for all except IE11 - .on('change', function() { - var files = event.target.files; - if (files && files.length) { - _currSettings.url = ''; - textSection.select('.field-url').property('value', ''); - _currSettings.fileList = files; - } else { - _currSettings.fileList = null; - } - }); - - textSection - .append('h4') - .text(_t('settings.custom_data.or')); - - textSection - .append('pre') - .attr('class', 'instructions-url') - .text(_t('settings.custom_data.url.instructions')); - - textSection - .append('textarea') - .attr('class', 'field-url') - .attr('placeholder', _t('settings.custom_data.url.placeholder')) - .call(utilNoAuto) - .property('value', _currSettings.url); - - - // insert a cancel button - var buttonSection = modal.select('.modal-section.buttons'); - - buttonSection - .insert('button', '.ok-button') - .attr('class', 'button cancel-button secondary-action') - .text(_t('confirm.cancel')); - - - buttonSection.select('.cancel-button') - .on('click.cancel', clickCancel); - - buttonSection.select('.ok-button') - .attr('disabled', isSaveDisabled) - .on('click.save', clickSave); - - - function isSaveDisabled() { - return null; - } - - - // restore the original url - function clickCancel() { - textSection.select('.field-url').property('value', _origSettings.url); - corePreferences('settings-custom-data-url', _origSettings.url); - this.blur(); - modal.close(); - } - - // accept the current url - function clickSave() { - _currSettings.url = textSection.select('.field-url').property('value').trim(); - - // one or the other but not both - if (_currSettings.url) { _currSettings.fileList = null; } - if (_currSettings.fileList) { _currSettings.url = ''; } - - corePreferences('settings-custom-data-url', _currSettings.url); - this.blur(); - modal.close(); - dispatch$1.call('change', this, _currSettings); - } - } - - return utilRebind(render, dispatch$1, 'on'); - } - - function uiSectionDataLayers(context) { - - var settingsCustomData = uiSettingsCustomData(context) - .on('change', customChanged); - - var layers = context.layers(); - - var section = uiSection('data-layers', context) - .title(_t('map_data.data_layers')) - .disclosureContent(renderDisclosureContent); - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.data-layer-container') - .data([0]); - - container.enter() - .append('div') - .attr('class', 'data-layer-container') - .merge(container) - .call(drawOsmItems) - .call(drawQAItems) - .call(drawCustomDataItems) - .call(drawVectorItems) // Beta - Detroit mapping challenge - .call(drawPanelItems); - } - - function showsLayer(which) { - var layer = layers.layer(which); - if (layer) { - return layer.enabled(); - } - return false; - } - - function setLayer(which, enabled) { - // Don't allow layer changes while drawing - #6584 - var mode = context.mode(); - if (mode && /^draw/.test(mode.id)) return; - - var layer = layers.layer(which); - if (layer) { - layer.enabled(enabled); - - if (!enabled && (which === 'osm' || which === 'notes')) { - context.enter(modeBrowse(context)); - } - } - } - - function toggleLayer(which) { - setLayer(which, !showsLayer(which)); - } - - function drawOsmItems(selection) { - var osmKeys = ['osm', 'notes']; - var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; }); - - var ul = selection - .selectAll('.layer-list-osm') - .data([0]); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-osm') - .merge(ul); - - var li = ul.selectAll('.list-item') - .data(osmLayers); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', function(d) { return 'list-item list-item-' + d.id; }); - - var labelEnter = liEnter - .append('label') - .each(function(d) { - if (d.id === 'osm') { - select(this) - .call(uiTooltip() - .title(_t('map_data.layers.' + d.id + '.tooltip')) - .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]) - .placement('bottom') - ); - } else { - select(this) - .call(uiTooltip() - .title(_t('map_data.layers.' + d.id + '.tooltip')) - .placement('bottom') - ); - } - }); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function(d) { toggleLayer(d.id); }); - - labelEnter - .append('span') - .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); }); - - - // Update - li - .merge(liEnter) - .classed('active', function (d) { return d.layer.enabled(); }) - .selectAll('input') - .property('checked', function (d) { return d.layer.enabled(); }); - } - - function drawQAItems(selection) { - var qaKeys = ['keepRight', 'improveOSM', 'osmose']; - var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; }); - - var ul = selection - .selectAll('.layer-list-qa') - .data([0]); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-qa') - .merge(ul); - - var li = ul.selectAll('.list-item') - .data(qaLayers); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', function(d) { return 'list-item list-item-' + d.id; }); - - var labelEnter = liEnter - .append('label') - .each(function(d) { - select(this) - .call(uiTooltip() - .title(_t('map_data.layers.' + d.id + '.tooltip')) - .placement('bottom') - ); - }); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function(d) { toggleLayer(d.id); }); - - labelEnter - .append('span') - .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); }); - - - // Update - li - .merge(liEnter) - .classed('active', function (d) { return d.layer.enabled(); }) - .selectAll('input') - .property('checked', function (d) { return d.layer.enabled(); }); - } - - // Beta feature - sample vector layers to support Detroit Mapping Challenge - // https://github.com/osmus/detroit-mapping-challenge - function drawVectorItems(selection) { - var dataLayer = layers.layer('data'); - var vtData = [ - { - name: 'Detroit Neighborhoods/Parks', - src: 'neighborhoods-parks', - tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.', - template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmur6x34562qp9iv1u3ksf-54hev,jonahadkins.cjksmqxdx33jj2wp90xd9x2md-4e5y2/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' - }, { - name: 'Detroit Composite POIs', - src: 'composite-poi', - tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.', - template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmm6a02sli31myxhsr7zf3-2sw8h/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' - }, { - name: 'Detroit All-The-Places POIs', - src: 'alltheplaces-poi', - tooltip: 'Public domain business location data created by web scrapers.', - template: 'https://{switch:a,b,c,d}.tiles.mapbox.com/v4/jonahadkins.cjksmswgk340g2vo06p1w9w0j-8fjjc/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1Ijoiam9uYWhhZGtpbnMiLCJhIjoiRlVVVkx3VSJ9.9sdVEK_B_VkEXPjssU5MqA' - } - ]; - - // Only show this if the map is around Detroit.. - var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]); - var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center())); - - var container = selection.selectAll('.vectortile-container') - .data(showVectorItems ? [0] : []); - - container.exit() - .remove(); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'vectortile-container'); - - containerEnter - .append('h4') - .attr('class', 'vectortile-header') - .text('Detroit Vector Tiles (Beta)'); - - containerEnter - .append('ul') - .attr('class', 'layer-list layer-list-vectortile'); - - containerEnter - .append('div') - .attr('class', 'vectortile-footer') - .append('a') - .attr('target', '_blank') - .attr('tabindex', -1) - .call(svgIcon('#iD-icon-out-link', 'inline')) - .attr('href', 'https://github.com/osmus/detroit-mapping-challenge') - .append('span') - .text('About these layers'); - - container = container - .merge(containerEnter); - - - var ul = container.selectAll('.layer-list-vectortile'); - - var li = ul.selectAll('.list-item') - .data(vtData); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', function(d) { return 'list-item list-item-' + d.src; }); - - var labelEnter = liEnter - .append('label') - .each(function(d) { - select(this).call( - uiTooltip().title(d.tooltip).placement('top') - ); - }); - - labelEnter - .append('input') - .attr('type', 'radio') - .attr('name', 'vectortile') - .on('change', selectVTLayer); - - labelEnter - .append('span') - .text(function(d) { return d.name; }); - - // Update - li - .merge(liEnter) - .classed('active', isVTLayerSelected) - .selectAll('input') - .property('checked', isVTLayerSelected); - - - function isVTLayerSelected(d) { - return dataLayer && dataLayer.template() === d.template; - } - - function selectVTLayer(d) { - corePreferences('settings-custom-data-url', d.template); - if (dataLayer) { - dataLayer.template(d.template, d.src); - dataLayer.enabled(true); - } - } - } - - function drawCustomDataItems(selection) { - var dataLayer = layers.layer('data'); - var hasData = dataLayer && dataLayer.hasData(); - var showsData = hasData && dataLayer.enabled(); - - var ul = selection - .selectAll('.layer-list-data') - .data(dataLayer ? [0] : []); - - // Exit - ul.exit() - .remove(); - - // Enter - var ulEnter = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-data'); - - var liEnter = ulEnter - .append('li') - .attr('class', 'list-item-data'); - - var labelEnter = liEnter - .append('label') - .call(uiTooltip() - .title(_t('map_data.layers.custom.tooltip')) - .placement('top') - ); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { toggleLayer('data'); }); - - labelEnter - .append('span') - .text(_t('map_data.layers.custom.title')); - - liEnter - .append('button') - .call(uiTooltip() - .title(_t('settings.custom_data.tooltip')) - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - ) - .on('click', editCustom) - .call(svgIcon('#iD-icon-more')); - - liEnter - .append('button') - .call(uiTooltip() - .title(_t('map_data.layers.custom.zoom')) - .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left') - ) - .on('click', function() { - event.preventDefault(); - event.stopPropagation(); - dataLayer.fitZoom(); - }) - .call(svgIcon('#iD-icon-framed-dot')); - - // Update - ul = ul - .merge(ulEnter); - - ul.selectAll('.list-item-data') - .classed('active', showsData) - .selectAll('label') - .classed('deemphasize', !hasData) - .selectAll('input') - .property('disabled', !hasData) - .property('checked', showsData); - } - - function editCustom() { - event.preventDefault(); - context.container() - .call(settingsCustomData); - } - - function customChanged(d) { - var dataLayer = layers.layer('data'); - - if (d && d.url) { - dataLayer.url(d.url); - } else if (d && d.fileList) { - dataLayer.fileList(d.fileList); - } - } - - - function drawPanelItems(selection) { - - var panelsListEnter = selection.selectAll('.md-extras-list') - .data([0]) - .enter() - .append('ul') - .attr('class', 'layer-list md-extras-list'); - - var historyPanelLabelEnter = panelsListEnter - .append('li') - .attr('class', 'history-panel-toggle-item') - .append('label') - .call(uiTooltip() - .title(_t('map_data.history_panel.tooltip')) - .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))]) - .placement('top') - ); - - historyPanelLabelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { - event.preventDefault(); - context.ui().info.toggle('history'); - }); - - historyPanelLabelEnter - .append('span') - .text(_t('map_data.history_panel.title')); - - var measurementPanelLabelEnter = panelsListEnter - .append('li') - .attr('class', 'measurement-panel-toggle-item') - .append('label') - .call(uiTooltip() - .title(_t('map_data.measurement_panel.tooltip')) - .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))]) - .placement('top') - ); - - measurementPanelLabelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function() { - event.preventDefault(); - context.ui().info.toggle('measurement'); - }); - - measurementPanelLabelEnter - .append('span') - .text(_t('map_data.measurement_panel.title')); - } - - context.layers().on('change.uiSectionDataLayers', section.reRender); - - context.map() - .on('move.uiSectionDataLayers', - debounce(function() { - // Detroit layers may have moved in or out of view - window.requestIdleCallback(section.reRender); - }, 1000) - ); - - return section; - } - - function uiSectionMapFeatures(context) { - - var _features = context.features().keys(); - - var section = uiSection('map-features', context) - .title(_t('map_data.map_features')) - .disclosureContent(renderDisclosureContent) - .expandedByDefault(false); - - function renderDisclosureContent(selection) { - - var container = selection.selectAll('.layer-feature-list-container') - .data([0]); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'layer-feature-list-container'); - - containerEnter - .append('ul') - .attr('class', 'layer-list layer-feature-list'); - - var footer = containerEnter - .append('div') - .attr('class', 'feature-list-links section-footer'); - - footer - .append('a') - .attr('class', 'feature-list-link') - .attr('href', '#') - .text(_t('issues.enable_all')) - .on('click', function() { - context.features().enableAll(); - }); - - footer - .append('a') - .attr('class', 'feature-list-link') - .attr('href', '#') - .text(_t('issues.disable_all')) - .on('click', function() { - context.features().disableAll(); - }); - - // Update - container = container - .merge(containerEnter); - - container.selectAll('.layer-feature-list') - .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature); - } - - function drawListItems(selection, data, type, name, change, active) { - var items = selection.selectAll('li') - .data(data); - - // Exit - items.exit() - .remove(); - - // Enter - var enter = items.enter() - .append('li') - .call(uiTooltip() - .title(function(d) { - var tip = _t(name + '.' + d + '.tooltip'); - if (autoHiddenFeature(d)) { - var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden'); - tip += '
    ' + msg + '
    '; - } - return tip; - }) - .placement('top') - ); - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', type) - .attr('name', name) - .on('change', change); - - label - .append('span') - .text(function(d) { return _t(name + '.' + d + '.description'); }); - - // Update - items = items - .merge(enter); - - items - .classed('active', active) - .selectAll('input') - .property('checked', active) - .property('indeterminate', autoHiddenFeature); - } - - function autoHiddenFeature(d) { - return context.features().autoHidden(d); - } - - function showsFeature(d) { - return context.features().enabled(d); - } - - function clickFeature(d) { - context.features().toggle(d); - } - - function showsLayer(id) { - var layer = context.layers().layer(id); - return layer && layer.enabled(); - } - - // add listeners - context.features() - .on('change.map_features', section.reRender); - - return section; - } - - function uiSectionMapStyleOptions(context) { - - var section = uiSection('fill-area', context) - .title(_t('map_data.style_options')) - .disclosureContent(renderDisclosureContent) - .expandedByDefault(false); - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.layer-fill-list') - .data([0]); - - container.enter() - .append('ul') - .attr('class', 'layer-list layer-fill-list') - .merge(container) - .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill); - - var container2 = selection.selectAll('.layer-visual-diff-list') - .data([0]); - - container2.enter() - .append('ul') - .attr('class', 'layer-list layer-visual-diff-list') - .merge(container2) - .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() { - return context.surface().classed('highlight-edited'); - }); - } - - function drawListItems(selection, data, type, name, change, active) { - var items = selection.selectAll('li') - .data(data); - - // Exit - items.exit() - .remove(); - - // Enter - var enter = items.enter() - .append('li') - .call(uiTooltip() - .title(function(d) { - return _t(name + '.' + d + '.tooltip'); - }) - .keys(function(d) { - var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null); - if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key'); - return key ? [key] : null; - }) - .placement('top') - ); - - var label = enter - .append('label'); - - label - .append('input') - .attr('type', type) - .attr('name', name) - .on('change', change); - - label - .append('span') - .text(function(d) { return _t(name + '.' + d + '.description'); }); - - // Update - items = items - .merge(enter); - - items - .classed('active', active) - .selectAll('input') - .property('checked', active) - .property('indeterminate', false); - } - - function isActiveFill(d) { - return context.map().activeAreaFill() === d; - } - - function toggleHighlightEdited() { - event.preventDefault(); - context.map().toggleHighlightEdited(); - } - - function setFill(d) { - context.map().activeAreaFill(d); - } - - context.map() - .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender); - - return section; - } - - function uiSectionPhotoOverlays(context) { - - var layers = context.layers(); - - var section = uiSection('photo-overlays', context) - .title(_t('photo_overlays.title')) - .disclosureContent(renderDisclosureContent) - .expandedByDefault(false); - - function renderDisclosureContent(selection) { - var container = selection.selectAll('.photo-overlay-container') - .data([0]); - - container.enter() - .append('div') - .attr('class', 'photo-overlay-container') - .merge(container) - .call(drawPhotoItems) - .call(drawPhotoTypeItems); - } - - function drawPhotoItems(selection) { - var photoKeys = context.photos().overlayLayerIDs(); - var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; }); - var data = photoLayers.filter(function(obj) { return obj.layer.supported(); }); - - function layerSupported(d) { - return d.layer && d.layer.supported(); - } - function layerEnabled(d) { - return layerSupported(d) && d.layer.enabled(); - } - - var ul = selection - .selectAll('.layer-list-photos') - .data([0]); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-photos') - .merge(ul); - - var li = ul.selectAll('.list-item-photos') - .data(data); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', function(d) { - var classes = 'list-item-photos list-item-' + d.id; - if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') { - classes += ' indented'; - } - return classes; - }); - - var labelEnter = liEnter - .append('label') - .each(function(d) { - var titleID; - if (d.id === 'mapillary-signs') titleID = 'mapillary.signs.tooltip'; - else if (d.id === 'mapillary') titleID = 'mapillary_images.tooltip'; - else if (d.id === 'openstreetcam') titleID = 'openstreetcam_images.tooltip'; - else titleID = d.id.replace(/-/g, '_') + '.tooltip'; - select(this) - .call(uiTooltip() - .title(_t(titleID)) - .placement('top') - ); - }); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function(d) { toggleLayer(d.id); }); - - labelEnter - .append('span') - .text(function(d) { - var id = d.id; - if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs'; - return _t(id.replace(/-/g, '_') + '.title'); - }); - - - // Update - li - .merge(liEnter) - .classed('active', layerEnabled) - .selectAll('input') - .property('checked', layerEnabled); - } - - function drawPhotoTypeItems(selection) { - var data = context.photos().allPhotoTypes(); - - function typeEnabled(d) { - return context.photos().showsPhotoType(d); - } - - var ul = selection - .selectAll('.layer-list-photo-types') - .data(context.photos().shouldFilterByPhotoType() ? [0] : []); - - ul.exit() - .remove(); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-photo-types') - .merge(ul); - - var li = ul.selectAll('.list-item-photo-types') - .data(data); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', function(d) { - return 'list-item-photo-types list-item-' + d; - }); - - var labelEnter = liEnter - .append('label') - .each(function(d) { - select(this) - .call(uiTooltip() - .title(_t('photo_overlays.photo_type.' + d + '.tooltip')) - .placement('top') - ); - }); - - labelEnter - .append('input') - .attr('type', 'checkbox') - .on('change', function(d) { - context.photos().togglePhotoType(d); - }); - - labelEnter - .append('span') - .text(function(d) { - return _t('photo_overlays.photo_type.' + d + '.title'); - }); - - - // Update - li - .merge(liEnter) - .classed('active', typeEnabled) - .selectAll('input') - .property('checked', typeEnabled); - } - - function toggleLayer(which) { - setLayer(which, !showsLayer(which)); - } - - function showsLayer(which) { - var layer = layers.layer(which); - if (layer) { - return layer.enabled(); - } - return false; - } - - function setLayer(which, enabled) { - var layer = layers.layer(which); - if (layer) { - layer.enabled(enabled); - } - } - - context.layers().on('change.uiSectionPhotoOverlays', section.reRender); - context.photos().on('change.uiSectionPhotoOverlays', section.reRender); - - return section; - } - - function uiPaneMapData(context) { - - var mapDataPane = uiPane('map-data', context) - .key(_t('map_data.key')) - .title(_t('map_data.title')) - .description(_t('map_data.description')) - .iconName('iD-icon-data') - .sections([ - uiSectionDataLayers(context), - uiSectionPhotoOverlays(context), - uiSectionMapStyleOptions(context), - uiSectionMapFeatures(context) - ]); - - return mapDataPane; - } - - function uiSectionPrivacy(context) { - - let section = uiSection('preferences-third-party', context) - .title(_t('preferences.privacy.title')) - .disclosureContent(renderDisclosureContent); - - let _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true'; - - function renderDisclosureContent(selection) { - // enter - let privacyOptionsListEnter = selection.selectAll('.privacy-options-list') - .data([0]) - .enter() - .append('ul') - .attr('class', 'layer-list privacy-options-list'); - - let thirdPartyIconsEnter = privacyOptionsListEnter - .append('li') - .attr('class', 'privacy-third-party-icons-item') - .append('label') - .call(uiTooltip() - .title(_t('preferences.privacy.third_party_icons.tooltip')) - .placement('bottom') - ); - - thirdPartyIconsEnter - .append('input') - .attr('type', 'checkbox') - .on('change', () => { - event.preventDefault(); - _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true'; - corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons); - update(); - }); - - thirdPartyIconsEnter - .append('span') - .text(_t('preferences.privacy.third_party_icons.description')); - - - // Privacy Policy link - selection.selectAll('.privacy-link') - .data([0]) - .enter() - .append('div') - .attr('class', 'privacy-link') - .append('a') - .attr('target', '_blank') - .call(svgIcon('#iD-icon-out-link', 'inline')) - .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md') - .append('span') - .text(_t('preferences.privacy.privacy_link')); - - update(); - - - function update() { - selection.selectAll('.privacy-third-party-icons-item') - .classed('active', (_showThirdPartyIcons === 'true')) - .select('input') - .property('checked', (_showThirdPartyIcons === 'true')); - } - } - - return section; - } - - function uiPanePreferences(context) { - - let preferencesPane = uiPane('preferences', context) - .key(_t('preferences.key')) - .title(_t('preferences.title')) - .description(_t('preferences.description')) - .iconName('fas-user-cog') - .sections([ - uiSectionPrivacy(context) - ]); - - return preferencesPane; - } - - function uiInit(context) { - var _initCounter = 0; - var _needWidth = {}; - - var _lastPointerType; - - - function render(container) { - - container - .on('click.ui', function() { - // we're only concerned with the primary mouse button - if (event.button !== 0) return; - - if (!event.composedPath) return; - - // some targets have default click events we don't want to override - var isOkayTarget = event.composedPath().some(function(node) { - // clicking