3 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5 function createCommonjsModule(fn) {
6 var module = { exports: {} };
7 return fn(module, module.exports), module.exports;
10 var check = function (it) {
11 return it && it.Math == Math && it;
14 // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
16 // eslint-disable-next-line es/no-global-this -- safe
17 check(typeof globalThis == 'object' && globalThis) ||
18 check(typeof window == 'object' && window) ||
19 // eslint-disable-next-line no-restricted-globals -- safe
20 check(typeof self == 'object' && self) ||
21 check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
22 // eslint-disable-next-line no-new-func -- fallback
23 (function () { return this; })() || Function('return this')();
25 var fails = function (exec) {
33 // Detect IE8's incomplete defineProperty implementation
34 var descriptors = !fails(function () {
35 // eslint-disable-next-line es/no-object-defineproperty -- required for testing
36 return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
39 var $propertyIsEnumerable$1 = {}.propertyIsEnumerable;
40 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
41 var getOwnPropertyDescriptor$5 = Object.getOwnPropertyDescriptor;
44 var NASHORN_BUG = getOwnPropertyDescriptor$5 && !$propertyIsEnumerable$1.call({ 1: 2 }, 1);
46 // `Object.prototype.propertyIsEnumerable` method implementation
47 // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
48 var f$7 = NASHORN_BUG ? function propertyIsEnumerable(V) {
49 var descriptor = getOwnPropertyDescriptor$5(this, V);
50 return !!descriptor && descriptor.enumerable;
51 } : $propertyIsEnumerable$1;
53 var objectPropertyIsEnumerable = {
57 var createPropertyDescriptor = function (bitmap, value) {
59 enumerable: !(bitmap & 1),
60 configurable: !(bitmap & 2),
61 writable: !(bitmap & 4),
66 var toString$1 = {}.toString;
68 var classofRaw = function (it) {
69 return toString$1.call(it).slice(8, -1);
72 var split$1 = ''.split;
74 // fallback for non-array-like ES3 and non-enumerable old V8 strings
75 var indexedObject = fails(function () {
76 // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
77 // eslint-disable-next-line no-prototype-builtins -- safe
78 return !Object('z').propertyIsEnumerable(0);
80 return classofRaw(it) == 'String' ? split$1.call(it, '') : Object(it);
83 // `RequireObjectCoercible` abstract operation
84 // https://tc39.es/ecma262/#sec-requireobjectcoercible
85 var requireObjectCoercible = function (it) {
86 if (it == undefined) throw TypeError("Can't call method on " + it);
90 // toObject with fallback for non-array-like ES3 strings
94 var toIndexedObject = function (it) {
95 return indexedObject(requireObjectCoercible(it));
98 var isObject$4 = function (it) {
99 return typeof it === 'object' ? it !== null : typeof it === 'function';
102 // `ToPrimitive` abstract operation
103 // https://tc39.es/ecma262/#sec-toprimitive
104 // instead of the ES6 spec version, we didn't implement @@toPrimitive case
105 // and the second argument - flag - preferred type is a string
106 var toPrimitive = function (input, PREFERRED_STRING) {
107 if (!isObject$4(input)) return input;
109 if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
110 if (typeof (fn = input.valueOf) == 'function' && !isObject$4(val = fn.call(input))) return val;
111 if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject$4(val = fn.call(input))) return val;
112 throw TypeError("Can't convert object to primitive value");
115 // `ToObject` abstract operation
116 // https://tc39.es/ecma262/#sec-toobject
117 var toObject = function (argument) {
118 return Object(requireObjectCoercible(argument));
121 var hasOwnProperty$3 = {}.hasOwnProperty;
123 var has$1 = Object.hasOwn || function hasOwn(it, key) {
124 return hasOwnProperty$3.call(toObject(it), key);
127 var document$3 = global$2.document;
128 // typeof document.createElement is 'object' in old IE
129 var EXISTS = isObject$4(document$3) && isObject$4(document$3.createElement);
131 var documentCreateElement = function (it) {
132 return EXISTS ? document$3.createElement(it) : {};
135 // Thank's IE8 for his funny defineProperty
136 var ie8DomDefine = !descriptors && !fails(function () {
137 // eslint-disable-next-line es/no-object-defineproperty -- requied for testing
138 return Object.defineProperty(documentCreateElement('div'), 'a', {
139 get: function () { return 7; }
143 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
144 var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor;
146 // `Object.getOwnPropertyDescriptor` method
147 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
148 var f$6 = descriptors ? $getOwnPropertyDescriptor$1 : function getOwnPropertyDescriptor(O, P) {
149 O = toIndexedObject(O);
150 P = toPrimitive(P, true);
151 if (ie8DomDefine) try {
152 return $getOwnPropertyDescriptor$1(O, P);
153 } catch (error) { /* empty */ }
154 if (has$1(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
157 var objectGetOwnPropertyDescriptor = {
161 var anObject = function (it) {
162 if (!isObject$4(it)) {
163 throw TypeError(String(it) + ' is not an object');
167 // eslint-disable-next-line es/no-object-defineproperty -- safe
168 var $defineProperty$1 = Object.defineProperty;
170 // `Object.defineProperty` method
171 // https://tc39.es/ecma262/#sec-object.defineproperty
172 var f$5 = descriptors ? $defineProperty$1 : function defineProperty(O, P, Attributes) {
174 P = toPrimitive(P, true);
175 anObject(Attributes);
176 if (ie8DomDefine) try {
177 return $defineProperty$1(O, P, Attributes);
178 } catch (error) { /* empty */ }
179 if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
180 if ('value' in Attributes) O[P] = Attributes.value;
184 var objectDefineProperty = {
188 var createNonEnumerableProperty = descriptors ? function (object, key, value) {
189 return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
190 } : function (object, key, value) {
195 var setGlobal = function (key, value) {
197 createNonEnumerableProperty(global$2, key, value);
199 global$2[key] = value;
203 var SHARED = '__core-js_shared__';
204 var store$1 = global$2[SHARED] || setGlobal(SHARED, {});
206 var sharedStore = store$1;
208 var functionToString = Function.toString;
210 // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper
211 if (typeof sharedStore.inspectSource != 'function') {
212 sharedStore.inspectSource = function (it) {
213 return functionToString.call(it);
217 var inspectSource = sharedStore.inspectSource;
219 var WeakMap$1 = global$2.WeakMap;
221 var nativeWeakMap = typeof WeakMap$1 === 'function' && /native code/.test(inspectSource(WeakMap$1));
225 var shared = createCommonjsModule(function (module) {
226 (module.exports = function (key, value) {
227 return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
228 })('versions', []).push({
231 copyright: '© 2021 Denis Pushkarev (zloirock.ru)'
236 var postfix = Math.random();
238 var uid = function (key) {
239 return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id$1 + postfix).toString(36);
242 var keys$3 = shared('keys');
244 var sharedKey = function (key) {
245 return keys$3[key] || (keys$3[key] = uid(key));
248 var hiddenKeys$1 = {};
250 var OBJECT_ALREADY_INITIALIZED = 'Object already initialized';
251 var WeakMap = global$2.WeakMap;
252 var set$4, get$5, has;
254 var enforce = function (it) {
255 return has(it) ? get$5(it) : set$4(it, {});
258 var getterFor = function (TYPE) {
259 return function (it) {
261 if (!isObject$4(it) || (state = get$5(it)).type !== TYPE) {
262 throw TypeError('Incompatible receiver, ' + TYPE + ' required');
267 if (nativeWeakMap || sharedStore.state) {
268 var store = sharedStore.state || (sharedStore.state = new WeakMap());
269 var wmget = store.get;
270 var wmhas = store.has;
271 var wmset = store.set;
272 set$4 = function (it, metadata) {
273 if (wmhas.call(store, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
274 metadata.facade = it;
275 wmset.call(store, it, metadata);
278 get$5 = function (it) {
279 return wmget.call(store, it) || {};
281 has = function (it) {
282 return wmhas.call(store, it);
285 var STATE = sharedKey('state');
286 hiddenKeys$1[STATE] = true;
287 set$4 = function (it, metadata) {
288 if (has$1(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
289 metadata.facade = it;
290 createNonEnumerableProperty(it, STATE, metadata);
293 get$5 = function (it) {
294 return has$1(it, STATE) ? it[STATE] : {};
296 has = function (it) {
297 return has$1(it, STATE);
301 var internalState = {
309 var redefine = createCommonjsModule(function (module) {
310 var getInternalState = internalState.get;
311 var enforceInternalState = internalState.enforce;
312 var TEMPLATE = String(String).split('String');
314 (module.exports = function (O, key, value, options) {
315 var unsafe = options ? !!options.unsafe : false;
316 var simple = options ? !!options.enumerable : false;
317 var noTargetGet = options ? !!options.noTargetGet : false;
319 if (typeof value == 'function') {
320 if (typeof key == 'string' && !has$1(value, 'name')) {
321 createNonEnumerableProperty(value, 'name', key);
323 state = enforceInternalState(value);
325 state.source = TEMPLATE.join(typeof key == 'string' ? key : '');
328 if (O === global$2) {
329 if (simple) O[key] = value;
330 else setGlobal(key, value);
332 } else if (!unsafe) {
334 } else if (!noTargetGet && O[key]) {
337 if (simple) O[key] = value;
338 else createNonEnumerableProperty(O, key, value);
339 // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
340 })(Function.prototype, 'toString', function toString() {
341 return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
347 var aFunction$1 = function (variable) {
348 return typeof variable == 'function' ? variable : undefined;
351 var getBuiltIn = function (namespace, method) {
352 return arguments.length < 2 ? aFunction$1(path[namespace]) || aFunction$1(global$2[namespace])
353 : path[namespace] && path[namespace][method] || global$2[namespace] && global$2[namespace][method];
356 var ceil$1 = Math.ceil;
357 var floor$7 = Math.floor;
359 // `ToInteger` abstract operation
360 // https://tc39.es/ecma262/#sec-tointeger
361 var toInteger = function (argument) {
362 return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor$7 : ceil$1)(argument);
365 var min$9 = Math.min;
367 // `ToLength` abstract operation
368 // https://tc39.es/ecma262/#sec-tolength
369 var toLength = function (argument) {
370 return argument > 0 ? min$9(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
373 var max$4 = Math.max;
374 var min$8 = Math.min;
376 // Helper for a popular repeating case of the spec:
377 // Let integer be ? ToInteger(index).
378 // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
379 var toAbsoluteIndex = function (index, length) {
380 var integer = toInteger(index);
381 return integer < 0 ? max$4(integer + length, 0) : min$8(integer, length);
384 // `Array.prototype.{ indexOf, includes }` methods implementation
385 var createMethod$6 = function (IS_INCLUDES) {
386 return function ($this, el, fromIndex) {
387 var O = toIndexedObject($this);
388 var length = toLength(O.length);
389 var index = toAbsoluteIndex(fromIndex, length);
391 // Array#includes uses SameValueZero equality algorithm
392 // eslint-disable-next-line no-self-compare -- NaN check
393 if (IS_INCLUDES && el != el) while (length > index) {
395 // eslint-disable-next-line no-self-compare -- NaN check
396 if (value != value) return true;
397 // Array#indexOf ignores holes, Array#includes - not
398 } else for (;length > index; index++) {
399 if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
400 } return !IS_INCLUDES && -1;
404 var arrayIncludes = {
405 // `Array.prototype.includes` method
406 // https://tc39.es/ecma262/#sec-array.prototype.includes
407 includes: createMethod$6(true),
408 // `Array.prototype.indexOf` method
409 // https://tc39.es/ecma262/#sec-array.prototype.indexof
410 indexOf: createMethod$6(false)
413 var indexOf = arrayIncludes.indexOf;
416 var objectKeysInternal = function (object, names) {
417 var O = toIndexedObject(object);
421 for (key in O) !has$1(hiddenKeys$1, key) && has$1(O, key) && result.push(key);
422 // Don't enum bug & hidden keys
423 while (names.length > i) if (has$1(O, key = names[i++])) {
424 ~indexOf(result, key) || result.push(key);
429 // IE8- don't enum bug keys
434 'propertyIsEnumerable',
440 var hiddenKeys = enumBugKeys.concat('length', 'prototype');
442 // `Object.getOwnPropertyNames` method
443 // https://tc39.es/ecma262/#sec-object.getownpropertynames
444 // eslint-disable-next-line es/no-object-getownpropertynames -- safe
445 var f$4 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
446 return objectKeysInternal(O, hiddenKeys);
449 var objectGetOwnPropertyNames = {
453 // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe
454 var f$3 = Object.getOwnPropertySymbols;
456 var objectGetOwnPropertySymbols = {
460 // all object keys, includes non-enumerable and symbols
461 var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
462 var keys = objectGetOwnPropertyNames.f(anObject(it));
463 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
464 return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
467 var copyConstructorProperties = function (target, source) {
468 var keys = ownKeys(source);
469 var defineProperty = objectDefineProperty.f;
470 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
471 for (var i = 0; i < keys.length; i++) {
473 if (!has$1(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
477 var replacement = /#|\.prototype\./;
479 var isForced = function (feature, detection) {
480 var value = data[normalize$1(feature)];
481 return value == POLYFILL ? true
482 : value == NATIVE ? false
483 : typeof detection == 'function' ? fails(detection)
487 var normalize$1 = isForced.normalize = function (string) {
488 return String(string).replace(replacement, '.').toLowerCase();
491 var data = isForced.data = {};
492 var NATIVE = isForced.NATIVE = 'N';
493 var POLYFILL = isForced.POLYFILL = 'P';
495 var isForced_1 = isForced;
497 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
505 options.target - name of the target object
506 options.global - target is the global object
507 options.stat - export as static methods of target
508 options.proto - export as prototype methods of target
509 options.real - real prototype method for the `pure` version
510 options.forced - export even if the native feature is available
511 options.bind - bind methods to the target, required for the `pure` version
512 options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
513 options.unsafe - use the simple assignment of property instead of delete + defineProperty
514 options.sham - add a flag to not completely full polyfills
515 options.enumerable - export as enumerable property
516 options.noTargetGet - prevent calling a getter on target
518 var _export = function (options, source) {
519 var TARGET = options.target;
520 var GLOBAL = options.global;
521 var STATIC = options.stat;
522 var FORCED, target, key, targetProperty, sourceProperty, descriptor;
526 target = global$2[TARGET] || setGlobal(TARGET, {});
528 target = (global$2[TARGET] || {}).prototype;
530 if (target) for (key in source) {
531 sourceProperty = source[key];
532 if (options.noTargetGet) {
533 descriptor = getOwnPropertyDescriptor$4(target, key);
534 targetProperty = descriptor && descriptor.value;
535 } else targetProperty = target[key];
536 FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
537 // contained in target
538 if (!FORCED && targetProperty !== undefined) {
539 if (typeof sourceProperty === typeof targetProperty) continue;
540 copyConstructorProperties(sourceProperty, targetProperty);
542 // add a flag to not completely full polyfills
543 if (options.sham || (targetProperty && targetProperty.sham)) {
544 createNonEnumerableProperty(sourceProperty, 'sham', true);
547 redefine(target, key, sourceProperty, options);
552 // https://tc39.es/ecma262/#sec-date.now
553 _export({ target: 'Date', stat: true }, {
554 now: function now() {
555 return new Date().getTime();
559 var DatePrototype$1 = Date.prototype;
560 var INVALID_DATE = 'Invalid Date';
561 var TO_STRING$1 = 'toString';
562 var nativeDateToString = DatePrototype$1[TO_STRING$1];
563 var getTime$1 = DatePrototype$1.getTime;
565 // `Date.prototype.toString` method
566 // https://tc39.es/ecma262/#sec-date.prototype.tostring
567 if (new Date(NaN) + '' != INVALID_DATE) {
568 redefine(DatePrototype$1, TO_STRING$1, function toString() {
569 var value = getTime$1.call(this);
570 // eslint-disable-next-line no-self-compare -- NaN check
571 return value === value ? nativeDateToString.call(this) : INVALID_DATE;
575 function _typeof(obj) {
576 "@babel/helpers - typeof";
578 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
579 _typeof = function (obj) {
583 _typeof = function (obj) {
584 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
591 function _classCallCheck$1(instance, Constructor) {
592 if (!(instance instanceof Constructor)) {
593 throw new TypeError("Cannot call a class as a function");
597 function _defineProperties$1(target, props) {
598 for (var i = 0; i < props.length; i++) {
599 var descriptor = props[i];
600 descriptor.enumerable = descriptor.enumerable || false;
601 descriptor.configurable = true;
602 if ("value" in descriptor) descriptor.writable = true;
603 Object.defineProperty(target, descriptor.key, descriptor);
607 function _createClass$1(Constructor, protoProps, staticProps) {
608 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
609 if (staticProps) _defineProperties$1(Constructor, staticProps);
613 function _defineProperty(obj, key, value) {
615 Object.defineProperty(obj, key, {
628 function _slicedToArray(arr, i) {
629 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
632 function _toConsumableArray(arr) {
633 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
636 function _arrayWithoutHoles(arr) {
637 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
640 function _arrayWithHoles(arr) {
641 if (Array.isArray(arr)) return arr;
644 function _iterableToArray(iter) {
645 if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
648 function _iterableToArrayLimit(arr, i) {
649 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
651 if (_i == null) return;
659 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
662 if (i && _arr.length === i) break;
669 if (!_n && _i["return"] != null) _i["return"]();
678 function _unsupportedIterableToArray(o, minLen) {
680 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
681 var n = Object.prototype.toString.call(o).slice(8, -1);
682 if (n === "Object" && o.constructor) n = o.constructor.name;
683 if (n === "Map" || n === "Set") return Array.from(o);
684 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
687 function _arrayLikeToArray(arr, len) {
688 if (len == null || len > arr.length) len = arr.length;
690 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
695 function _nonIterableSpread() {
696 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
699 function _nonIterableRest() {
700 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
703 function _createForOfIteratorHelper(o, allowArrayLike) {
704 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
707 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
711 var F = function () {};
716 if (i >= o.length) return {
731 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
734 var normalCompletion = true,
742 var step = it.next();
743 normalCompletion = step.done;
752 if (!normalCompletion && it.return != null) it.return();
754 if (didErr) throw err;
760 var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
762 var process$4 = global$2.process;
763 var versions = process$4 && process$4.versions;
764 var v8 = versions && versions.v8;
765 var match, version$1;
768 match = v8.split('.');
769 version$1 = match[0] < 4 ? 1 : match[0] + match[1];
770 } else if (engineUserAgent) {
771 match = engineUserAgent.match(/Edge\/(\d+)/);
772 if (!match || match[1] >= 74) {
773 match = engineUserAgent.match(/Chrome\/(\d+)/);
774 if (match) version$1 = match[1];
778 var engineV8Version = version$1 && +version$1;
780 /* eslint-disable es/no-symbol -- required for testing */
782 // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing
783 var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
784 var symbol = Symbol();
785 // Chrome 38 Symbol has incorrect toString conversion
786 // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances
787 return !String(symbol) || !(Object(symbol) instanceof Symbol) ||
788 // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
789 !Symbol.sham && engineV8Version && engineV8Version < 41;
792 /* eslint-disable es/no-symbol -- required for testing */
794 var useSymbolAsUid = nativeSymbol
796 && typeof Symbol.iterator == 'symbol';
798 var WellKnownSymbolsStore$1 = shared('wks');
799 var Symbol$1 = global$2.Symbol;
800 var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
802 var wellKnownSymbol = function (name) {
803 if (!has$1(WellKnownSymbolsStore$1, name) || !(nativeSymbol || typeof WellKnownSymbolsStore$1[name] == 'string')) {
804 if (nativeSymbol && has$1(Symbol$1, name)) {
805 WellKnownSymbolsStore$1[name] = Symbol$1[name];
807 WellKnownSymbolsStore$1[name] = createWellKnownSymbol('Symbol.' + name);
809 } return WellKnownSymbolsStore$1[name];
812 var f$2 = wellKnownSymbol;
814 var wellKnownSymbolWrapped = {
818 var defineProperty$9 = objectDefineProperty.f;
820 var defineWellKnownSymbol = function (NAME) {
821 var Symbol = path.Symbol || (path.Symbol = {});
822 if (!has$1(Symbol, NAME)) defineProperty$9(Symbol, NAME, {
823 value: wellKnownSymbolWrapped.f(NAME)
827 // `Symbol.iterator` well-known symbol
828 // https://tc39.es/ecma262/#sec-symbol.iterator
829 defineWellKnownSymbol('iterator');
831 // `Object.keys` method
832 // https://tc39.es/ecma262/#sec-object.keys
833 // eslint-disable-next-line es/no-object-keys -- safe
834 var objectKeys = Object.keys || function keys(O) {
835 return objectKeysInternal(O, enumBugKeys);
838 // `Object.defineProperties` method
839 // https://tc39.es/ecma262/#sec-object.defineproperties
840 // eslint-disable-next-line es/no-object-defineproperties -- safe
841 var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
843 var keys = objectKeys(Properties);
844 var length = keys.length;
847 while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
851 var html = getBuiltIn('document', 'documentElement');
855 var PROTOTYPE$2 = 'prototype';
856 var SCRIPT = 'script';
857 var IE_PROTO$1 = sharedKey('IE_PROTO');
859 var EmptyConstructor = function () { /* empty */ };
861 var scriptTag = function (content) {
862 return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
865 // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
866 var NullProtoObjectViaActiveX = function (activeXDocument) {
867 activeXDocument.write(scriptTag(''));
868 activeXDocument.close();
869 var temp = activeXDocument.parentWindow.Object;
870 activeXDocument = null; // avoid memory leak
874 // Create object with fake `null` prototype: use iframe Object with cleared prototype
875 var NullProtoObjectViaIFrame = function () {
876 // Thrash, waste and sodomy: IE GC bug
877 var iframe = documentCreateElement('iframe');
878 var JS = 'java' + SCRIPT + ':';
880 iframe.style.display = 'none';
881 html.appendChild(iframe);
882 // https://github.com/zloirock/core-js/issues/475
883 iframe.src = String(JS);
884 iframeDocument = iframe.contentWindow.document;
885 iframeDocument.open();
886 iframeDocument.write(scriptTag('document.F=Object'));
887 iframeDocument.close();
888 return iframeDocument.F;
891 // Check for document.domain and active x support
892 // No need to use active x approach when document.domain is not set
893 // see https://github.com/es-shims/es5-shim/issues/150
894 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
897 var NullProtoObject = function () {
899 /* global ActiveXObject -- old IE */
900 activeXDocument = document.domain && new ActiveXObject('htmlfile');
901 } catch (error) { /* ignore */ }
902 NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
903 var length = enumBugKeys.length;
904 while (length--) delete NullProtoObject[PROTOTYPE$2][enumBugKeys[length]];
905 return NullProtoObject();
908 hiddenKeys$1[IE_PROTO$1] = true;
910 // `Object.create` method
911 // https://tc39.es/ecma262/#sec-object.create
912 var objectCreate = Object.create || function create(O, Properties) {
915 EmptyConstructor[PROTOTYPE$2] = anObject(O);
916 result = new EmptyConstructor();
917 EmptyConstructor[PROTOTYPE$2] = null;
918 // add "__proto__" for Object.getPrototypeOf polyfill
919 result[IE_PROTO$1] = O;
920 } else result = NullProtoObject();
921 return Properties === undefined ? result : objectDefineProperties(result, Properties);
924 var UNSCOPABLES = wellKnownSymbol('unscopables');
925 var ArrayPrototype$1 = Array.prototype;
927 // Array.prototype[@@unscopables]
928 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
929 if (ArrayPrototype$1[UNSCOPABLES] == undefined) {
930 objectDefineProperty.f(ArrayPrototype$1, UNSCOPABLES, {
932 value: objectCreate(null)
936 // add a key to Array.prototype[@@unscopables]
937 var addToUnscopables = function (key) {
938 ArrayPrototype$1[UNSCOPABLES][key] = true;
943 var correctPrototypeGetter = !fails(function () {
944 function F() { /* empty */ }
945 F.prototype.constructor = null;
946 // eslint-disable-next-line es/no-object-getprototypeof -- required for testing
947 return Object.getPrototypeOf(new F()) !== F.prototype;
950 var IE_PROTO = sharedKey('IE_PROTO');
951 var ObjectPrototype$3 = Object.prototype;
953 // `Object.getPrototypeOf` method
954 // https://tc39.es/ecma262/#sec-object.getprototypeof
955 // eslint-disable-next-line es/no-object-getprototypeof -- safe
956 var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
958 if (has$1(O, IE_PROTO)) return O[IE_PROTO];
959 if (typeof O.constructor == 'function' && O instanceof O.constructor) {
960 return O.constructor.prototype;
961 } return O instanceof Object ? ObjectPrototype$3 : null;
964 var ITERATOR$8 = wellKnownSymbol('iterator');
965 var BUGGY_SAFARI_ITERATORS$1 = false;
967 var returnThis$2 = function () { return this; };
969 // `%IteratorPrototype%` object
970 // https://tc39.es/ecma262/#sec-%iteratorprototype%-object
971 var IteratorPrototype$2, PrototypeOfArrayIteratorPrototype, arrayIterator;
973 /* eslint-disable es/no-array-prototype-keys -- safe */
975 arrayIterator = [].keys();
976 // Safari 8 has buggy iterators w/o `next`
977 if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true;
979 PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
980 if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$2 = PrototypeOfArrayIteratorPrototype;
984 var NEW_ITERATOR_PROTOTYPE = IteratorPrototype$2 == undefined || fails(function () {
986 // FF44- legacy iterators case
987 return IteratorPrototype$2[ITERATOR$8].call(test) !== test;
990 if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$2 = {};
992 // `%IteratorPrototype%[@@iterator]()` method
993 // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
994 if (!has$1(IteratorPrototype$2, ITERATOR$8)) {
995 createNonEnumerableProperty(IteratorPrototype$2, ITERATOR$8, returnThis$2);
998 var iteratorsCore = {
999 IteratorPrototype: IteratorPrototype$2,
1000 BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1
1003 var defineProperty$8 = objectDefineProperty.f;
1007 var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
1009 var setToStringTag = function (it, TAG, STATIC) {
1010 if (it && !has$1(it = STATIC ? it : it.prototype, TO_STRING_TAG$4)) {
1011 defineProperty$8(it, TO_STRING_TAG$4, { configurable: true, value: TAG });
1015 var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1021 var returnThis$1 = function () { return this; };
1023 var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1024 var TO_STRING_TAG = NAME + ' Iterator';
1025 IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1026 setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1027 iterators[TO_STRING_TAG] = returnThis$1;
1028 return IteratorConstructor;
1031 var aPossiblePrototype = function (it) {
1032 if (!isObject$4(it) && it !== null) {
1033 throw TypeError("Can't set " + String(it) + ' as a prototype');
1037 /* eslint-disable no-proto -- safe */
1039 // `Object.setPrototypeOf` method
1040 // https://tc39.es/ecma262/#sec-object.setprototypeof
1041 // Works with __proto__ only. Old v8 can't work with null proto objects.
1042 // eslint-disable-next-line es/no-object-setprototypeof -- safe
1043 var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1044 var CORRECT_SETTER = false;
1048 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
1049 setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1050 setter.call(test, []);
1051 CORRECT_SETTER = test instanceof Array;
1052 } catch (error) { /* empty */ }
1053 return function setPrototypeOf(O, proto) {
1055 aPossiblePrototype(proto);
1056 if (CORRECT_SETTER) setter.call(O, proto);
1057 else O.__proto__ = proto;
1062 var IteratorPrototype = iteratorsCore.IteratorPrototype;
1063 var BUGGY_SAFARI_ITERATORS = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1064 var ITERATOR$7 = wellKnownSymbol('iterator');
1066 var VALUES = 'values';
1067 var ENTRIES = 'entries';
1069 var returnThis = function () { return this; };
1071 var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1072 createIteratorConstructor(IteratorConstructor, NAME, next);
1074 var getIterationMethod = function (KIND) {
1075 if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1076 if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND];
1078 case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1079 case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1080 case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1081 } return function () { return new IteratorConstructor(this); };
1084 var TO_STRING_TAG = NAME + ' Iterator';
1085 var INCORRECT_VALUES_NAME = false;
1086 var IterablePrototype = Iterable.prototype;
1087 var nativeIterator = IterablePrototype[ITERATOR$7]
1088 || IterablePrototype['@@iterator']
1089 || DEFAULT && IterablePrototype[DEFAULT];
1090 var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT);
1091 var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1092 var CurrentIteratorPrototype, methods, KEY;
1095 if (anyNativeIterator) {
1096 CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1097 if (IteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) {
1098 if (objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype) {
1099 if (objectSetPrototypeOf) {
1100 objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype);
1101 } else if (typeof CurrentIteratorPrototype[ITERATOR$7] != 'function') {
1102 createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$7, returnThis);
1105 // Set @@toStringTag to native iterators
1106 setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1110 // fix Array.prototype.{ values, @@iterator }.name in V8 / FF
1111 if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1112 INCORRECT_VALUES_NAME = true;
1113 defaultIterator = function values() { return nativeIterator.call(this); };
1117 if (IterablePrototype[ITERATOR$7] !== defaultIterator) {
1118 createNonEnumerableProperty(IterablePrototype, ITERATOR$7, defaultIterator);
1120 iterators[NAME] = defaultIterator;
1122 // export additional methods
1125 values: getIterationMethod(VALUES),
1126 keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1127 entries: getIterationMethod(ENTRIES)
1129 if (FORCED) for (KEY in methods) {
1130 if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1131 redefine(IterablePrototype, KEY, methods[KEY]);
1133 } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods);
1139 var ARRAY_ITERATOR = 'Array Iterator';
1140 var setInternalState$7 = internalState.set;
1141 var getInternalState$5 = internalState.getterFor(ARRAY_ITERATOR);
1143 // `Array.prototype.entries` method
1144 // https://tc39.es/ecma262/#sec-array.prototype.entries
1145 // `Array.prototype.keys` method
1146 // https://tc39.es/ecma262/#sec-array.prototype.keys
1147 // `Array.prototype.values` method
1148 // https://tc39.es/ecma262/#sec-array.prototype.values
1149 // `Array.prototype[@@iterator]` method
1150 // https://tc39.es/ecma262/#sec-array.prototype-@@iterator
1151 // `CreateArrayIterator` internal method
1152 // https://tc39.es/ecma262/#sec-createarrayiterator
1153 var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1154 setInternalState$7(this, {
1155 type: ARRAY_ITERATOR,
1156 target: toIndexedObject(iterated), // target
1157 index: 0, // next index
1160 // `%ArrayIteratorPrototype%.next` method
1161 // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next
1163 var state = getInternalState$5(this);
1164 var target = state.target;
1165 var kind = state.kind;
1166 var index = state.index++;
1167 if (!target || index >= target.length) {
1168 state.target = undefined;
1169 return { value: undefined, done: true };
1171 if (kind == 'keys') return { value: index, done: false };
1172 if (kind == 'values') return { value: target[index], done: false };
1173 return { value: [index, target[index]], done: false };
1176 // argumentsList[@@iterator] is %ArrayProto_values%
1177 // https://tc39.es/ecma262/#sec-createunmappedargumentsobject
1178 // https://tc39.es/ecma262/#sec-createmappedargumentsobject
1179 iterators.Arguments = iterators.Array;
1181 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
1182 addToUnscopables('keys');
1183 addToUnscopables('values');
1184 addToUnscopables('entries');
1186 var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
1189 test$2[TO_STRING_TAG$3] = 'z';
1191 var toStringTagSupport = String(test$2) === '[object z]';
1193 var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1195 var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
1197 // fallback for IE11 Script Access Denied error
1198 var tryGet = function (it, key) {
1201 } catch (error) { /* empty */ }
1204 // getting tag from ES6+ `Object.prototype.toString`
1205 var classof = toStringTagSupport ? classofRaw : function (it) {
1207 return it === undefined ? 'Undefined' : it === null ? 'Null'
1208 // @@toStringTag case
1209 : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag
1211 : CORRECT_ARGUMENTS ? classofRaw(O)
1212 // ES3 arguments fallback
1213 : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
1216 // `Object.prototype.toString` method implementation
1217 // https://tc39.es/ecma262/#sec-object.prototype.tostring
1218 var objectToString$1 = toStringTagSupport ? {}.toString : function toString() {
1219 return '[object ' + classof(this) + ']';
1222 // `Object.prototype.toString` method
1223 // https://tc39.es/ecma262/#sec-object.prototype.tostring
1224 if (!toStringTagSupport) {
1225 redefine(Object.prototype, 'toString', objectToString$1, { unsafe: true });
1228 // `String.prototype.{ codePointAt, at }` methods implementation
1229 var createMethod$5 = function (CONVERT_TO_STRING) {
1230 return function ($this, pos) {
1231 var S = String(requireObjectCoercible($this));
1232 var position = toInteger(pos);
1233 var size = S.length;
1235 if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
1236 first = S.charCodeAt(position);
1237 return first < 0xD800 || first > 0xDBFF || position + 1 === size
1238 || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
1239 ? CONVERT_TO_STRING ? S.charAt(position) : first
1240 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
1244 var stringMultibyte = {
1245 // `String.prototype.codePointAt` method
1246 // https://tc39.es/ecma262/#sec-string.prototype.codepointat
1247 codeAt: createMethod$5(false),
1248 // `String.prototype.at` method
1249 // https://github.com/mathiasbynens/String.prototype.at
1250 charAt: createMethod$5(true)
1253 var charAt$1 = stringMultibyte.charAt;
1257 var STRING_ITERATOR = 'String Iterator';
1258 var setInternalState$6 = internalState.set;
1259 var getInternalState$4 = internalState.getterFor(STRING_ITERATOR);
1261 // `String.prototype[@@iterator]` method
1262 // https://tc39.es/ecma262/#sec-string.prototype-@@iterator
1263 defineIterator(String, 'String', function (iterated) {
1264 setInternalState$6(this, {
1265 type: STRING_ITERATOR,
1266 string: String(iterated),
1269 // `%StringIteratorPrototype%.next` method
1270 // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next
1271 }, function next() {
1272 var state = getInternalState$4(this);
1273 var string = state.string;
1274 var index = state.index;
1276 if (index >= string.length) return { value: undefined, done: true };
1277 point = charAt$1(string, index);
1278 state.index += point.length;
1279 return { value: point, done: false };
1282 // iterable DOM collections
1283 // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
1284 var domIterables = {
1286 CSSStyleDeclaration: 0,
1292 DataTransferItemList: 0,
1294 HTMLAllCollection: 0,
1297 HTMLSelectElement: 0,
1302 PaintRequestList: 0,
1310 SVGTransformList: 0,
1311 SourceBufferList: 0,
1313 TextTrackCueList: 0,
1318 var ITERATOR$6 = wellKnownSymbol('iterator');
1319 var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
1320 var ArrayValues = es_array_iterator.values;
1322 for (var COLLECTION_NAME$1 in domIterables) {
1323 var Collection$1 = global$2[COLLECTION_NAME$1];
1324 var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
1325 if (CollectionPrototype$1) {
1326 // some Chrome versions have non-configurable methods on DOMTokenList
1327 if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try {
1328 createNonEnumerableProperty(CollectionPrototype$1, ITERATOR$6, ArrayValues);
1330 CollectionPrototype$1[ITERATOR$6] = ArrayValues;
1332 if (!CollectionPrototype$1[TO_STRING_TAG$1]) {
1333 createNonEnumerableProperty(CollectionPrototype$1, TO_STRING_TAG$1, COLLECTION_NAME$1);
1335 if (domIterables[COLLECTION_NAME$1]) for (var METHOD_NAME in es_array_iterator) {
1336 // some Chrome versions have non-configurable methods on DOMTokenList
1337 if (CollectionPrototype$1[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
1338 createNonEnumerableProperty(CollectionPrototype$1, METHOD_NAME, es_array_iterator[METHOD_NAME]);
1340 CollectionPrototype$1[METHOD_NAME] = es_array_iterator[METHOD_NAME];
1346 // `IsArray` abstract operation
1347 // https://tc39.es/ecma262/#sec-isarray
1348 // eslint-disable-next-line es/no-array-isarray -- safe
1349 var isArray = Array.isArray || function isArray(arg) {
1350 return classofRaw(arg) == 'Array';
1353 /* eslint-disable es/no-object-getownpropertynames -- safe */
1355 var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
1357 var toString = {}.toString;
1359 var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
1360 ? Object.getOwnPropertyNames(window) : [];
1362 var getWindowNames = function (it) {
1364 return $getOwnPropertyNames$1(it);
1366 return windowNames.slice();
1370 // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
1371 var f$1 = function getOwnPropertyNames(it) {
1372 return windowNames && toString.call(it) == '[object Window]'
1373 ? getWindowNames(it)
1374 : $getOwnPropertyNames$1(toIndexedObject(it));
1377 var objectGetOwnPropertyNamesExternal = {
1381 var aFunction = function (it) {
1382 if (typeof it != 'function') {
1383 throw TypeError(String(it) + ' is not a function');
1387 // optional / simple context binding
1388 var functionBindContext = function (fn, that, length) {
1390 if (that === undefined) return fn;
1392 case 0: return function () {
1393 return fn.call(that);
1395 case 1: return function (a) {
1396 return fn.call(that, a);
1398 case 2: return function (a, b) {
1399 return fn.call(that, a, b);
1401 case 3: return function (a, b, c) {
1402 return fn.call(that, a, b, c);
1405 return function (/* ...args */) {
1406 return fn.apply(that, arguments);
1410 var SPECIES$6 = wellKnownSymbol('species');
1412 // `ArraySpeciesCreate` abstract operation
1413 // https://tc39.es/ecma262/#sec-arrayspeciescreate
1414 var arraySpeciesCreate = function (originalArray, length) {
1416 if (isArray(originalArray)) {
1417 C = originalArray.constructor;
1418 // cross-realm fallback
1419 if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
1420 else if (isObject$4(C)) {
1422 if (C === null) C = undefined;
1424 } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
1429 // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterOut }` methods implementation
1430 var createMethod$4 = function (TYPE) {
1431 var IS_MAP = TYPE == 1;
1432 var IS_FILTER = TYPE == 2;
1433 var IS_SOME = TYPE == 3;
1434 var IS_EVERY = TYPE == 4;
1435 var IS_FIND_INDEX = TYPE == 6;
1436 var IS_FILTER_OUT = TYPE == 7;
1437 var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
1438 return function ($this, callbackfn, that, specificCreate) {
1439 var O = toObject($this);
1440 var self = indexedObject(O);
1441 var boundFunction = functionBindContext(callbackfn, that, 3);
1442 var length = toLength(self.length);
1444 var create = specificCreate || arraySpeciesCreate;
1445 var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_OUT ? create($this, 0) : undefined;
1447 for (;length > index; index++) if (NO_HOLES || index in self) {
1448 value = self[index];
1449 result = boundFunction(value, index, O);
1451 if (IS_MAP) target[index] = result; // map
1452 else if (result) switch (TYPE) {
1453 case 3: return true; // some
1454 case 5: return value; // find
1455 case 6: return index; // findIndex
1456 case 2: push.call(target, value); // filter
1457 } else switch (TYPE) {
1458 case 4: return false; // every
1459 case 7: push.call(target, value); // filterOut
1463 return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
1467 var arrayIteration = {
1468 // `Array.prototype.forEach` method
1469 // https://tc39.es/ecma262/#sec-array.prototype.foreach
1470 forEach: createMethod$4(0),
1471 // `Array.prototype.map` method
1472 // https://tc39.es/ecma262/#sec-array.prototype.map
1473 map: createMethod$4(1),
1474 // `Array.prototype.filter` method
1475 // https://tc39.es/ecma262/#sec-array.prototype.filter
1476 filter: createMethod$4(2),
1477 // `Array.prototype.some` method
1478 // https://tc39.es/ecma262/#sec-array.prototype.some
1479 some: createMethod$4(3),
1480 // `Array.prototype.every` method
1481 // https://tc39.es/ecma262/#sec-array.prototype.every
1482 every: createMethod$4(4),
1483 // `Array.prototype.find` method
1484 // https://tc39.es/ecma262/#sec-array.prototype.find
1485 find: createMethod$4(5),
1486 // `Array.prototype.findIndex` method
1487 // https://tc39.es/ecma262/#sec-array.prototype.findIndex
1488 findIndex: createMethod$4(6),
1489 // `Array.prototype.filterOut` method
1490 // https://github.com/tc39/proposal-array-filtering
1491 filterOut: createMethod$4(7)
1494 var $forEach$2 = arrayIteration.forEach;
1496 var HIDDEN = sharedKey('hidden');
1497 var SYMBOL = 'Symbol';
1498 var PROTOTYPE$1 = 'prototype';
1499 var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
1500 var setInternalState$5 = internalState.set;
1501 var getInternalState$3 = internalState.getterFor(SYMBOL);
1502 var ObjectPrototype$2 = Object[PROTOTYPE$1];
1503 var $Symbol = global$2.Symbol;
1504 var $stringify = getBuiltIn('JSON', 'stringify');
1505 var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
1506 var nativeDefineProperty = objectDefineProperty.f;
1507 var nativeGetOwnPropertyNames = objectGetOwnPropertyNamesExternal.f;
1508 var nativePropertyIsEnumerable = objectPropertyIsEnumerable.f;
1509 var AllSymbols = shared('symbols');
1510 var ObjectPrototypeSymbols = shared('op-symbols');
1511 var StringToSymbolRegistry = shared('string-to-symbol-registry');
1512 var SymbolToStringRegistry = shared('symbol-to-string-registry');
1513 var WellKnownSymbolsStore = shared('wks');
1514 var QObject = global$2.QObject;
1515 // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
1516 var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
1518 // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
1519 var setSymbolDescriptor = descriptors && fails(function () {
1520 return objectCreate(nativeDefineProperty({}, 'a', {
1521 get: function () { return nativeDefineProperty(this, 'a', { value: 7 }).a; }
1523 }) ? function (O, P, Attributes) {
1524 var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$2, P);
1525 if (ObjectPrototypeDescriptor) delete ObjectPrototype$2[P];
1526 nativeDefineProperty(O, P, Attributes);
1527 if (ObjectPrototypeDescriptor && O !== ObjectPrototype$2) {
1528 nativeDefineProperty(ObjectPrototype$2, P, ObjectPrototypeDescriptor);
1530 } : nativeDefineProperty;
1532 var wrap$2 = function (tag, description) {
1533 var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
1534 setInternalState$5(symbol, {
1537 description: description
1539 if (!descriptors) symbol.description = description;
1543 var isSymbol$1 = useSymbolAsUid ? function (it) {
1544 return typeof it == 'symbol';
1546 return Object(it) instanceof $Symbol;
1549 var $defineProperty = function defineProperty(O, P, Attributes) {
1550 if (O === ObjectPrototype$2) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
1552 var key = toPrimitive(P, true);
1553 anObject(Attributes);
1554 if (has$1(AllSymbols, key)) {
1555 if (!Attributes.enumerable) {
1556 if (!has$1(O, HIDDEN)) nativeDefineProperty(O, HIDDEN, createPropertyDescriptor(1, {}));
1557 O[HIDDEN][key] = true;
1559 if (has$1(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
1560 Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
1561 } return setSymbolDescriptor(O, key, Attributes);
1562 } return nativeDefineProperty(O, key, Attributes);
1565 var $defineProperties = function defineProperties(O, Properties) {
1567 var properties = toIndexedObject(Properties);
1568 var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
1569 $forEach$2(keys, function (key) {
1570 if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
1575 var $create = function create(O, Properties) {
1576 return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
1579 var $propertyIsEnumerable = function propertyIsEnumerable(V) {
1580 var P = toPrimitive(V, true);
1581 var enumerable = nativePropertyIsEnumerable.call(this, P);
1582 if (this === ObjectPrototype$2 && has$1(AllSymbols, P) && !has$1(ObjectPrototypeSymbols, P)) return false;
1583 return enumerable || !has$1(this, P) || !has$1(AllSymbols, P) || has$1(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
1586 var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
1587 var it = toIndexedObject(O);
1588 var key = toPrimitive(P, true);
1589 if (it === ObjectPrototype$2 && has$1(AllSymbols, key) && !has$1(ObjectPrototypeSymbols, key)) return;
1590 var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
1591 if (descriptor && has$1(AllSymbols, key) && !(has$1(it, HIDDEN) && it[HIDDEN][key])) {
1592 descriptor.enumerable = true;
1597 var $getOwnPropertyNames = function getOwnPropertyNames(O) {
1598 var names = nativeGetOwnPropertyNames(toIndexedObject(O));
1600 $forEach$2(names, function (key) {
1601 if (!has$1(AllSymbols, key) && !has$1(hiddenKeys$1, key)) result.push(key);
1606 var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
1607 var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$2;
1608 var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
1610 $forEach$2(names, function (key) {
1611 if (has$1(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has$1(ObjectPrototype$2, key))) {
1612 result.push(AllSymbols[key]);
1618 // `Symbol` constructor
1619 // https://tc39.es/ecma262/#sec-symbol-constructor
1620 if (!nativeSymbol) {
1621 $Symbol = function Symbol() {
1622 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
1623 var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
1624 var tag = uid(description);
1625 var setter = function (value) {
1626 if (this === ObjectPrototype$2) setter.call(ObjectPrototypeSymbols, value);
1627 if (has$1(this, HIDDEN) && has$1(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
1628 setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
1630 if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype$2, tag, { configurable: true, set: setter });
1631 return wrap$2(tag, description);
1634 redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
1635 return getInternalState$3(this).tag;
1638 redefine($Symbol, 'withoutSetter', function (description) {
1639 return wrap$2(uid(description), description);
1642 objectPropertyIsEnumerable.f = $propertyIsEnumerable;
1643 objectDefineProperty.f = $defineProperty;
1644 objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
1645 objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1646 objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1648 wellKnownSymbolWrapped.f = function (name) {
1649 return wrap$2(wellKnownSymbol(name), name);
1653 // https://github.com/tc39/proposal-Symbol-description
1654 nativeDefineProperty($Symbol[PROTOTYPE$1], 'description', {
1656 get: function description() {
1657 return getInternalState$3(this).description;
1661 redefine(ObjectPrototype$2, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1666 _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1670 $forEach$2(objectKeys(WellKnownSymbolsStore), function (name) {
1671 defineWellKnownSymbol(name);
1674 _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1675 // `Symbol.for` method
1676 // https://tc39.es/ecma262/#sec-symbol.for
1677 'for': function (key) {
1678 var string = String(key);
1679 if (has$1(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1680 var symbol = $Symbol(string);
1681 StringToSymbolRegistry[string] = symbol;
1682 SymbolToStringRegistry[symbol] = string;
1685 // `Symbol.keyFor` method
1686 // https://tc39.es/ecma262/#sec-symbol.keyfor
1687 keyFor: function keyFor(sym) {
1688 if (!isSymbol$1(sym)) throw TypeError(sym + ' is not a symbol');
1689 if (has$1(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1691 useSetter: function () { USE_SETTER = true; },
1692 useSimple: function () { USE_SETTER = false; }
1695 _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1696 // `Object.create` method
1697 // https://tc39.es/ecma262/#sec-object.create
1699 // `Object.defineProperty` method
1700 // https://tc39.es/ecma262/#sec-object.defineproperty
1701 defineProperty: $defineProperty,
1702 // `Object.defineProperties` method
1703 // https://tc39.es/ecma262/#sec-object.defineproperties
1704 defineProperties: $defineProperties,
1705 // `Object.getOwnPropertyDescriptor` method
1706 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors
1707 getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1710 _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1711 // `Object.getOwnPropertyNames` method
1712 // https://tc39.es/ecma262/#sec-object.getownpropertynames
1713 getOwnPropertyNames: $getOwnPropertyNames,
1714 // `Object.getOwnPropertySymbols` method
1715 // https://tc39.es/ecma262/#sec-object.getownpropertysymbols
1716 getOwnPropertySymbols: $getOwnPropertySymbols
1719 // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1720 // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1721 _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1722 getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1723 return objectGetOwnPropertySymbols.f(toObject(it));
1727 // `JSON.stringify` method behavior with symbols
1728 // https://tc39.es/ecma262/#sec-json.stringify
1730 var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1731 var symbol = $Symbol();
1732 // MS Edge converts symbol values to JSON as {}
1733 return $stringify([symbol]) != '[null]'
1734 // WebKit converts symbol values to JSON as null
1735 || $stringify({ a: symbol }) != '{}'
1736 // V8 throws on boxed symbols
1737 || $stringify(Object(symbol)) != '{}';
1740 _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1741 // eslint-disable-next-line no-unused-vars -- required for `.length`
1742 stringify: function stringify(it, replacer, space) {
1746 while (arguments.length > index) args.push(arguments[index++]);
1747 $replacer = replacer;
1748 if (!isObject$4(replacer) && it === undefined || isSymbol$1(it)) return; // IE8 returns string on undefined
1749 if (!isArray(replacer)) replacer = function (key, value) {
1750 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1751 if (!isSymbol$1(value)) return value;
1754 return $stringify.apply(null, args);
1759 // `Symbol.prototype[@@toPrimitive]` method
1760 // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive
1761 if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1762 createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1764 // `Symbol.prototype[@@toStringTag]` property
1765 // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag
1766 setToStringTag($Symbol, SYMBOL);
1768 hiddenKeys$1[HIDDEN] = true;
1770 var defineProperty$7 = objectDefineProperty.f;
1773 var NativeSymbol = global$2.Symbol;
1775 if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1777 NativeSymbol().description !== undefined
1779 var EmptyStringDescriptionStore = {};
1780 // wrap Symbol constructor for correct work with undefined description
1781 var SymbolWrapper = function Symbol() {
1782 var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1783 var result = this instanceof SymbolWrapper
1784 ? new NativeSymbol(description)
1785 // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1786 : description === undefined ? NativeSymbol() : NativeSymbol(description);
1787 if (description === '') EmptyStringDescriptionStore[result] = true;
1790 copyConstructorProperties(SymbolWrapper, NativeSymbol);
1791 var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1792 symbolPrototype.constructor = SymbolWrapper;
1794 var symbolToString = symbolPrototype.toString;
1795 var native = String(NativeSymbol('test')) == 'Symbol(test)';
1796 var regexp = /^Symbol\((.*)\)[^)]+$/;
1797 defineProperty$7(symbolPrototype, 'description', {
1799 get: function description() {
1800 var symbol = isObject$4(this) ? this.valueOf() : this;
1801 var string = symbolToString.call(symbol);
1802 if (has$1(EmptyStringDescriptionStore, symbol)) return '';
1803 var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1804 return desc === '' ? undefined : desc;
1808 _export({ global: true, forced: true }, {
1809 Symbol: SymbolWrapper
1813 // eslint-disable-next-line es/no-typed-arrays -- safe
1814 var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1816 var redefineAll = function (target, src, options) {
1817 for (var key in src) redefine(target, key, src[key], options);
1821 var anInstance = function (it, Constructor, name) {
1822 if (!(it instanceof Constructor)) {
1823 throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1827 // `ToIndex` abstract operation
1828 // https://tc39.es/ecma262/#sec-toindex
1829 var toIndex = function (it) {
1830 if (it === undefined) return 0;
1831 var number = toInteger(it);
1832 var length = toLength(number);
1833 if (number !== length) throw RangeError('Wrong length or index');
1837 // IEEE754 conversions based on https://github.com/feross/ieee754
1838 var abs$4 = Math.abs;
1839 var pow$2 = Math.pow;
1840 var floor$6 = Math.floor;
1841 var log$2 = Math.log;
1844 var pack = function (number, mantissaLength, bytes) {
1845 var buffer = new Array(bytes);
1846 var exponentLength = bytes * 8 - mantissaLength - 1;
1847 var eMax = (1 << exponentLength) - 1;
1848 var eBias = eMax >> 1;
1849 var rt = mantissaLength === 23 ? pow$2(2, -24) - pow$2(2, -77) : 0;
1850 var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1852 var exponent, mantissa, c;
1853 number = abs$4(number);
1854 // eslint-disable-next-line no-self-compare -- NaN check
1855 if (number != number || number === Infinity) {
1856 // eslint-disable-next-line no-self-compare -- NaN check
1857 mantissa = number != number ? 1 : 0;
1860 exponent = floor$6(log$2(number) / LN2);
1861 if (number * (c = pow$2(2, -exponent)) < 1) {
1865 if (exponent + eBias >= 1) {
1868 number += rt * pow$2(2, 1 - eBias);
1870 if (number * c >= 2) {
1874 if (exponent + eBias >= eMax) {
1877 } else if (exponent + eBias >= 1) {
1878 mantissa = (number * c - 1) * pow$2(2, mantissaLength);
1879 exponent = exponent + eBias;
1881 mantissa = number * pow$2(2, eBias - 1) * pow$2(2, mantissaLength);
1885 for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1886 exponent = exponent << mantissaLength | mantissa;
1887 exponentLength += mantissaLength;
1888 for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1889 buffer[--index] |= sign * 128;
1893 var unpack = function (buffer, mantissaLength) {
1894 var bytes = buffer.length;
1895 var exponentLength = bytes * 8 - mantissaLength - 1;
1896 var eMax = (1 << exponentLength) - 1;
1897 var eBias = eMax >> 1;
1898 var nBits = exponentLength - 7;
1899 var index = bytes - 1;
1900 var sign = buffer[index--];
1901 var exponent = sign & 127;
1904 for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1905 mantissa = exponent & (1 << -nBits) - 1;
1906 exponent >>= -nBits;
1907 nBits += mantissaLength;
1908 for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1909 if (exponent === 0) {
1910 exponent = 1 - eBias;
1911 } else if (exponent === eMax) {
1912 return mantissa ? NaN : sign ? -Infinity : Infinity;
1914 mantissa = mantissa + pow$2(2, mantissaLength);
1915 exponent = exponent - eBias;
1916 } return (sign ? -1 : 1) * mantissa * pow$2(2, exponent - mantissaLength);
1924 // `Array.prototype.fill` method implementation
1925 // https://tc39.es/ecma262/#sec-array.prototype.fill
1926 var arrayFill = function fill(value /* , start = 0, end = @length */) {
1927 var O = toObject(this);
1928 var length = toLength(O.length);
1929 var argumentsLength = arguments.length;
1930 var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1931 var end = argumentsLength > 2 ? arguments[2] : undefined;
1932 var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1933 while (endPos > index) O[index++] = value;
1937 var getOwnPropertyNames$3 = objectGetOwnPropertyNames.f;
1938 var defineProperty$6 = objectDefineProperty.f;
1943 var getInternalState$2 = internalState.get;
1944 var setInternalState$4 = internalState.set;
1945 var ARRAY_BUFFER$1 = 'ArrayBuffer';
1946 var DATA_VIEW = 'DataView';
1947 var PROTOTYPE = 'prototype';
1948 var WRONG_LENGTH = 'Wrong length';
1949 var WRONG_INDEX = 'Wrong index';
1950 var NativeArrayBuffer$1 = global$2[ARRAY_BUFFER$1];
1951 var $ArrayBuffer = NativeArrayBuffer$1;
1952 var $DataView = global$2[DATA_VIEW];
1953 var $DataViewPrototype = $DataView && $DataView[PROTOTYPE];
1954 var ObjectPrototype$1 = Object.prototype;
1955 var RangeError$1 = global$2.RangeError;
1957 var packIEEE754 = ieee754$1.pack;
1958 var unpackIEEE754 = ieee754$1.unpack;
1960 var packInt8 = function (number) {
1961 return [number & 0xFF];
1964 var packInt16 = function (number) {
1965 return [number & 0xFF, number >> 8 & 0xFF];
1968 var packInt32 = function (number) {
1969 return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1972 var unpackInt32 = function (buffer) {
1973 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1976 var packFloat32 = function (number) {
1977 return packIEEE754(number, 23, 4);
1980 var packFloat64 = function (number) {
1981 return packIEEE754(number, 52, 8);
1984 var addGetter = function (Constructor, key) {
1985 defineProperty$6(Constructor[PROTOTYPE], key, { get: function () { return getInternalState$2(this)[key]; } });
1988 var get$4 = function (view, count, index, isLittleEndian) {
1989 var intIndex = toIndex(index);
1990 var store = getInternalState$2(view);
1991 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1992 var bytes = getInternalState$2(store.buffer).bytes;
1993 var start = intIndex + store.byteOffset;
1994 var pack = bytes.slice(start, start + count);
1995 return isLittleEndian ? pack : pack.reverse();
1998 var set$3 = function (view, count, index, conversion, value, isLittleEndian) {
1999 var intIndex = toIndex(index);
2000 var store = getInternalState$2(view);
2001 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
2002 var bytes = getInternalState$2(store.buffer).bytes;
2003 var start = intIndex + store.byteOffset;
2004 var pack = conversion(+value);
2005 for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
2008 if (!arrayBufferNative) {
2009 $ArrayBuffer = function ArrayBuffer(length) {
2010 anInstance(this, $ArrayBuffer, ARRAY_BUFFER$1);
2011 var byteLength = toIndex(length);
2012 setInternalState$4(this, {
2013 bytes: arrayFill.call(new Array(byteLength), 0),
2014 byteLength: byteLength
2016 if (!descriptors) this.byteLength = byteLength;
2019 $DataView = function DataView(buffer, byteOffset, byteLength) {
2020 anInstance(this, $DataView, DATA_VIEW);
2021 anInstance(buffer, $ArrayBuffer, DATA_VIEW);
2022 var bufferLength = getInternalState$2(buffer).byteLength;
2023 var offset = toInteger(byteOffset);
2024 if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
2025 byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
2026 if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
2027 setInternalState$4(this, {
2029 byteLength: byteLength,
2033 this.buffer = buffer;
2034 this.byteLength = byteLength;
2035 this.byteOffset = offset;
2040 addGetter($ArrayBuffer, 'byteLength');
2041 addGetter($DataView, 'buffer');
2042 addGetter($DataView, 'byteLength');
2043 addGetter($DataView, 'byteOffset');
2046 redefineAll($DataView[PROTOTYPE], {
2047 getInt8: function getInt8(byteOffset) {
2048 return get$4(this, 1, byteOffset)[0] << 24 >> 24;
2050 getUint8: function getUint8(byteOffset) {
2051 return get$4(this, 1, byteOffset)[0];
2053 getInt16: function getInt16(byteOffset /* , littleEndian */) {
2054 var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2055 return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
2057 getUint16: function getUint16(byteOffset /* , littleEndian */) {
2058 var bytes = get$4(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
2059 return bytes[1] << 8 | bytes[0];
2061 getInt32: function getInt32(byteOffset /* , littleEndian */) {
2062 return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
2064 getUint32: function getUint32(byteOffset /* , littleEndian */) {
2065 return unpackInt32(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
2067 getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
2068 return unpackIEEE754(get$4(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
2070 getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
2071 return unpackIEEE754(get$4(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
2073 setInt8: function setInt8(byteOffset, value) {
2074 set$3(this, 1, byteOffset, packInt8, value);
2076 setUint8: function setUint8(byteOffset, value) {
2077 set$3(this, 1, byteOffset, packInt8, value);
2079 setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
2080 set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2082 setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
2083 set$3(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
2085 setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
2086 set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2088 setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
2089 set$3(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
2091 setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
2092 set$3(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
2094 setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
2095 set$3(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
2099 /* eslint-disable no-new -- required for testing */
2100 if (!fails(function () {
2101 NativeArrayBuffer$1(1);
2102 }) || !fails(function () {
2103 new NativeArrayBuffer$1(-1);
2104 }) || fails(function () {
2105 new NativeArrayBuffer$1();
2106 new NativeArrayBuffer$1(1.5);
2107 new NativeArrayBuffer$1(NaN);
2108 return NativeArrayBuffer$1.name != ARRAY_BUFFER$1;
2110 /* eslint-enable no-new -- required for testing */
2111 $ArrayBuffer = function ArrayBuffer(length) {
2112 anInstance(this, $ArrayBuffer);
2113 return new NativeArrayBuffer$1(toIndex(length));
2115 var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE] = NativeArrayBuffer$1[PROTOTYPE];
2116 for (var keys$2 = getOwnPropertyNames$3(NativeArrayBuffer$1), j$2 = 0, key$1; keys$2.length > j$2;) {
2117 if (!((key$1 = keys$2[j$2++]) in $ArrayBuffer)) {
2118 createNonEnumerableProperty($ArrayBuffer, key$1, NativeArrayBuffer$1[key$1]);
2121 ArrayBufferPrototype.constructor = $ArrayBuffer;
2124 // WebKit bug - the same parent prototype for typed arrays and data view
2125 if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$1) {
2126 objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$1);
2129 // iOS Safari 7.x bug
2130 var testView = new $DataView(new $ArrayBuffer(2));
2131 var $setInt8 = $DataViewPrototype.setInt8;
2132 testView.setInt8(0, 2147483648);
2133 testView.setInt8(1, 2147483649);
2134 if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
2135 setInt8: function setInt8(byteOffset, value) {
2136 $setInt8.call(this, byteOffset, value << 24 >> 24);
2138 setUint8: function setUint8(byteOffset, value) {
2139 $setInt8.call(this, byteOffset, value << 24 >> 24);
2141 }, { unsafe: true });
2144 setToStringTag($ArrayBuffer, ARRAY_BUFFER$1);
2145 setToStringTag($DataView, DATA_VIEW);
2148 ArrayBuffer: $ArrayBuffer,
2152 var SPECIES$5 = wellKnownSymbol('species');
2154 // `SpeciesConstructor` abstract operation
2155 // https://tc39.es/ecma262/#sec-speciesconstructor
2156 var speciesConstructor = function (O, defaultConstructor) {
2157 var C = anObject(O).constructor;
2159 return C === undefined || (S = anObject(C)[SPECIES$5]) == undefined ? defaultConstructor : aFunction(S);
2162 var ArrayBuffer$3 = arrayBuffer.ArrayBuffer;
2163 var DataView$1 = arrayBuffer.DataView;
2164 var nativeArrayBufferSlice = ArrayBuffer$3.prototype.slice;
2166 var INCORRECT_SLICE = fails(function () {
2167 return !new ArrayBuffer$3(2).slice(1, undefined).byteLength;
2170 // `ArrayBuffer.prototype.slice` method
2171 // https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
2172 _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2173 slice: function slice(start, end) {
2174 if (nativeArrayBufferSlice !== undefined && end === undefined) {
2175 return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2177 var length = anObject(this).byteLength;
2178 var first = toAbsoluteIndex(start, length);
2179 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2180 var result = new (speciesConstructor(this, ArrayBuffer$3))(toLength(fin - first));
2181 var viewSource = new DataView$1(this);
2182 var viewTarget = new DataView$1(result);
2184 while (first < fin) {
2185 viewTarget.setUint8(index++, viewSource.getUint8(first++));
2190 // `DataView` constructor
2191 // https://tc39.es/ecma262/#sec-dataview-constructor
2192 _export({ global: true, forced: !arrayBufferNative }, {
2193 DataView: arrayBuffer.DataView
2196 var defineProperty$5 = objectDefineProperty.f;
2202 var Int8Array$3 = global$2.Int8Array;
2203 var Int8ArrayPrototype = Int8Array$3 && Int8Array$3.prototype;
2204 var Uint8ClampedArray = global$2.Uint8ClampedArray;
2205 var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2206 var TypedArray = Int8Array$3 && objectGetPrototypeOf(Int8Array$3);
2207 var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2208 var ObjectPrototype = Object.prototype;
2209 var isPrototypeOf = ObjectPrototype.isPrototypeOf;
2211 var TO_STRING_TAG = wellKnownSymbol('toStringTag');
2212 var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2213 // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2214 var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferNative && !!objectSetPrototypeOf && classof(global$2.opera) !== 'Opera';
2215 var TYPED_ARRAY_TAG_REQIRED = false;
2218 var TypedArrayConstructorsList = {
2221 Uint8ClampedArray: 1,
2230 var BigIntArrayConstructorsList = {
2235 var isView = function isView(it) {
2236 if (!isObject$4(it)) return false;
2237 var klass = classof(it);
2238 return klass === 'DataView'
2239 || has$1(TypedArrayConstructorsList, klass)
2240 || has$1(BigIntArrayConstructorsList, klass);
2243 var isTypedArray = function (it) {
2244 if (!isObject$4(it)) return false;
2245 var klass = classof(it);
2246 return has$1(TypedArrayConstructorsList, klass)
2247 || has$1(BigIntArrayConstructorsList, klass);
2250 var aTypedArray$m = function (it) {
2251 if (isTypedArray(it)) return it;
2252 throw TypeError('Target is not a typed array');
2255 var aTypedArrayConstructor$4 = function (C) {
2256 if (objectSetPrototypeOf) {
2257 if (isPrototypeOf.call(TypedArray, C)) return C;
2258 } else for (var ARRAY in TypedArrayConstructorsList) if (has$1(TypedArrayConstructorsList, NAME$1)) {
2259 var TypedArrayConstructor = global$2[ARRAY];
2260 if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2263 } throw TypeError('Target is not a typed array constructor');
2266 var exportTypedArrayMethod$n = function (KEY, property, forced) {
2267 if (!descriptors) return;
2268 if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2269 var TypedArrayConstructor = global$2[ARRAY];
2270 if (TypedArrayConstructor && has$1(TypedArrayConstructor.prototype, KEY)) try {
2271 delete TypedArrayConstructor.prototype[KEY];
2272 } catch (error) { /* empty */ }
2274 if (!TypedArrayPrototype[KEY] || forced) {
2275 redefine(TypedArrayPrototype, KEY, forced ? property
2276 : NATIVE_ARRAY_BUFFER_VIEWS$2 && Int8ArrayPrototype[KEY] || property);
2280 var exportTypedArrayStaticMethod$1 = function (KEY, property, forced) {
2281 var ARRAY, TypedArrayConstructor;
2282 if (!descriptors) return;
2283 if (objectSetPrototypeOf) {
2284 if (forced) for (ARRAY in TypedArrayConstructorsList) {
2285 TypedArrayConstructor = global$2[ARRAY];
2286 if (TypedArrayConstructor && has$1(TypedArrayConstructor, KEY)) try {
2287 delete TypedArrayConstructor[KEY];
2288 } catch (error) { /* empty */ }
2290 if (!TypedArray[KEY] || forced) {
2291 // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2293 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS$2 && TypedArray[KEY] || property);
2294 } catch (error) { /* empty */ }
2297 for (ARRAY in TypedArrayConstructorsList) {
2298 TypedArrayConstructor = global$2[ARRAY];
2299 if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2300 redefine(TypedArrayConstructor, KEY, property);
2305 for (NAME$1 in TypedArrayConstructorsList) {
2306 if (!global$2[NAME$1]) NATIVE_ARRAY_BUFFER_VIEWS$2 = false;
2309 // WebKit bug - typed arrays constructors prototype is Object.prototype
2310 if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2311 // eslint-disable-next-line no-shadow -- safe
2312 TypedArray = function TypedArray() {
2313 throw TypeError('Incorrect invocation');
2315 if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2316 if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1], TypedArray);
2320 if (!NATIVE_ARRAY_BUFFER_VIEWS$2 || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype) {
2321 TypedArrayPrototype = TypedArray.prototype;
2322 if (NATIVE_ARRAY_BUFFER_VIEWS$2) for (NAME$1 in TypedArrayConstructorsList) {
2323 if (global$2[NAME$1]) objectSetPrototypeOf(global$2[NAME$1].prototype, TypedArrayPrototype);
2327 // WebKit bug - one more object in Uint8ClampedArray prototype chain
2328 if (NATIVE_ARRAY_BUFFER_VIEWS$2 && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2329 objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2332 if (descriptors && !has$1(TypedArrayPrototype, TO_STRING_TAG)) {
2333 TYPED_ARRAY_TAG_REQIRED = true;
2334 defineProperty$5(TypedArrayPrototype, TO_STRING_TAG, { get: function () {
2335 return isObject$4(this) ? this[TYPED_ARRAY_TAG] : undefined;
2337 for (NAME$1 in TypedArrayConstructorsList) if (global$2[NAME$1]) {
2338 createNonEnumerableProperty(global$2[NAME$1], TYPED_ARRAY_TAG, NAME$1);
2342 var arrayBufferViewCore = {
2343 NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS$2,
2344 TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2345 aTypedArray: aTypedArray$m,
2346 aTypedArrayConstructor: aTypedArrayConstructor$4,
2347 exportTypedArrayMethod: exportTypedArrayMethod$n,
2348 exportTypedArrayStaticMethod: exportTypedArrayStaticMethod$1,
2350 isTypedArray: isTypedArray,
2351 TypedArray: TypedArray,
2352 TypedArrayPrototype: TypedArrayPrototype
2355 var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2357 // `ArrayBuffer.isView` method
2358 // https://tc39.es/ecma262/#sec-arraybuffer.isview
2359 _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2360 isView: arrayBufferViewCore.isView
2363 var SPECIES$4 = wellKnownSymbol('species');
2365 var setSpecies = function (CONSTRUCTOR_NAME) {
2366 var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
2367 var defineProperty = objectDefineProperty.f;
2369 if (descriptors && Constructor && !Constructor[SPECIES$4]) {
2370 defineProperty(Constructor, SPECIES$4, {
2372 get: function () { return this; }
2377 var ARRAY_BUFFER = 'ArrayBuffer';
2378 var ArrayBuffer$2 = arrayBuffer[ARRAY_BUFFER];
2379 var NativeArrayBuffer = global$2[ARRAY_BUFFER];
2381 // `ArrayBuffer` constructor
2382 // https://tc39.es/ecma262/#sec-arraybuffer-constructor
2383 _export({ global: true, forced: NativeArrayBuffer !== ArrayBuffer$2 }, {
2384 ArrayBuffer: ArrayBuffer$2
2387 setSpecies(ARRAY_BUFFER);
2389 var arrayMethodIsStrict = function (METHOD_NAME, argument) {
2390 var method = [][METHOD_NAME];
2391 return !!method && fails(function () {
2392 // eslint-disable-next-line no-useless-call,no-throw-literal -- required for testing
2393 method.call(null, argument || function () { throw 1; }, 1);
2397 /* eslint-disable es/no-array-prototype-indexof -- required for testing */
2399 var $indexOf$1 = arrayIncludes.indexOf;
2402 var nativeIndexOf = [].indexOf;
2404 var NEGATIVE_ZERO$1 = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
2405 var STRICT_METHOD$7 = arrayMethodIsStrict('indexOf');
2407 // `Array.prototype.indexOf` method
2408 // https://tc39.es/ecma262/#sec-array.prototype.indexof
2409 _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO$1 || !STRICT_METHOD$7 }, {
2410 indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
2411 return NEGATIVE_ZERO$1
2413 ? nativeIndexOf.apply(this, arguments) || 0
2414 : $indexOf$1(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
2418 var SPECIES$3 = wellKnownSymbol('species');
2420 var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
2421 // We can't use this feature detection in V8 since it causes
2422 // deoptimization and serious performance degradation
2423 // https://github.com/zloirock/core-js/issues/677
2424 return engineV8Version >= 51 || !fails(function () {
2426 var constructor = array.constructor = {};
2427 constructor[SPECIES$3] = function () {
2430 return array[METHOD_NAME](Boolean).foo !== 1;
2434 var $map$1 = arrayIteration.map;
2437 var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('map');
2439 // `Array.prototype.map` method
2440 // https://tc39.es/ecma262/#sec-array.prototype.map
2441 // with adding support of @@species
2442 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, {
2443 map: function map(callbackfn /* , thisArg */) {
2444 return $map$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2448 var $forEach$1 = arrayIteration.forEach;
2451 var STRICT_METHOD$6 = arrayMethodIsStrict('forEach');
2453 // `Array.prototype.forEach` method implementation
2454 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2455 var arrayForEach = !STRICT_METHOD$6 ? function forEach(callbackfn /* , thisArg */) {
2456 return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
2457 // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2460 // `Array.prototype.forEach` method
2461 // https://tc39.es/ecma262/#sec-array.prototype.foreach
2462 // eslint-disable-next-line es/no-array-prototype-foreach -- safe
2463 _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
2464 forEach: arrayForEach
2467 for (var COLLECTION_NAME in domIterables) {
2468 var Collection = global$2[COLLECTION_NAME];
2469 var CollectionPrototype = Collection && Collection.prototype;
2470 // some Chrome versions have non-configurable methods on DOMTokenList
2471 if (CollectionPrototype && CollectionPrototype.forEach !== arrayForEach) try {
2472 createNonEnumerableProperty(CollectionPrototype, 'forEach', arrayForEach);
2474 CollectionPrototype.forEach = arrayForEach;
2478 // `Array.isArray` method
2479 // https://tc39.es/ecma262/#sec-array.isarray
2480 _export({ target: 'Array', stat: true }, {
2484 var getOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2486 // eslint-disable-next-line es/no-object-getownpropertynames -- required for testing
2487 var FAILS_ON_PRIMITIVES$4 = fails(function () { return !Object.getOwnPropertyNames(1); });
2489 // `Object.getOwnPropertyNames` method
2490 // https://tc39.es/ecma262/#sec-object.getownpropertynames
2491 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4 }, {
2492 getOwnPropertyNames: getOwnPropertyNames$2
2495 var nativePromiseConstructor = global$2.Promise;
2497 var ITERATOR$5 = wellKnownSymbol('iterator');
2498 var ArrayPrototype = Array.prototype;
2500 // check on default Array iterator
2501 var isArrayIteratorMethod = function (it) {
2502 return it !== undefined && (iterators.Array === it || ArrayPrototype[ITERATOR$5] === it);
2505 var ITERATOR$4 = wellKnownSymbol('iterator');
2507 var getIteratorMethod = function (it) {
2508 if (it != undefined) return it[ITERATOR$4]
2510 || iterators[classof(it)];
2513 var iteratorClose = function (iterator) {
2514 var returnMethod = iterator['return'];
2515 if (returnMethod !== undefined) {
2516 return anObject(returnMethod.call(iterator)).value;
2520 var Result = function (stopped, result) {
2521 this.stopped = stopped;
2522 this.result = result;
2525 var iterate = function (iterable, unboundFunction, options) {
2526 var that = options && options.that;
2527 var AS_ENTRIES = !!(options && options.AS_ENTRIES);
2528 var IS_ITERATOR = !!(options && options.IS_ITERATOR);
2529 var INTERRUPTED = !!(options && options.INTERRUPTED);
2530 var fn = functionBindContext(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
2531 var iterator, iterFn, index, length, result, next, step;
2533 var stop = function (condition) {
2534 if (iterator) iteratorClose(iterator);
2535 return new Result(true, condition);
2538 var callFn = function (value) {
2541 return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
2542 } return INTERRUPTED ? fn(value, stop) : fn(value);
2546 iterator = iterable;
2548 iterFn = getIteratorMethod(iterable);
2549 if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2550 // optimisation for array iterators
2551 if (isArrayIteratorMethod(iterFn)) {
2552 for (index = 0, length = toLength(iterable.length); length > index; index++) {
2553 result = callFn(iterable[index]);
2554 if (result && result instanceof Result) return result;
2555 } return new Result(false);
2557 iterator = iterFn.call(iterable);
2560 next = iterator.next;
2561 while (!(step = next.call(iterator)).done) {
2563 result = callFn(step.value);
2565 iteratorClose(iterator);
2568 if (typeof result == 'object' && result && result instanceof Result) return result;
2569 } return new Result(false);
2572 var ITERATOR$3 = wellKnownSymbol('iterator');
2573 var SAFE_CLOSING = false;
2577 var iteratorWithReturn = {
2579 return { done: !!called++ };
2581 'return': function () {
2582 SAFE_CLOSING = true;
2585 iteratorWithReturn[ITERATOR$3] = function () {
2588 // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing
2589 Array.from(iteratorWithReturn, function () { throw 2; });
2590 } catch (error) { /* empty */ }
2592 var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2593 if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2594 var ITERATION_SUPPORT = false;
2597 object[ITERATOR$3] = function () {
2600 return { done: ITERATION_SUPPORT = true };
2605 } catch (error) { /* empty */ }
2606 return ITERATION_SUPPORT;
2609 var engineIsIos = /(?:iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2611 var engineIsNode = classofRaw(global$2.process) == 'process';
2613 var location$1 = global$2.location;
2614 var set$2 = global$2.setImmediate;
2615 var clear = global$2.clearImmediate;
2616 var process$3 = global$2.process;
2617 var MessageChannel = global$2.MessageChannel;
2618 var Dispatch$1 = global$2.Dispatch;
2621 var ONREADYSTATECHANGE = 'onreadystatechange';
2622 var defer, channel, port;
2624 var run = function (id) {
2625 // eslint-disable-next-line no-prototype-builtins -- safe
2626 if (queue.hasOwnProperty(id)) {
2633 var runner = function (id) {
2634 return function () {
2639 var listener = function (event) {
2643 var post = function (id) {
2644 // old engines have not location.origin
2645 global$2.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2648 // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2649 if (!set$2 || !clear) {
2650 set$2 = function setImmediate(fn) {
2653 while (arguments.length > i) args.push(arguments[i++]);
2654 queue[++counter] = function () {
2655 // eslint-disable-next-line no-new-func -- spec requirement
2656 (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2661 clear = function clearImmediate(id) {
2666 defer = function (id) {
2667 process$3.nextTick(runner(id));
2669 // Sphere (JS game engine) Dispatch API
2670 } else if (Dispatch$1 && Dispatch$1.now) {
2671 defer = function (id) {
2672 Dispatch$1.now(runner(id));
2674 // Browsers with MessageChannel, includes WebWorkers
2675 // except iOS - https://github.com/zloirock/core-js/issues/624
2676 } else if (MessageChannel && !engineIsIos) {
2677 channel = new MessageChannel();
2678 port = channel.port2;
2679 channel.port1.onmessage = listener;
2680 defer = functionBindContext(port.postMessage, port, 1);
2681 // Browsers with postMessage, skip WebWorkers
2682 // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2684 global$2.addEventListener &&
2685 typeof postMessage == 'function' &&
2686 !global$2.importScripts &&
2687 location$1 && location$1.protocol !== 'file:' &&
2691 global$2.addEventListener('message', listener, false);
2693 } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2694 defer = function (id) {
2695 html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2696 html.removeChild(this);
2700 // Rest old browsers
2702 defer = function (id) {
2703 setTimeout(runner(id), 0);
2713 var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(engineUserAgent);
2715 var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
2716 var macrotask = task$1.set;
2721 var MutationObserver = global$2.MutationObserver || global$2.WebKitMutationObserver;
2722 var document$2 = global$2.document;
2723 var process$2 = global$2.process;
2724 var Promise$1 = global$2.Promise;
2725 // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2726 var queueMicrotaskDescriptor = getOwnPropertyDescriptor$3(global$2, 'queueMicrotask');
2727 var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2729 var flush, head, last, notify$1, toggle, node, promise, then;
2731 // modern engines have queueMicrotask method
2732 if (!queueMicrotask) {
2733 flush = function () {
2735 if (engineIsNode && (parent = process$2.domain)) parent.exit();
2742 if (head) notify$1();
2743 else last = undefined;
2747 if (parent) parent.enter();
2750 // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2751 // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898
2752 if (!engineIsIos && !engineIsNode && !engineIsWebosWebkit && MutationObserver && document$2) {
2754 node = document$2.createTextNode('');
2755 new MutationObserver(flush).observe(node, { characterData: true });
2756 notify$1 = function () {
2757 node.data = toggle = !toggle;
2759 // environments with maybe non-completely correct, but existent Promise
2760 } else if (Promise$1 && Promise$1.resolve) {
2761 // Promise.resolve without an argument throws an error in LG WebOS 2
2762 promise = Promise$1.resolve(undefined);
2763 // workaround of WebKit ~ iOS Safari 10.1 bug
2764 promise.constructor = Promise$1;
2765 then = promise.then;
2766 notify$1 = function () {
2767 then.call(promise, flush);
2769 // Node.js without promises
2770 } else if (engineIsNode) {
2771 notify$1 = function () {
2772 process$2.nextTick(flush);
2774 // for other environments - macrotask based on:
2777 // - window.postMessag
2778 // - onreadystatechange
2781 notify$1 = function () {
2782 // strange IE + webpack dev server bug - use .call(global)
2783 macrotask.call(global$2, flush);
2788 var microtask = queueMicrotask || function (fn) {
2789 var task = { fn: fn, next: undefined };
2790 if (last) last.next = task;
2797 var PromiseCapability = function (C) {
2798 var resolve, reject;
2799 this.promise = new C(function ($$resolve, $$reject) {
2800 if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2801 resolve = $$resolve;
2804 this.resolve = aFunction(resolve);
2805 this.reject = aFunction(reject);
2808 // `NewPromiseCapability` abstract operation
2809 // https://tc39.es/ecma262/#sec-newpromisecapability
2810 var f = function (C) {
2811 return new PromiseCapability(C);
2814 var newPromiseCapability$1 = {
2818 var promiseResolve = function (C, x) {
2820 if (isObject$4(x) && x.constructor === C) return x;
2821 var promiseCapability = newPromiseCapability$1.f(C);
2822 var resolve = promiseCapability.resolve;
2824 return promiseCapability.promise;
2827 var hostReportErrors = function (a, b) {
2828 var console = global$2.console;
2829 if (console && console.error) {
2830 arguments.length === 1 ? console.error(a) : console.error(a, b);
2834 var perform = function (exec) {
2836 return { error: false, value: exec() };
2838 return { error: true, value: error };
2842 var engineIsBrowser = typeof window == 'object';
2844 var task = task$1.set;
2857 var SPECIES$2 = wellKnownSymbol('species');
2858 var PROMISE = 'Promise';
2859 var getInternalState$1 = internalState.get;
2860 var setInternalState$3 = internalState.set;
2861 var getInternalPromiseState = internalState.getterFor(PROMISE);
2862 var NativePromisePrototype = nativePromiseConstructor && nativePromiseConstructor.prototype;
2863 var PromiseConstructor = nativePromiseConstructor;
2864 var PromiseConstructorPrototype = NativePromisePrototype;
2865 var TypeError$1 = global$2.TypeError;
2866 var document$1 = global$2.document;
2867 var process$1 = global$2.process;
2868 var newPromiseCapability = newPromiseCapability$1.f;
2869 var newGenericPromiseCapability = newPromiseCapability;
2870 var DISPATCH_EVENT = !!(document$1 && document$1.createEvent && global$2.dispatchEvent);
2871 var NATIVE_REJECTION_EVENT = typeof PromiseRejectionEvent == 'function';
2872 var UNHANDLED_REJECTION = 'unhandledrejection';
2873 var REJECTION_HANDLED = 'rejectionhandled';
2879 var SUBCLASSING = false;
2880 var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2882 var FORCED$f = isForced_1(PROMISE, function () {
2883 var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor);
2884 // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2885 // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2886 // We can't detect it synchronously, so just check versions
2887 if (!GLOBAL_CORE_JS_PROMISE && engineV8Version === 66) return true;
2888 // We can't use @@species feature detection in V8 since it causes
2889 // deoptimization and performance degradation
2890 // https://github.com/zloirock/core-js/issues/679
2891 if (engineV8Version >= 51 && /native code/.test(PromiseConstructor)) return false;
2892 // Detect correctness of subclassing with @@species support
2893 var promise = new PromiseConstructor(function (resolve) { resolve(1); });
2894 var FakePromise = function (exec) {
2895 exec(function () { /* empty */ }, function () { /* empty */ });
2897 var constructor = promise.constructor = {};
2898 constructor[SPECIES$2] = FakePromise;
2899 SUBCLASSING = promise.then(function () { /* empty */ }) instanceof FakePromise;
2900 if (!SUBCLASSING) return true;
2901 // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2902 return !GLOBAL_CORE_JS_PROMISE && engineIsBrowser && !NATIVE_REJECTION_EVENT;
2905 var INCORRECT_ITERATION$1 = FORCED$f || !checkCorrectnessOfIteration(function (iterable) {
2906 PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2910 var isThenable = function (it) {
2912 return isObject$4(it) && typeof (then = it.then) == 'function' ? then : false;
2915 var notify = function (state, isReject) {
2916 if (state.notified) return;
2917 state.notified = true;
2918 var chain = state.reactions;
2919 microtask(function () {
2920 var value = state.value;
2921 var ok = state.state == FULFILLED;
2923 // variable length - can't use forEach
2924 while (chain.length > index) {
2925 var reaction = chain[index++];
2926 var handler = ok ? reaction.ok : reaction.fail;
2927 var resolve = reaction.resolve;
2928 var reject = reaction.reject;
2929 var domain = reaction.domain;
2930 var result, then, exited;
2934 if (state.rejection === UNHANDLED) onHandleUnhandled(state);
2935 state.rejection = HANDLED;
2937 if (handler === true) result = value;
2939 if (domain) domain.enter();
2940 result = handler(value); // can throw
2946 if (result === reaction.promise) {
2947 reject(TypeError$1('Promise-chain cycle'));
2948 } else if (then = isThenable(result)) {
2949 then.call(result, resolve, reject);
2950 } else resolve(result);
2951 } else reject(value);
2953 if (domain && !exited) domain.exit();
2957 state.reactions = [];
2958 state.notified = false;
2959 if (isReject && !state.rejection) onUnhandled(state);
2963 var dispatchEvent$1 = function (name, promise, reason) {
2965 if (DISPATCH_EVENT) {
2966 event = document$1.createEvent('Event');
2967 event.promise = promise;
2968 event.reason = reason;
2969 event.initEvent(name, false, true);
2970 global$2.dispatchEvent(event);
2971 } else event = { promise: promise, reason: reason };
2972 if (!NATIVE_REJECTION_EVENT && (handler = global$2['on' + name])) handler(event);
2973 else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2976 var onUnhandled = function (state) {
2977 task.call(global$2, function () {
2978 var promise = state.facade;
2979 var value = state.value;
2980 var IS_UNHANDLED = isUnhandled(state);
2983 result = perform(function () {
2985 process$1.emit('unhandledRejection', value, promise);
2986 } else dispatchEvent$1(UNHANDLED_REJECTION, promise, value);
2988 // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2989 state.rejection = engineIsNode || isUnhandled(state) ? UNHANDLED : HANDLED;
2990 if (result.error) throw result.value;
2995 var isUnhandled = function (state) {
2996 return state.rejection !== HANDLED && !state.parent;
2999 var onHandleUnhandled = function (state) {
3000 task.call(global$2, function () {
3001 var promise = state.facade;
3003 process$1.emit('rejectionHandled', promise);
3004 } else dispatchEvent$1(REJECTION_HANDLED, promise, state.value);
3008 var bind$2 = function (fn, state, unwrap) {
3009 return function (value) {
3010 fn(state, value, unwrap);
3014 var internalReject = function (state, value, unwrap) {
3015 if (state.done) return;
3017 if (unwrap) state = unwrap;
3018 state.value = value;
3019 state.state = REJECTED;
3020 notify(state, true);
3023 var internalResolve = function (state, value, unwrap) {
3024 if (state.done) return;
3026 if (unwrap) state = unwrap;
3028 if (state.facade === value) throw TypeError$1("Promise can't be resolved itself");
3029 var then = isThenable(value);
3031 microtask(function () {
3032 var wrapper = { done: false };
3035 bind$2(internalResolve, wrapper, state),
3036 bind$2(internalReject, wrapper, state)
3039 internalReject(wrapper, error, state);
3043 state.value = value;
3044 state.state = FULFILLED;
3045 notify(state, false);
3048 internalReject({ done: false }, error, state);
3052 // constructor polyfill
3054 // 25.4.3.1 Promise(executor)
3055 PromiseConstructor = function Promise(executor) {
3056 anInstance(this, PromiseConstructor, PROMISE);
3057 aFunction(executor);
3058 Internal.call(this);
3059 var state = getInternalState$1(this);
3061 executor(bind$2(internalResolve, state), bind$2(internalReject, state));
3063 internalReject(state, error);
3066 PromiseConstructorPrototype = PromiseConstructor.prototype;
3067 // eslint-disable-next-line no-unused-vars -- required for `.length`
3068 Internal = function Promise(executor) {
3069 setInternalState$3(this, {
3080 Internal.prototype = redefineAll(PromiseConstructorPrototype, {
3081 // `Promise.prototype.then` method
3082 // https://tc39.es/ecma262/#sec-promise.prototype.then
3083 then: function then(onFulfilled, onRejected) {
3084 var state = getInternalPromiseState(this);
3085 var reaction = newPromiseCapability(speciesConstructor(this, PromiseConstructor));
3086 reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
3087 reaction.fail = typeof onRejected == 'function' && onRejected;
3088 reaction.domain = engineIsNode ? process$1.domain : undefined;
3089 state.parent = true;
3090 state.reactions.push(reaction);
3091 if (state.state != PENDING) notify(state, false);
3092 return reaction.promise;
3094 // `Promise.prototype.catch` method
3095 // https://tc39.es/ecma262/#sec-promise.prototype.catch
3096 'catch': function (onRejected) {
3097 return this.then(undefined, onRejected);
3100 OwnPromiseCapability = function () {
3101 var promise = new Internal();
3102 var state = getInternalState$1(promise);
3103 this.promise = promise;
3104 this.resolve = bind$2(internalResolve, state);
3105 this.reject = bind$2(internalReject, state);
3107 newPromiseCapability$1.f = newPromiseCapability = function (C) {
3108 return C === PromiseConstructor || C === PromiseWrapper
3109 ? new OwnPromiseCapability(C)
3110 : newGenericPromiseCapability(C);
3113 if (typeof nativePromiseConstructor == 'function' && NativePromisePrototype !== Object.prototype) {
3114 nativeThen = NativePromisePrototype.then;
3117 // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs
3118 redefine(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) {
3120 return new PromiseConstructor(function (resolve, reject) {
3121 nativeThen.call(that, resolve, reject);
3122 }).then(onFulfilled, onRejected);
3123 // https://github.com/zloirock/core-js/issues/640
3124 }, { unsafe: true });
3126 // makes sure that native promise-based APIs `Promise#catch` properly works with patched `Promise#then`
3127 redefine(NativePromisePrototype, 'catch', PromiseConstructorPrototype['catch'], { unsafe: true });
3130 // make `.constructor === Promise` work for native promise-based APIs
3132 delete NativePromisePrototype.constructor;
3133 } catch (error) { /* empty */ }
3135 // make `instanceof Promise` work for native promise-based APIs
3136 if (objectSetPrototypeOf) {
3137 objectSetPrototypeOf(NativePromisePrototype, PromiseConstructorPrototype);
3142 _export({ global: true, wrap: true, forced: FORCED$f }, {
3143 Promise: PromiseConstructor
3146 setToStringTag(PromiseConstructor, PROMISE, false);
3147 setSpecies(PROMISE);
3149 PromiseWrapper = getBuiltIn(PROMISE);
3152 _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3153 // `Promise.reject` method
3154 // https://tc39.es/ecma262/#sec-promise.reject
3155 reject: function reject(r) {
3156 var capability = newPromiseCapability(this);
3157 capability.reject.call(undefined, r);
3158 return capability.promise;
3162 _export({ target: PROMISE, stat: true, forced: FORCED$f }, {
3163 // `Promise.resolve` method
3164 // https://tc39.es/ecma262/#sec-promise.resolve
3165 resolve: function resolve(x) {
3166 return promiseResolve(this, x);
3170 _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION$1 }, {
3171 // `Promise.all` method
3172 // https://tc39.es/ecma262/#sec-promise.all
3173 all: function all(iterable) {
3175 var capability = newPromiseCapability(C);
3176 var resolve = capability.resolve;
3177 var reject = capability.reject;
3178 var result = perform(function () {
3179 var $promiseResolve = aFunction(C.resolve);
3183 iterate(iterable, function (promise) {
3184 var index = counter++;
3185 var alreadyCalled = false;
3186 values.push(undefined);
3188 $promiseResolve.call(C, promise).then(function (value) {
3189 if (alreadyCalled) return;
3190 alreadyCalled = true;
3191 values[index] = value;
3192 --remaining || resolve(values);
3195 --remaining || resolve(values);
3197 if (result.error) reject(result.value);
3198 return capability.promise;
3200 // `Promise.race` method
3201 // https://tc39.es/ecma262/#sec-promise.race
3202 race: function race(iterable) {
3204 var capability = newPromiseCapability(C);
3205 var reject = capability.reject;
3206 var result = perform(function () {
3207 var $promiseResolve = aFunction(C.resolve);
3208 iterate(iterable, function (promise) {
3209 $promiseResolve.call(C, promise).then(capability.resolve, reject);
3212 if (result.error) reject(result.value);
3213 return capability.promise;
3217 /* eslint-disable no-new -- required for testing */
3219 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3221 var ArrayBuffer$1 = global$2.ArrayBuffer;
3222 var Int8Array$2 = global$2.Int8Array;
3224 var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS || !fails(function () {
3226 }) || !fails(function () {
3227 new Int8Array$2(-1);
3228 }) || !checkCorrectnessOfIteration(function (iterable) {
3230 new Int8Array$2(null);
3231 new Int8Array$2(1.5);
3232 new Int8Array$2(iterable);
3233 }, true) || fails(function () {
3234 // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3235 return new Int8Array$2(new ArrayBuffer$1(2), 1, undefined).length !== 1;
3238 var toPositiveInteger = function (it) {
3239 var result = toInteger(it);
3240 if (result < 0) throw RangeError("The argument can't be less than 0");
3244 var toOffset = function (it, BYTES) {
3245 var offset = toPositiveInteger(it);
3246 if (offset % BYTES) throw RangeError('Wrong offset');
3250 var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
3252 var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3253 var O = toObject(source);
3254 var argumentsLength = arguments.length;
3255 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3256 var mapping = mapfn !== undefined;
3257 var iteratorMethod = getIteratorMethod(O);
3258 var i, length, result, step, iterator, next;
3259 if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3260 iterator = iteratorMethod.call(O);
3261 next = iterator.next;
3263 while (!(step = next.call(iterator)).done) {
3267 if (mapping && argumentsLength > 2) {
3268 mapfn = functionBindContext(mapfn, arguments[2], 2);
3270 length = toLength(O.length);
3271 result = new (aTypedArrayConstructor$3(this))(length);
3272 for (i = 0; length > i; i++) {
3273 result[i] = mapping ? mapfn(O[i], i) : O[i];
3278 // makes subclassing work correct for wrapped built-ins
3279 var inheritIfRequired = function ($this, dummy, Wrapper) {
3280 var NewTarget, NewTargetPrototype;
3282 // it can work only with native `setPrototypeOf`
3283 objectSetPrototypeOf &&
3284 // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3285 typeof (NewTarget = dummy.constructor) == 'function' &&
3286 NewTarget !== Wrapper &&
3287 isObject$4(NewTargetPrototype = NewTarget.prototype) &&
3288 NewTargetPrototype !== Wrapper.prototype
3289 ) objectSetPrototypeOf($this, NewTargetPrototype);
3293 var typedArrayConstructor = createCommonjsModule(function (module) {
3312 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3314 var forEach = arrayIteration.forEach;
3321 var getInternalState = internalState.get;
3322 var setInternalState = internalState.set;
3323 var nativeDefineProperty = objectDefineProperty.f;
3324 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3325 var round = Math.round;
3326 var RangeError = global$2.RangeError;
3327 var ArrayBuffer = arrayBuffer.ArrayBuffer;
3328 var DataView = arrayBuffer.DataView;
3329 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3330 var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3331 var TypedArray = arrayBufferViewCore.TypedArray;
3332 var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3333 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3334 var isTypedArray = arrayBufferViewCore.isTypedArray;
3335 var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3336 var WRONG_LENGTH = 'Wrong length';
3338 var fromList = function (C, list) {
3340 var length = list.length;
3341 var result = new (aTypedArrayConstructor(C))(length);
3342 while (length > index) result[index] = list[index++];
3346 var addGetter = function (it, key) {
3347 nativeDefineProperty(it, key, { get: function () {
3348 return getInternalState(this)[key];
3352 var isArrayBuffer = function (it) {
3354 return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3357 var isTypedArrayIndex = function (target, key) {
3358 return isTypedArray(target)
3359 && typeof key != 'symbol'
3361 && String(+key) == String(key);
3364 var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3365 return isTypedArrayIndex(target, key = toPrimitive(key, true))
3366 ? createPropertyDescriptor(2, target[key])
3367 : nativeGetOwnPropertyDescriptor(target, key);
3370 var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3371 if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3372 && isObject$4(descriptor)
3373 && has$1(descriptor, 'value')
3374 && !has$1(descriptor, 'get')
3375 && !has$1(descriptor, 'set')
3376 // TODO: add validation descriptor w/o calling accessors
3377 && !descriptor.configurable
3378 && (!has$1(descriptor, 'writable') || descriptor.writable)
3379 && (!has$1(descriptor, 'enumerable') || descriptor.enumerable)
3381 target[key] = descriptor.value;
3383 } return nativeDefineProperty(target, key, descriptor);
3387 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3388 objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3389 objectDefineProperty.f = wrappedDefineProperty;
3390 addGetter(TypedArrayPrototype, 'buffer');
3391 addGetter(TypedArrayPrototype, 'byteOffset');
3392 addGetter(TypedArrayPrototype, 'byteLength');
3393 addGetter(TypedArrayPrototype, 'length');
3396 _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3397 getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3398 defineProperty: wrappedDefineProperty
3401 module.exports = function (TYPE, wrapper, CLAMPED) {
3402 var BYTES = TYPE.match(/\d+$/)[0] / 8;
3403 var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3404 var GETTER = 'get' + TYPE;
3405 var SETTER = 'set' + TYPE;
3406 var NativeTypedArrayConstructor = global$2[CONSTRUCTOR_NAME];
3407 var TypedArrayConstructor = NativeTypedArrayConstructor;
3408 var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3411 var getter = function (that, index) {
3412 var data = getInternalState(that);
3413 return data.view[GETTER](index * BYTES + data.byteOffset, true);
3416 var setter = function (that, index, value) {
3417 var data = getInternalState(that);
3418 if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3419 data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3422 var addElement = function (that, index) {
3423 nativeDefineProperty(that, index, {
3425 return getter(this, index);
3427 set: function (value) {
3428 return setter(this, index, value);
3434 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3435 TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3436 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3439 var buffer, byteLength, length;
3440 if (!isObject$4(data)) {
3441 length = toIndex(data);
3442 byteLength = length * BYTES;
3443 buffer = new ArrayBuffer(byteLength);
3444 } else if (isArrayBuffer(data)) {
3446 byteOffset = toOffset(offset, BYTES);
3447 var $len = data.byteLength;
3448 if ($length === undefined) {
3449 if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3450 byteLength = $len - byteOffset;
3451 if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3453 byteLength = toLength($length) * BYTES;
3454 if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3456 length = byteLength / BYTES;
3457 } else if (isTypedArray(data)) {
3458 return fromList(TypedArrayConstructor, data);
3460 return typedArrayFrom.call(TypedArrayConstructor, data);
3462 setInternalState(that, {
3464 byteOffset: byteOffset,
3465 byteLength: byteLength,
3467 view: new DataView(buffer)
3469 while (index < length) addElement(that, index++);
3472 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3473 TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3474 } else if (typedArrayConstructorsRequireWrappers) {
3475 TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3476 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3477 return inheritIfRequired(function () {
3478 if (!isObject$4(data)) return new NativeTypedArrayConstructor(toIndex(data));
3479 if (isArrayBuffer(data)) return $length !== undefined
3480 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3481 : typedArrayOffset !== undefined
3482 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3483 : new NativeTypedArrayConstructor(data);
3484 if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3485 return typedArrayFrom.call(TypedArrayConstructor, data);
3486 }(), dummy, TypedArrayConstructor);
3489 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3490 forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3491 if (!(key in TypedArrayConstructor)) {
3492 createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3495 TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3498 if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3499 createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3502 if (TYPED_ARRAY_TAG) {
3503 createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3506 exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3509 global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3512 if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3513 createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3516 if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3517 createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3520 setSpecies(CONSTRUCTOR_NAME);
3522 } else module.exports = function () { /* empty */ };
3525 // `Uint8Array` constructor
3526 // https://tc39.es/ecma262/#sec-typedarray-objects
3527 typedArrayConstructor('Uint8', function (init) {
3528 return function Uint8Array(data, byteOffset, length) {
3529 return init(this, data, byteOffset, length);
3533 var min$7 = Math.min;
3535 // `Array.prototype.copyWithin` method implementation
3536 // https://tc39.es/ecma262/#sec-array.prototype.copywithin
3537 // eslint-disable-next-line es/no-array-prototype-copywithin -- safe
3538 var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3539 var O = toObject(this);
3540 var len = toLength(O.length);
3541 var to = toAbsoluteIndex(target, len);
3542 var from = toAbsoluteIndex(start, len);
3543 var end = arguments.length > 2 ? arguments[2] : undefined;
3544 var count = min$7((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3546 if (from < to && to < from + count) {
3551 while (count-- > 0) {
3552 if (from in O) O[to] = O[from];
3559 var aTypedArray$l = arrayBufferViewCore.aTypedArray;
3560 var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
3562 // `%TypedArray%.prototype.copyWithin` method
3563 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
3564 exportTypedArrayMethod$m('copyWithin', function copyWithin(target, start /* , end */) {
3565 return arrayCopyWithin.call(aTypedArray$l(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3568 var $every$1 = arrayIteration.every;
3570 var aTypedArray$k = arrayBufferViewCore.aTypedArray;
3571 var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
3573 // `%TypedArray%.prototype.every` method
3574 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.every
3575 exportTypedArrayMethod$l('every', function every(callbackfn /* , thisArg */) {
3576 return $every$1(aTypedArray$k(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3579 var aTypedArray$j = arrayBufferViewCore.aTypedArray;
3580 var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
3582 // `%TypedArray%.prototype.fill` method
3583 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill
3584 // eslint-disable-next-line no-unused-vars -- required for `.length`
3585 exportTypedArrayMethod$k('fill', function fill(value /* , start, end */) {
3586 return arrayFill.apply(aTypedArray$j(this), arguments);
3589 var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3592 var typedArrayFromSpeciesAndList = function (instance, list) {
3593 var C = speciesConstructor(instance, instance.constructor);
3595 var length = list.length;
3596 var result = new (aTypedArrayConstructor$2(C))(length);
3597 while (length > index) result[index] = list[index++];
3601 var $filter$1 = arrayIteration.filter;
3604 var aTypedArray$i = arrayBufferViewCore.aTypedArray;
3605 var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
3607 // `%TypedArray%.prototype.filter` method
3608 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter
3609 exportTypedArrayMethod$j('filter', function filter(callbackfn /* , thisArg */) {
3610 var list = $filter$1(aTypedArray$i(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3611 return typedArrayFromSpeciesAndList(this, list);
3614 var $find$1 = arrayIteration.find;
3616 var aTypedArray$h = arrayBufferViewCore.aTypedArray;
3617 var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
3619 // `%TypedArray%.prototype.find` method
3620 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.find
3621 exportTypedArrayMethod$i('find', function find(predicate /* , thisArg */) {
3622 return $find$1(aTypedArray$h(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3625 var $findIndex$1 = arrayIteration.findIndex;
3627 var aTypedArray$g = arrayBufferViewCore.aTypedArray;
3628 var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
3630 // `%TypedArray%.prototype.findIndex` method
3631 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex
3632 exportTypedArrayMethod$h('findIndex', function findIndex(predicate /* , thisArg */) {
3633 return $findIndex$1(aTypedArray$g(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3636 var $forEach = arrayIteration.forEach;
3638 var aTypedArray$f = arrayBufferViewCore.aTypedArray;
3639 var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
3641 // `%TypedArray%.prototype.forEach` method
3642 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
3643 exportTypedArrayMethod$g('forEach', function forEach(callbackfn /* , thisArg */) {
3644 $forEach(aTypedArray$f(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3647 var $includes$1 = arrayIncludes.includes;
3649 var aTypedArray$e = arrayBufferViewCore.aTypedArray;
3650 var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
3652 // `%TypedArray%.prototype.includes` method
3653 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes
3654 exportTypedArrayMethod$f('includes', function includes(searchElement /* , fromIndex */) {
3655 return $includes$1(aTypedArray$e(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3658 var $indexOf = arrayIncludes.indexOf;
3660 var aTypedArray$d = arrayBufferViewCore.aTypedArray;
3661 var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
3663 // `%TypedArray%.prototype.indexOf` method
3664 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof
3665 exportTypedArrayMethod$e('indexOf', function indexOf(searchElement /* , fromIndex */) {
3666 return $indexOf(aTypedArray$d(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
3669 var ITERATOR$2 = wellKnownSymbol('iterator');
3670 var Uint8Array$2 = global$2.Uint8Array;
3671 var arrayValues = es_array_iterator.values;
3672 var arrayKeys = es_array_iterator.keys;
3673 var arrayEntries = es_array_iterator.entries;
3674 var aTypedArray$c = arrayBufferViewCore.aTypedArray;
3675 var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
3676 var nativeTypedArrayIterator = Uint8Array$2 && Uint8Array$2.prototype[ITERATOR$2];
3678 var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
3679 && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
3681 var typedArrayValues = function values() {
3682 return arrayValues.call(aTypedArray$c(this));
3685 // `%TypedArray%.prototype.entries` method
3686 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries
3687 exportTypedArrayMethod$d('entries', function entries() {
3688 return arrayEntries.call(aTypedArray$c(this));
3690 // `%TypedArray%.prototype.keys` method
3691 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys
3692 exportTypedArrayMethod$d('keys', function keys() {
3693 return arrayKeys.call(aTypedArray$c(this));
3695 // `%TypedArray%.prototype.values` method
3696 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
3697 exportTypedArrayMethod$d('values', typedArrayValues, !CORRECT_ITER_NAME);
3698 // `%TypedArray%.prototype[@@iterator]` method
3699 // https://tc39.es/ecma262/#sec-%typedarray%.prototype-@@iterator
3700 exportTypedArrayMethod$d(ITERATOR$2, typedArrayValues, !CORRECT_ITER_NAME);
3702 var aTypedArray$b = arrayBufferViewCore.aTypedArray;
3703 var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
3704 var $join = [].join;
3706 // `%TypedArray%.prototype.join` method
3707 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.join
3708 // eslint-disable-next-line no-unused-vars -- required for `.length`
3709 exportTypedArrayMethod$c('join', function join(separator) {
3710 return $join.apply(aTypedArray$b(this), arguments);
3713 /* eslint-disable es/no-array-prototype-lastindexof -- safe */
3719 var min$6 = Math.min;
3720 var $lastIndexOf = [].lastIndexOf;
3721 var NEGATIVE_ZERO = !!$lastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
3722 var STRICT_METHOD$5 = arrayMethodIsStrict('lastIndexOf');
3723 var FORCED$e = NEGATIVE_ZERO || !STRICT_METHOD$5;
3725 // `Array.prototype.lastIndexOf` method implementation
3726 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
3727 var arrayLastIndexOf = FORCED$e ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
3729 if (NEGATIVE_ZERO) return $lastIndexOf.apply(this, arguments) || 0;
3730 var O = toIndexedObject(this);
3731 var length = toLength(O.length);
3732 var index = length - 1;
3733 if (arguments.length > 1) index = min$6(index, toInteger(arguments[1]));
3734 if (index < 0) index = length + index;
3735 for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
3739 var aTypedArray$a = arrayBufferViewCore.aTypedArray;
3740 var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
3742 // `%TypedArray%.prototype.lastIndexOf` method
3743 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof
3744 // eslint-disable-next-line no-unused-vars -- required for `.length`
3745 exportTypedArrayMethod$b('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
3746 return arrayLastIndexOf.apply(aTypedArray$a(this), arguments);
3749 var $map = arrayIteration.map;
3752 var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
3753 var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3754 var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
3756 // `%TypedArray%.prototype.map` method
3757 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
3758 exportTypedArrayMethod$a('map', function map(mapfn /* , thisArg */) {
3759 return $map(aTypedArray$9(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
3760 return new (aTypedArrayConstructor$1(speciesConstructor(O, O.constructor)))(length);
3764 // `Array.prototype.{ reduce, reduceRight }` methods implementation
3765 var createMethod$3 = function (IS_RIGHT) {
3766 return function (that, callbackfn, argumentsLength, memo) {
3767 aFunction(callbackfn);
3768 var O = toObject(that);
3769 var self = indexedObject(O);
3770 var length = toLength(O.length);
3771 var index = IS_RIGHT ? length - 1 : 0;
3772 var i = IS_RIGHT ? -1 : 1;
3773 if (argumentsLength < 2) while (true) {
3774 if (index in self) {
3780 if (IS_RIGHT ? index < 0 : length <= index) {
3781 throw TypeError('Reduce of empty array with no initial value');
3784 for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
3785 memo = callbackfn(memo, self[index], index, O);
3792 // `Array.prototype.reduce` method
3793 // https://tc39.es/ecma262/#sec-array.prototype.reduce
3794 left: createMethod$3(false),
3795 // `Array.prototype.reduceRight` method
3796 // https://tc39.es/ecma262/#sec-array.prototype.reduceright
3797 right: createMethod$3(true)
3800 var $reduce$1 = arrayReduce.left;
3802 var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
3803 var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
3805 // `%TypedArray%.prototype.reduce` method
3806 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce
3807 exportTypedArrayMethod$9('reduce', function reduce(callbackfn /* , initialValue */) {
3808 return $reduce$1(aTypedArray$8(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3811 var $reduceRight = arrayReduce.right;
3813 var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
3814 var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
3816 // `%TypedArray%.prototype.reduceRicht` method
3817 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright
3818 exportTypedArrayMethod$8('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
3819 return $reduceRight(aTypedArray$7(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
3822 var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
3823 var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
3824 var floor$5 = Math.floor;
3826 // `%TypedArray%.prototype.reverse` method
3827 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse
3828 exportTypedArrayMethod$7('reverse', function reverse() {
3830 var length = aTypedArray$6(that).length;
3831 var middle = floor$5(length / 2);
3834 while (index < middle) {
3835 value = that[index];
3836 that[index++] = that[--length];
3837 that[length] = value;
3841 var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
3842 var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
3844 var FORCED$d = fails(function () {
3845 // eslint-disable-next-line es/no-typed-arrays -- required for testing
3846 new Int8Array(1).set({});
3849 // `%TypedArray%.prototype.set` method
3850 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
3851 exportTypedArrayMethod$6('set', function set(arrayLike /* , offset */) {
3852 aTypedArray$5(this);
3853 var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
3854 var length = this.length;
3855 var src = toObject(arrayLike);
3856 var len = toLength(src.length);
3858 if (len + offset > length) throw RangeError('Wrong length');
3859 while (index < len) this[offset + index] = src[index++];
3862 var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3863 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3864 var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
3865 var $slice$1 = [].slice;
3867 var FORCED$c = fails(function () {
3868 // eslint-disable-next-line es/no-typed-arrays -- required for testing
3869 new Int8Array(1).slice();
3872 // `%TypedArray%.prototype.slice` method
3873 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice
3874 exportTypedArrayMethod$5('slice', function slice(start, end) {
3875 var list = $slice$1.call(aTypedArray$4(this), start, end);
3876 var C = speciesConstructor(this, this.constructor);
3878 var length = list.length;
3879 var result = new (aTypedArrayConstructor(C))(length);
3880 while (length > index) result[index] = list[index++];
3884 var $some$1 = arrayIteration.some;
3886 var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3887 var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3889 // `%TypedArray%.prototype.some` method
3890 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
3891 exportTypedArrayMethod$4('some', function some(callbackfn /* , thisArg */) {
3892 return $some$1(aTypedArray$3(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3895 // TODO: use something more complex like timsort?
3896 var floor$4 = Math.floor;
3898 var mergeSort = function (array, comparefn) {
3899 var length = array.length;
3900 var middle = floor$4(length / 2);
3901 return length < 8 ? insertionSort(array, comparefn) : merge$5(
3902 mergeSort(array.slice(0, middle), comparefn),
3903 mergeSort(array.slice(middle), comparefn),
3908 var insertionSort = function (array, comparefn) {
3909 var length = array.length;
3913 while (i < length) {
3916 while (j && comparefn(array[j - 1], element) > 0) {
3917 array[j] = array[--j];
3919 if (j !== i++) array[j] = element;
3923 var merge$5 = function (left, right, comparefn) {
3924 var llength = left.length;
3925 var rlength = right.length;
3930 while (lindex < llength || rindex < rlength) {
3931 if (lindex < llength && rindex < rlength) {
3932 result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]);
3934 result.push(lindex < llength ? left[lindex++] : right[rindex++]);
3939 var arraySort = mergeSort;
3941 var firefox = engineUserAgent.match(/firefox\/(\d+)/i);
3943 var engineFfVersion = !!firefox && +firefox[1];
3945 var engineIsIeOrEdge = /MSIE|Trident/.test(engineUserAgent);
3947 var webkit = engineUserAgent.match(/AppleWebKit\/(\d+)\./);
3949 var engineWebkitVersion = !!webkit && +webkit[1];
3951 var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3952 var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3953 var Uint16Array = global$2.Uint16Array;
3954 var nativeSort$1 = Uint16Array && Uint16Array.prototype.sort;
3957 var ACCEPT_INCORRECT_ARGUMENTS = !!nativeSort$1 && !fails(function () {
3958 var array = new Uint16Array(2);
3963 var STABLE_SORT$1 = !!nativeSort$1 && !fails(function () {
3964 // feature detection can be too slow, so check engines versions
3965 if (engineV8Version) return engineV8Version < 74;
3966 if (engineFfVersion) return engineFfVersion < 67;
3967 if (engineIsIeOrEdge) return true;
3968 if (engineWebkitVersion) return engineWebkitVersion < 602;
3970 var array = new Uint16Array(516);
3971 var expected = Array(516);
3974 for (index = 0; index < 516; index++) {
3976 array[index] = 515 - index;
3977 expected[index] = index - 2 * mod + 3;
3980 array.sort(function (a, b) {
3981 return (a / 4 | 0) - (b / 4 | 0);
3984 for (index = 0; index < 516; index++) {
3985 if (array[index] !== expected[index]) return true;
3989 var getSortCompare$1 = function (comparefn) {
3990 return function (x, y) {
3991 if (comparefn !== undefined) return +comparefn(x, y) || 0;
3992 // eslint-disable-next-line no-self-compare -- NaN check
3993 if (y !== y) return -1;
3994 // eslint-disable-next-line no-self-compare -- NaN check
3995 if (x !== x) return 1;
3996 if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1;
4001 // `%TypedArray%.prototype.sort` method
4002 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
4003 exportTypedArrayMethod$3('sort', function sort(comparefn) {
4005 if (comparefn !== undefined) aFunction(comparefn);
4006 if (STABLE_SORT$1) return nativeSort$1.call(array, comparefn);
4008 aTypedArray$2(array);
4009 var arrayLength = toLength(array.length);
4010 var items = Array(arrayLength);
4013 for (index = 0; index < arrayLength; index++) {
4014 items[index] = array[index];
4017 items = arraySort(array, getSortCompare$1(comparefn));
4019 for (index = 0; index < arrayLength; index++) {
4020 array[index] = items[index];
4024 }, !STABLE_SORT$1 || ACCEPT_INCORRECT_ARGUMENTS);
4026 var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
4027 var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
4029 // `%TypedArray%.prototype.subarray` method
4030 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray
4031 exportTypedArrayMethod$2('subarray', function subarray(begin, end) {
4032 var O = aTypedArray$1(this);
4033 var length = O.length;
4034 var beginIndex = toAbsoluteIndex(begin, length);
4035 return new (speciesConstructor(O, O.constructor))(
4037 O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
4038 toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
4042 var Int8Array$1 = global$2.Int8Array;
4043 var aTypedArray = arrayBufferViewCore.aTypedArray;
4044 var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
4045 var $toLocaleString = [].toLocaleString;
4046 var $slice = [].slice;
4048 // iOS Safari 6.x fails here
4049 var TO_LOCALE_STRING_BUG = !!Int8Array$1 && fails(function () {
4050 $toLocaleString.call(new Int8Array$1(1));
4053 var FORCED$b = fails(function () {
4054 return [1, 2].toLocaleString() != new Int8Array$1([1, 2]).toLocaleString();
4055 }) || !fails(function () {
4056 Int8Array$1.prototype.toLocaleString.call([1, 2]);
4059 // `%TypedArray%.prototype.toLocaleString` method
4060 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
4061 exportTypedArrayMethod$1('toLocaleString', function toLocaleString() {
4062 return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice.call(aTypedArray(this)) : aTypedArray(this), arguments);
4065 var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod;
4069 var Uint8Array$1 = global$2.Uint8Array;
4070 var Uint8ArrayPrototype = Uint8Array$1 && Uint8Array$1.prototype || {};
4071 var arrayToString = [].toString;
4072 var arrayJoin = [].join;
4074 if (fails(function () { arrayToString.call({}); })) {
4075 arrayToString = function toString() {
4076 return arrayJoin.call(this);
4080 var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
4082 // `%TypedArray%.prototype.toString` method
4083 // https://tc39.es/ecma262/#sec-%typedarray%.prototype.tostring
4084 exportTypedArrayMethod('toString', arrayToString, IS_NOT_ARRAY_METHOD);
4086 var nativeJoin = [].join;
4088 var ES3_STRINGS = indexedObject != Object;
4089 var STRICT_METHOD$4 = arrayMethodIsStrict('join', ',');
4091 // `Array.prototype.join` method
4092 // https://tc39.es/ecma262/#sec-array.prototype.join
4093 _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$4 }, {
4094 join: function join(separator) {
4095 return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
4099 var createProperty = function (object, key, value) {
4100 var propertyKey = toPrimitive(key);
4101 if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
4102 else object[propertyKey] = value;
4105 var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('slice');
4107 var SPECIES$1 = wellKnownSymbol('species');
4108 var nativeSlice = [].slice;
4109 var max$3 = Math.max;
4111 // `Array.prototype.slice` method
4112 // https://tc39.es/ecma262/#sec-array.prototype.slice
4113 // fallback for not array-like ES3 strings and DOM objects
4114 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, {
4115 slice: function slice(start, end) {
4116 var O = toIndexedObject(this);
4117 var length = toLength(O.length);
4118 var k = toAbsoluteIndex(start, length);
4119 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
4120 // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
4121 var Constructor, result, n;
4123 Constructor = O.constructor;
4124 // cross-realm fallback
4125 if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
4126 Constructor = undefined;
4127 } else if (isObject$4(Constructor)) {
4128 Constructor = Constructor[SPECIES$1];
4129 if (Constructor === null) Constructor = undefined;
4131 if (Constructor === Array || Constructor === undefined) {
4132 return nativeSlice.call(O, k, fin);
4135 result = new (Constructor === undefined ? Array : Constructor)(max$3(fin - k, 0));
4136 for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
4142 var ITERATOR$1 = wellKnownSymbol('iterator');
4144 var nativeUrl = !fails(function () {
4145 var url = new URL('b?a=1&b=2&c=3', 'http://a');
4146 var searchParams = url.searchParams;
4148 url.pathname = 'c%20d';
4149 searchParams.forEach(function (value, key) {
4150 searchParams['delete']('b');
4151 result += key + value;
4153 return (isPure && !url.toJSON)
4154 || !searchParams.sort
4155 || url.href !== 'http://a/c%20d?a=1&c=3'
4156 || searchParams.get('c') !== '3'
4157 || String(new URLSearchParams('?a=1')) !== 'a=1'
4158 || !searchParams[ITERATOR$1]
4160 || new URL('https://a@b').username !== 'a'
4161 || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
4162 // not punycoded in Edge
4163 || new URL('http://тест').host !== 'xn--e1aybc'
4164 // not escaped in Chrome 62-
4165 || new URL('http://a#б').hash !== '#%D0%B1'
4166 // fails in Chrome 66-
4167 || result !== 'a1c3'
4169 || new URL('http://x', undefined).host !== 'x';
4172 // eslint-disable-next-line es/no-object-assign -- safe
4173 var $assign = Object.assign;
4174 // eslint-disable-next-line es/no-object-defineproperty -- required for testing
4175 var defineProperty$4 = Object.defineProperty;
4177 // `Object.assign` method
4178 // https://tc39.es/ecma262/#sec-object.assign
4179 var objectAssign = !$assign || fails(function () {
4180 // should have correct order of operations (Edge bug)
4181 if (descriptors && $assign({ b: 1 }, $assign(defineProperty$4({}, 'a', {
4184 defineProperty$4(this, 'b', {
4189 }), { b: 2 })).b !== 1) return true;
4190 // should work with symbols and should have deterministic property order (V8 bug)
4193 // eslint-disable-next-line es/no-symbol -- safe
4194 var symbol = Symbol();
4195 var alphabet = 'abcdefghijklmnopqrst';
4197 alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4198 return $assign({}, A)[symbol] != 7 || objectKeys($assign({}, B)).join('') != alphabet;
4199 }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length`
4200 var T = toObject(target);
4201 var argumentsLength = arguments.length;
4203 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4204 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4205 while (argumentsLength > index) {
4206 var S = indexedObject(arguments[index++]);
4207 var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4208 var length = keys.length;
4211 while (length > j) {
4213 if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4218 // call something on iterator step with safe closing on error
4219 var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
4221 return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
4223 iteratorClose(iterator);
4228 // `Array.from` method implementation
4229 // https://tc39.es/ecma262/#sec-array.from
4230 var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4231 var O = toObject(arrayLike);
4232 var C = typeof this == 'function' ? this : Array;
4233 var argumentsLength = arguments.length;
4234 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4235 var mapping = mapfn !== undefined;
4236 var iteratorMethod = getIteratorMethod(O);
4238 var length, result, step, iterator, next, value;
4239 if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4240 // if the target is not iterable or it's an array with the default iterator - use a simple case
4241 if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4242 iterator = iteratorMethod.call(O);
4243 next = iterator.next;
4245 for (;!(step = next.call(iterator)).done; index++) {
4246 value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4247 createProperty(result, index, value);
4250 length = toLength(O.length);
4251 result = new C(length);
4252 for (;length > index; index++) {
4253 value = mapping ? mapfn(O[index], index) : O[index];
4254 createProperty(result, index, value);
4257 result.length = index;
4261 // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4262 var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4268 var initialBias = 72;
4269 var initialN = 128; // 0x80
4270 var delimiter = '-'; // '\x2D'
4271 var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4272 var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4273 var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4274 var baseMinusTMin = base - tMin;
4275 var floor$3 = Math.floor;
4276 var stringFromCharCode = String.fromCharCode;
4279 * Creates an array containing the numeric code points of each Unicode
4280 * character in the string. While JavaScript uses UCS-2 internally,
4281 * this function will convert a pair of surrogate halves (each of which
4282 * UCS-2 exposes as separate characters) into a single code point,
4285 var ucs2decode = function (string) {
4288 var length = string.length;
4289 while (counter < length) {
4290 var value = string.charCodeAt(counter++);
4291 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4292 // It's a high surrogate, and there is a next character.
4293 var extra = string.charCodeAt(counter++);
4294 if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4295 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4297 // It's an unmatched surrogate; only append this code unit, in case the
4298 // next code unit is the high surrogate of a surrogate pair.
4310 * Converts a digit/integer into a basic code point.
4312 var digitToBasic = function (digit) {
4313 // 0..25 map to ASCII a..z or A..Z
4314 // 26..35 map to ASCII 0..9
4315 return digit + 22 + 75 * (digit < 26);
4319 * Bias adaptation function as per section 3.4 of RFC 3492.
4320 * https://tools.ietf.org/html/rfc3492#section-3.4
4322 var adapt = function (delta, numPoints, firstTime) {
4324 delta = firstTime ? floor$3(delta / damp) : delta >> 1;
4325 delta += floor$3(delta / numPoints);
4326 for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4327 delta = floor$3(delta / baseMinusTMin);
4329 return floor$3(k + (baseMinusTMin + 1) * delta / (delta + skew));
4333 * Converts a string of Unicode symbols (e.g. a domain name label) to a
4334 * Punycode string of ASCII-only symbols.
4336 // eslint-disable-next-line max-statements -- TODO
4337 var encode = function (input) {
4340 // Convert the input in UCS-2 to an array of Unicode code points.
4341 input = ucs2decode(input);
4343 // Cache the length.
4344 var inputLength = input.length;
4346 // Initialize the state.
4349 var bias = initialBias;
4350 var i, currentValue;
4352 // Handle the basic code points.
4353 for (i = 0; i < input.length; i++) {
4354 currentValue = input[i];
4355 if (currentValue < 0x80) {
4356 output.push(stringFromCharCode(currentValue));
4360 var basicLength = output.length; // number of basic code points.
4361 var handledCPCount = basicLength; // number of code points that have been handled;
4363 // Finish the basic string with a delimiter unless it's empty.
4365 output.push(delimiter);
4368 // Main encoding loop:
4369 while (handledCPCount < inputLength) {
4370 // All non-basic code points < n have been handled already. Find the next larger one:
4372 for (i = 0; i < input.length; i++) {
4373 currentValue = input[i];
4374 if (currentValue >= n && currentValue < m) {
4379 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4380 var handledCPCountPlusOne = handledCPCount + 1;
4381 if (m - n > floor$3((maxInt - delta) / handledCPCountPlusOne)) {
4382 throw RangeError(OVERFLOW_ERROR);
4385 delta += (m - n) * handledCPCountPlusOne;
4388 for (i = 0; i < input.length; i++) {
4389 currentValue = input[i];
4390 if (currentValue < n && ++delta > maxInt) {
4391 throw RangeError(OVERFLOW_ERROR);
4393 if (currentValue == n) {
4394 // Represent delta as a generalized variable-length integer.
4396 for (var k = base; /* no condition */; k += base) {
4397 var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4399 var qMinusT = q - t;
4400 var baseMinusT = base - t;
4401 output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4402 q = floor$3(qMinusT / baseMinusT);
4405 output.push(stringFromCharCode(digitToBasic(q)));
4406 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4415 return output.join('');
4418 var stringPunycodeToAscii = function (input) {
4420 var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4422 for (i = 0; i < labels.length; i++) {
4424 encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4426 return encoded.join('.');
4429 var getIterator = function (it) {
4430 var iteratorMethod = getIteratorMethod(it);
4431 if (typeof iteratorMethod != 'function') {
4432 throw TypeError(String(it) + ' is not iterable');
4433 } return anObject(iteratorMethod.call(it));
4436 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4458 var $fetch = getBuiltIn('fetch');
4459 var Headers$1 = getBuiltIn('Headers');
4460 var ITERATOR = wellKnownSymbol('iterator');
4461 var URL_SEARCH_PARAMS = 'URLSearchParams';
4462 var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4463 var setInternalState$2 = internalState.set;
4464 var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4465 var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4468 var sequences = Array(4);
4470 var percentSequence = function (bytes) {
4471 return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4474 var percentDecode = function (sequence) {
4476 return decodeURIComponent(sequence);
4482 var deserialize = function (it) {
4483 var result = it.replace(plus, ' ');
4486 return decodeURIComponent(result);
4489 result = result.replace(percentSequence(bytes--), percentDecode);
4495 var find$1 = /[!'()~]|%20/g;
4506 var replacer = function (match) {
4507 return replace$1[match];
4510 var serialize = function (it) {
4511 return encodeURIComponent(it).replace(find$1, replacer);
4514 var parseSearchParams = function (result, query) {
4516 var attributes = query.split('&');
4518 var attribute, entry;
4519 while (index < attributes.length) {
4520 attribute = attributes[index++];
4521 if (attribute.length) {
4522 entry = attribute.split('=');
4524 key: deserialize(entry.shift()),
4525 value: deserialize(entry.join('='))
4532 var updateSearchParams = function (query) {
4533 this.entries.length = 0;
4534 parseSearchParams(this.entries, query);
4537 var validateArgumentsLength = function (passed, required) {
4538 if (passed < required) throw TypeError('Not enough arguments');
4541 var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4542 setInternalState$2(this, {
4543 type: URL_SEARCH_PARAMS_ITERATOR,
4544 iterator: getIterator(getInternalParamsState(params).entries),
4547 }, 'Iterator', function next() {
4548 var state = getInternalIteratorState(this);
4549 var kind = state.kind;
4550 var step = state.iterator.next();
4551 var entry = step.value;
4553 step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4557 // `URLSearchParams` constructor
4558 // https://url.spec.whatwg.org/#interface-urlsearchparams
4559 var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4560 anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4561 var init = arguments.length > 0 ? arguments[0] : undefined;
4564 var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4566 setInternalState$2(that, {
4567 type: URL_SEARCH_PARAMS,
4569 updateURL: function () { /* empty */ },
4570 updateSearchParams: updateSearchParams
4573 if (init !== undefined) {
4574 if (isObject$4(init)) {
4575 iteratorMethod = getIteratorMethod(init);
4576 if (typeof iteratorMethod === 'function') {
4577 iterator = iteratorMethod.call(init);
4578 next = iterator.next;
4579 while (!(step = next.call(iterator)).done) {
4580 entryIterator = getIterator(anObject(step.value));
4581 entryNext = entryIterator.next;
4583 (first = entryNext.call(entryIterator)).done ||
4584 (second = entryNext.call(entryIterator)).done ||
4585 !entryNext.call(entryIterator).done
4586 ) throw TypeError('Expected sequence with length 2');
4587 entries.push({ key: first.value + '', value: second.value + '' });
4589 } else for (key in init) if (has$1(init, key)) entries.push({ key: key, value: init[key] + '' });
4591 parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4596 var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4598 redefineAll(URLSearchParamsPrototype, {
4599 // `URLSearchParams.prototype.append` method
4600 // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4601 append: function append(name, value) {
4602 validateArgumentsLength(arguments.length, 2);
4603 var state = getInternalParamsState(this);
4604 state.entries.push({ key: name + '', value: value + '' });
4607 // `URLSearchParams.prototype.delete` method
4608 // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4609 'delete': function (name) {
4610 validateArgumentsLength(arguments.length, 1);
4611 var state = getInternalParamsState(this);
4612 var entries = state.entries;
4613 var key = name + '';
4615 while (index < entries.length) {
4616 if (entries[index].key === key) entries.splice(index, 1);
4621 // `URLSearchParams.prototype.get` method
4622 // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4623 get: function get(name) {
4624 validateArgumentsLength(arguments.length, 1);
4625 var entries = getInternalParamsState(this).entries;
4626 var key = name + '';
4628 for (; index < entries.length; index++) {
4629 if (entries[index].key === key) return entries[index].value;
4633 // `URLSearchParams.prototype.getAll` method
4634 // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4635 getAll: function getAll(name) {
4636 validateArgumentsLength(arguments.length, 1);
4637 var entries = getInternalParamsState(this).entries;
4638 var key = name + '';
4641 for (; index < entries.length; index++) {
4642 if (entries[index].key === key) result.push(entries[index].value);
4646 // `URLSearchParams.prototype.has` method
4647 // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4648 has: function has(name) {
4649 validateArgumentsLength(arguments.length, 1);
4650 var entries = getInternalParamsState(this).entries;
4651 var key = name + '';
4653 while (index < entries.length) {
4654 if (entries[index++].key === key) return true;
4658 // `URLSearchParams.prototype.set` method
4659 // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4660 set: function set(name, value) {
4661 validateArgumentsLength(arguments.length, 1);
4662 var state = getInternalParamsState(this);
4663 var entries = state.entries;
4665 var key = name + '';
4666 var val = value + '';
4669 for (; index < entries.length; index++) {
4670 entry = entries[index];
4671 if (entry.key === key) {
4672 if (found) entries.splice(index--, 1);
4679 if (!found) entries.push({ key: key, value: val });
4682 // `URLSearchParams.prototype.sort` method
4683 // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4684 sort: function sort() {
4685 var state = getInternalParamsState(this);
4686 var entries = state.entries;
4687 // Array#sort is not stable in some engines
4688 var slice = entries.slice();
4689 var entry, entriesIndex, sliceIndex;
4691 for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
4692 entry = slice[sliceIndex];
4693 for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
4694 if (entries[entriesIndex].key > entry.key) {
4695 entries.splice(entriesIndex, 0, entry);
4699 if (entriesIndex === sliceIndex) entries.push(entry);
4703 // `URLSearchParams.prototype.forEach` method
4704 forEach: function forEach(callback /* , thisArg */) {
4705 var entries = getInternalParamsState(this).entries;
4706 var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
4709 while (index < entries.length) {
4710 entry = entries[index++];
4711 boundFunction(entry.value, entry.key, this);
4714 // `URLSearchParams.prototype.keys` method
4715 keys: function keys() {
4716 return new URLSearchParamsIterator(this, 'keys');
4718 // `URLSearchParams.prototype.values` method
4719 values: function values() {
4720 return new URLSearchParamsIterator(this, 'values');
4722 // `URLSearchParams.prototype.entries` method
4723 entries: function entries() {
4724 return new URLSearchParamsIterator(this, 'entries');
4726 }, { enumerable: true });
4728 // `URLSearchParams.prototype[@@iterator]` method
4729 redefine(URLSearchParamsPrototype, ITERATOR, URLSearchParamsPrototype.entries);
4731 // `URLSearchParams.prototype.toString` method
4732 // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
4733 redefine(URLSearchParamsPrototype, 'toString', function toString() {
4734 var entries = getInternalParamsState(this).entries;
4738 while (index < entries.length) {
4739 entry = entries[index++];
4740 result.push(serialize(entry.key) + '=' + serialize(entry.value));
4741 } return result.join('&');
4742 }, { enumerable: true });
4744 setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4746 _export({ global: true, forced: !nativeUrl }, {
4747 URLSearchParams: URLSearchParamsConstructor
4750 // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
4751 // https://github.com/zloirock/core-js/issues/674
4752 if (!nativeUrl && typeof $fetch == 'function' && typeof Headers$1 == 'function') {
4753 _export({ global: true, enumerable: true, forced: true }, {
4754 fetch: function fetch(input /* , init */) {
4756 var init, body, headers;
4757 if (arguments.length > 1) {
4758 init = arguments[1];
4759 if (isObject$4(init)) {
4761 if (classof(body) === URL_SEARCH_PARAMS) {
4762 headers = init.headers ? new Headers$1(init.headers) : new Headers$1();
4763 if (!headers.has('content-type')) {
4764 headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
4766 init = objectCreate(init, {
4767 body: createPropertyDescriptor(0, String(body)),
4768 headers: createPropertyDescriptor(0, headers)
4773 } return $fetch.apply(this, args);
4778 var web_urlSearchParams = {
4779 URLSearchParams: URLSearchParamsConstructor,
4780 getState: getInternalParamsState
4783 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4795 var codeAt = stringMultibyte.codeAt;
4801 var NativeURL = global$2.URL;
4802 var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
4803 var getInternalSearchParamsState = web_urlSearchParams.getState;
4804 var setInternalState$1 = internalState.set;
4805 var getInternalURLState = internalState.getterFor('URL');
4806 var floor$2 = Math.floor;
4807 var pow$1 = Math.pow;
4809 var INVALID_AUTHORITY = 'Invalid authority';
4810 var INVALID_SCHEME = 'Invalid scheme';
4811 var INVALID_HOST = 'Invalid host';
4812 var INVALID_PORT = 'Invalid port';
4814 var ALPHA = /[A-Za-z]/;
4815 // eslint-disable-next-line regexp/no-obscure-range -- safe
4816 var ALPHANUMERIC = /[\d+-.A-Za-z]/;
4818 var HEX_START = /^0x/i;
4819 var OCT = /^[0-7]+$/;
4821 var HEX = /^[\dA-Fa-f]+$/;
4822 /* eslint-disable no-control-regex -- safe */
4823 var FORBIDDEN_HOST_CODE_POINT = /[\0\t\n\r #%/:<>?@[\\\]^|]/;
4824 var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\0\t\n\r #/:<>?@[\\\]^|]/;
4825 var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
4826 var TAB_AND_NEW_LINE = /[\t\n\r]/g;
4827 /* eslint-enable no-control-regex -- safe */
4830 var parseHost = function (url, input) {
4831 var result, codePoints, index;
4832 if (input.charAt(0) == '[') {
4833 if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
4834 result = parseIPv6(input.slice(1, -1));
4835 if (!result) return INVALID_HOST;
4838 } else if (!isSpecial(url)) {
4839 if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
4841 codePoints = arrayFrom(input);
4842 for (index = 0; index < codePoints.length; index++) {
4843 result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
4847 input = stringPunycodeToAscii(input);
4848 if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
4849 result = parseIPv4(input);
4850 if (result === null) return INVALID_HOST;
4855 var parseIPv4 = function (input) {
4856 var parts = input.split('.');
4857 var partsLength, numbers, index, part, radix, number, ipv4;
4858 if (parts.length && parts[parts.length - 1] == '') {
4861 partsLength = parts.length;
4862 if (partsLength > 4) return input;
4864 for (index = 0; index < partsLength; index++) {
4865 part = parts[index];
4866 if (part == '') return input;
4868 if (part.length > 1 && part.charAt(0) == '0') {
4869 radix = HEX_START.test(part) ? 16 : 8;
4870 part = part.slice(radix == 8 ? 1 : 2);
4875 if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
4876 number = parseInt(part, radix);
4878 numbers.push(number);
4880 for (index = 0; index < partsLength; index++) {
4881 number = numbers[index];
4882 if (index == partsLength - 1) {
4883 if (number >= pow$1(256, 5 - partsLength)) return null;
4884 } else if (number > 255) return null;
4886 ipv4 = numbers.pop();
4887 for (index = 0; index < numbers.length; index++) {
4888 ipv4 += numbers[index] * pow$1(256, 3 - index);
4893 // eslint-disable-next-line max-statements -- TODO
4894 var parseIPv6 = function (input) {
4895 var address = [0, 0, 0, 0, 0, 0, 0, 0];
4897 var compress = null;
4899 var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
4901 var char = function () {
4902 return input.charAt(pointer);
4905 if (char() == ':') {
4906 if (input.charAt(1) != ':') return;
4909 compress = pieceIndex;
4912 if (pieceIndex == 8) return;
4913 if (char() == ':') {
4914 if (compress !== null) return;
4917 compress = pieceIndex;
4921 while (length < 4 && HEX.test(char())) {
4922 value = value * 16 + parseInt(char(), 16);
4926 if (char() == '.') {
4927 if (length == 0) return;
4929 if (pieceIndex > 6) return;
4933 if (numbersSeen > 0) {
4934 if (char() == '.' && numbersSeen < 4) pointer++;
4937 if (!DIGIT.test(char())) return;
4938 while (DIGIT.test(char())) {
4939 number = parseInt(char(), 10);
4940 if (ipv4Piece === null) ipv4Piece = number;
4941 else if (ipv4Piece == 0) return;
4942 else ipv4Piece = ipv4Piece * 10 + number;
4943 if (ipv4Piece > 255) return;
4946 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
4948 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
4950 if (numbersSeen != 4) return;
4952 } else if (char() == ':') {
4954 if (!char()) return;
4955 } else if (char()) return;
4956 address[pieceIndex++] = value;
4958 if (compress !== null) {
4959 swaps = pieceIndex - compress;
4961 while (pieceIndex != 0 && swaps > 0) {
4962 swap = address[pieceIndex];
4963 address[pieceIndex--] = address[compress + swaps - 1];
4964 address[compress + --swaps] = swap;
4966 } else if (pieceIndex != 8) return;
4970 var findLongestZeroSequence = function (ipv6) {
4971 var maxIndex = null;
4973 var currStart = null;
4976 for (; index < 8; index++) {
4977 if (ipv6[index] !== 0) {
4978 if (currLength > maxLength) {
4979 maxIndex = currStart;
4980 maxLength = currLength;
4985 if (currStart === null) currStart = index;
4989 if (currLength > maxLength) {
4990 maxIndex = currStart;
4991 maxLength = currLength;
4996 var serializeHost = function (host) {
4997 var result, index, compress, ignore0;
4999 if (typeof host == 'number') {
5001 for (index = 0; index < 4; index++) {
5002 result.unshift(host % 256);
5003 host = floor$2(host / 256);
5004 } return result.join('.');
5006 } else if (typeof host == 'object') {
5008 compress = findLongestZeroSequence(host);
5009 for (index = 0; index < 8; index++) {
5010 if (ignore0 && host[index] === 0) continue;
5011 if (ignore0) ignore0 = false;
5012 if (compress === index) {
5013 result += index ? ':' : '::';
5016 result += host[index].toString(16);
5017 if (index < 7) result += ':';
5020 return '[' + result + ']';
5024 var C0ControlPercentEncodeSet = {};
5025 var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
5026 ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
5028 var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
5029 '#': 1, '?': 1, '{': 1, '}': 1
5031 var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
5032 '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
5035 var percentEncode = function (char, set) {
5036 var code = codeAt(char, 0);
5037 return code > 0x20 && code < 0x7F && !has$1(set, char) ? char : encodeURIComponent(char);
5040 var specialSchemes = {
5049 var isSpecial = function (url) {
5050 return has$1(specialSchemes, url.scheme);
5053 var includesCredentials = function (url) {
5054 return url.username != '' || url.password != '';
5057 var cannotHaveUsernamePasswordPort = function (url) {
5058 return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
5061 var isWindowsDriveLetter = function (string, normalized) {
5063 return string.length == 2 && ALPHA.test(string.charAt(0))
5064 && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
5067 var startsWithWindowsDriveLetter = function (string) {
5069 return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
5070 string.length == 2 ||
5071 ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
5075 var shortenURLsPath = function (url) {
5076 var path = url.path;
5077 var pathSize = path.length;
5078 if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
5083 var isSingleDot = function (segment) {
5084 return segment === '.' || segment.toLowerCase() === '%2e';
5087 var isDoubleDot = function (segment) {
5088 segment = segment.toLowerCase();
5089 return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
5093 var SCHEME_START = {};
5096 var SPECIAL_RELATIVE_OR_AUTHORITY = {};
5097 var PATH_OR_AUTHORITY = {};
5099 var RELATIVE_SLASH = {};
5100 var SPECIAL_AUTHORITY_SLASHES = {};
5101 var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
5107 var FILE_SLASH = {};
5109 var PATH_START = {};
5111 var CANNOT_BE_A_BASE_URL_PATH = {};
5115 // eslint-disable-next-line max-statements -- TODO
5116 var parseURL = function (url, input, stateOverride, base) {
5117 var state = stateOverride || SCHEME_START;
5121 var seenBracket = false;
5122 var seenPasswordToken = false;
5123 var codePoints, char, bufferCodePoints, failure;
5125 if (!stateOverride) {
5133 url.fragment = null;
5134 url.cannotBeABaseURL = false;
5135 input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
5138 input = input.replace(TAB_AND_NEW_LINE, '');
5140 codePoints = arrayFrom(input);
5142 while (pointer <= codePoints.length) {
5143 char = codePoints[pointer];
5146 if (char && ALPHA.test(char)) {
5147 buffer += char.toLowerCase();
5149 } else if (!stateOverride) {
5152 } else return INVALID_SCHEME;
5156 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
5157 buffer += char.toLowerCase();
5158 } else if (char == ':') {
5159 if (stateOverride && (
5160 (isSpecial(url) != has$1(specialSchemes, buffer)) ||
5161 (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
5162 (url.scheme == 'file' && !url.host)
5164 url.scheme = buffer;
5165 if (stateOverride) {
5166 if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5170 if (url.scheme == 'file') {
5172 } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5173 state = SPECIAL_RELATIVE_OR_AUTHORITY;
5174 } else if (isSpecial(url)) {
5175 state = SPECIAL_AUTHORITY_SLASHES;
5176 } else if (codePoints[pointer + 1] == '/') {
5177 state = PATH_OR_AUTHORITY;
5180 url.cannotBeABaseURL = true;
5182 state = CANNOT_BE_A_BASE_URL_PATH;
5184 } else if (!stateOverride) {
5189 } else return INVALID_SCHEME;
5193 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5194 if (base.cannotBeABaseURL && char == '#') {
5195 url.scheme = base.scheme;
5196 url.path = base.path.slice();
5197 url.query = base.query;
5199 url.cannotBeABaseURL = true;
5203 state = base.scheme == 'file' ? FILE : RELATIVE;
5206 case SPECIAL_RELATIVE_OR_AUTHORITY:
5207 if (char == '/' && codePoints[pointer + 1] == '/') {
5208 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5215 case PATH_OR_AUTHORITY:
5225 url.scheme = base.scheme;
5227 url.username = base.username;
5228 url.password = base.password;
5229 url.host = base.host;
5230 url.port = base.port;
5231 url.path = base.path.slice();
5232 url.query = base.query;
5233 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5234 state = RELATIVE_SLASH;
5235 } else if (char == '?') {
5236 url.username = base.username;
5237 url.password = base.password;
5238 url.host = base.host;
5239 url.port = base.port;
5240 url.path = base.path.slice();
5243 } else if (char == '#') {
5244 url.username = base.username;
5245 url.password = base.password;
5246 url.host = base.host;
5247 url.port = base.port;
5248 url.path = base.path.slice();
5249 url.query = base.query;
5253 url.username = base.username;
5254 url.password = base.password;
5255 url.host = base.host;
5256 url.port = base.port;
5257 url.path = base.path.slice();
5263 case RELATIVE_SLASH:
5264 if (isSpecial(url) && (char == '/' || char == '\\')) {
5265 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5266 } else if (char == '/') {
5269 url.username = base.username;
5270 url.password = base.password;
5271 url.host = base.host;
5272 url.port = base.port;
5277 case SPECIAL_AUTHORITY_SLASHES:
5278 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5279 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5283 case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5284 if (char != '/' && char != '\\') {
5291 if (seenAt) buffer = '%40' + buffer;
5293 bufferCodePoints = arrayFrom(buffer);
5294 for (var i = 0; i < bufferCodePoints.length; i++) {
5295 var codePoint = bufferCodePoints[i];
5296 if (codePoint == ':' && !seenPasswordToken) {
5297 seenPasswordToken = true;
5300 var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5301 if (seenPasswordToken) url.password += encodedCodePoints;
5302 else url.username += encodedCodePoints;
5306 char == EOF || char == '/' || char == '?' || char == '#' ||
5307 (char == '\\' && isSpecial(url))
5309 if (seenAt && buffer == '') return INVALID_AUTHORITY;
5310 pointer -= arrayFrom(buffer).length + 1;
5313 } else buffer += char;
5318 if (stateOverride && url.scheme == 'file') {
5321 } else if (char == ':' && !seenBracket) {
5322 if (buffer == '') return INVALID_HOST;
5323 failure = parseHost(url, buffer);
5324 if (failure) return failure;
5327 if (stateOverride == HOSTNAME) return;
5329 char == EOF || char == '/' || char == '?' || char == '#' ||
5330 (char == '\\' && isSpecial(url))
5332 if (isSpecial(url) && buffer == '') return INVALID_HOST;
5333 if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5334 failure = parseHost(url, buffer);
5335 if (failure) return failure;
5338 if (stateOverride) return;
5341 if (char == '[') seenBracket = true;
5342 else if (char == ']') seenBracket = false;
5347 if (DIGIT.test(char)) {
5350 char == EOF || char == '/' || char == '?' || char == '#' ||
5351 (char == '\\' && isSpecial(url)) ||
5355 var port = parseInt(buffer, 10);
5356 if (port > 0xFFFF) return INVALID_PORT;
5357 url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5360 if (stateOverride) return;
5363 } else return INVALID_PORT;
5367 url.scheme = 'file';
5368 if (char == '/' || char == '\\') state = FILE_SLASH;
5369 else if (base && base.scheme == 'file') {
5371 url.host = base.host;
5372 url.path = base.path.slice();
5373 url.query = base.query;
5374 } else if (char == '?') {
5375 url.host = base.host;
5376 url.path = base.path.slice();
5379 } else if (char == '#') {
5380 url.host = base.host;
5381 url.path = base.path.slice();
5382 url.query = base.query;
5386 if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5387 url.host = base.host;
5388 url.path = base.path.slice();
5389 shortenURLsPath(url);
5400 if (char == '/' || char == '\\') {
5404 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5405 if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5406 else url.host = base.host;
5412 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5413 if (!stateOverride && isWindowsDriveLetter(buffer)) {
5415 } else if (buffer == '') {
5417 if (stateOverride) return;
5420 failure = parseHost(url, buffer);
5421 if (failure) return failure;
5422 if (url.host == 'localhost') url.host = '';
5423 if (stateOverride) return;
5427 } else buffer += char;
5431 if (isSpecial(url)) {
5433 if (char != '/' && char != '\\') continue;
5434 } else if (!stateOverride && char == '?') {
5437 } else if (!stateOverride && char == '#') {
5440 } else if (char != EOF) {
5442 if (char != '/') continue;
5447 char == EOF || char == '/' ||
5448 (char == '\\' && isSpecial(url)) ||
5449 (!stateOverride && (char == '?' || char == '#'))
5451 if (isDoubleDot(buffer)) {
5452 shortenURLsPath(url);
5453 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5456 } else if (isSingleDot(buffer)) {
5457 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5461 if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5462 if (url.host) url.host = '';
5463 buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5465 url.path.push(buffer);
5468 if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5469 while (url.path.length > 1 && url.path[0] === '') {
5476 } else if (char == '#') {
5481 buffer += percentEncode(char, pathPercentEncodeSet);
5484 case CANNOT_BE_A_BASE_URL_PATH:
5488 } else if (char == '#') {
5491 } else if (char != EOF) {
5492 url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5496 if (!stateOverride && char == '#') {
5499 } else if (char != EOF) {
5500 if (char == "'" && isSpecial(url)) url.query += '%27';
5501 else if (char == '#') url.query += '%23';
5502 else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5506 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5514 // `URL` constructor
5515 // https://url.spec.whatwg.org/#url-class
5516 var URLConstructor = function URL(url /* , base */) {
5517 var that = anInstance(this, URLConstructor, 'URL');
5518 var base = arguments.length > 1 ? arguments[1] : undefined;
5519 var urlString = String(url);
5520 var state = setInternalState$1(that, { type: 'URL' });
5521 var baseState, failure;
5522 if (base !== undefined) {
5523 if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5525 failure = parseURL(baseState = {}, String(base));
5526 if (failure) throw TypeError(failure);
5529 failure = parseURL(state, urlString, null, baseState);
5530 if (failure) throw TypeError(failure);
5531 var searchParams = state.searchParams = new URLSearchParams$1();
5532 var searchParamsState = getInternalSearchParamsState(searchParams);
5533 searchParamsState.updateSearchParams(state.query);
5534 searchParamsState.updateURL = function () {
5535 state.query = String(searchParams) || null;
5538 that.href = serializeURL.call(that);
5539 that.origin = getOrigin.call(that);
5540 that.protocol = getProtocol.call(that);
5541 that.username = getUsername.call(that);
5542 that.password = getPassword.call(that);
5543 that.host = getHost.call(that);
5544 that.hostname = getHostname.call(that);
5545 that.port = getPort.call(that);
5546 that.pathname = getPathname.call(that);
5547 that.search = getSearch.call(that);
5548 that.searchParams = getSearchParams.call(that);
5549 that.hash = getHash.call(that);
5553 var URLPrototype = URLConstructor.prototype;
5555 var serializeURL = function () {
5556 var url = getInternalURLState(this);
5557 var scheme = url.scheme;
5558 var username = url.username;
5559 var password = url.password;
5560 var host = url.host;
5561 var port = url.port;
5562 var path = url.path;
5563 var query = url.query;
5564 var fragment = url.fragment;
5565 var output = scheme + ':';
5566 if (host !== null) {
5568 if (includesCredentials(url)) {
5569 output += username + (password ? ':' + password : '') + '@';
5571 output += serializeHost(host);
5572 if (port !== null) output += ':' + port;
5573 } else if (scheme == 'file') output += '//';
5574 output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5575 if (query !== null) output += '?' + query;
5576 if (fragment !== null) output += '#' + fragment;
5580 var getOrigin = function () {
5581 var url = getInternalURLState(this);
5582 var scheme = url.scheme;
5583 var port = url.port;
5584 if (scheme == 'blob') try {
5585 return new URLConstructor(scheme.path[0]).origin;
5589 if (scheme == 'file' || !isSpecial(url)) return 'null';
5590 return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5593 var getProtocol = function () {
5594 return getInternalURLState(this).scheme + ':';
5597 var getUsername = function () {
5598 return getInternalURLState(this).username;
5601 var getPassword = function () {
5602 return getInternalURLState(this).password;
5605 var getHost = function () {
5606 var url = getInternalURLState(this);
5607 var host = url.host;
5608 var port = url.port;
5609 return host === null ? ''
5610 : port === null ? serializeHost(host)
5611 : serializeHost(host) + ':' + port;
5614 var getHostname = function () {
5615 var host = getInternalURLState(this).host;
5616 return host === null ? '' : serializeHost(host);
5619 var getPort = function () {
5620 var port = getInternalURLState(this).port;
5621 return port === null ? '' : String(port);
5624 var getPathname = function () {
5625 var url = getInternalURLState(this);
5626 var path = url.path;
5627 return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5630 var getSearch = function () {
5631 var query = getInternalURLState(this).query;
5632 return query ? '?' + query : '';
5635 var getSearchParams = function () {
5636 return getInternalURLState(this).searchParams;
5639 var getHash = function () {
5640 var fragment = getInternalURLState(this).fragment;
5641 return fragment ? '#' + fragment : '';
5644 var accessorDescriptor = function (getter, setter) {
5645 return { get: getter, set: setter, configurable: true, enumerable: true };
5649 objectDefineProperties(URLPrototype, {
5650 // `URL.prototype.href` accessors pair
5651 // https://url.spec.whatwg.org/#dom-url-href
5652 href: accessorDescriptor(serializeURL, function (href) {
5653 var url = getInternalURLState(this);
5654 var urlString = String(href);
5655 var failure = parseURL(url, urlString);
5656 if (failure) throw TypeError(failure);
5657 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5659 // `URL.prototype.origin` getter
5660 // https://url.spec.whatwg.org/#dom-url-origin
5661 origin: accessorDescriptor(getOrigin),
5662 // `URL.prototype.protocol` accessors pair
5663 // https://url.spec.whatwg.org/#dom-url-protocol
5664 protocol: accessorDescriptor(getProtocol, function (protocol) {
5665 var url = getInternalURLState(this);
5666 parseURL(url, String(protocol) + ':', SCHEME_START);
5668 // `URL.prototype.username` accessors pair
5669 // https://url.spec.whatwg.org/#dom-url-username
5670 username: accessorDescriptor(getUsername, function (username) {
5671 var url = getInternalURLState(this);
5672 var codePoints = arrayFrom(String(username));
5673 if (cannotHaveUsernamePasswordPort(url)) return;
5675 for (var i = 0; i < codePoints.length; i++) {
5676 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5679 // `URL.prototype.password` accessors pair
5680 // https://url.spec.whatwg.org/#dom-url-password
5681 password: accessorDescriptor(getPassword, function (password) {
5682 var url = getInternalURLState(this);
5683 var codePoints = arrayFrom(String(password));
5684 if (cannotHaveUsernamePasswordPort(url)) return;
5686 for (var i = 0; i < codePoints.length; i++) {
5687 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5690 // `URL.prototype.host` accessors pair
5691 // https://url.spec.whatwg.org/#dom-url-host
5692 host: accessorDescriptor(getHost, function (host) {
5693 var url = getInternalURLState(this);
5694 if (url.cannotBeABaseURL) return;
5695 parseURL(url, String(host), HOST);
5697 // `URL.prototype.hostname` accessors pair
5698 // https://url.spec.whatwg.org/#dom-url-hostname
5699 hostname: accessorDescriptor(getHostname, function (hostname) {
5700 var url = getInternalURLState(this);
5701 if (url.cannotBeABaseURL) return;
5702 parseURL(url, String(hostname), HOSTNAME);
5704 // `URL.prototype.port` accessors pair
5705 // https://url.spec.whatwg.org/#dom-url-port
5706 port: accessorDescriptor(getPort, function (port) {
5707 var url = getInternalURLState(this);
5708 if (cannotHaveUsernamePasswordPort(url)) return;
5709 port = String(port);
5710 if (port == '') url.port = null;
5711 else parseURL(url, port, PORT);
5713 // `URL.prototype.pathname` accessors pair
5714 // https://url.spec.whatwg.org/#dom-url-pathname
5715 pathname: accessorDescriptor(getPathname, function (pathname) {
5716 var url = getInternalURLState(this);
5717 if (url.cannotBeABaseURL) return;
5719 parseURL(url, pathname + '', PATH_START);
5721 // `URL.prototype.search` accessors pair
5722 // https://url.spec.whatwg.org/#dom-url-search
5723 search: accessorDescriptor(getSearch, function (search) {
5724 var url = getInternalURLState(this);
5725 search = String(search);
5729 if ('?' == search.charAt(0)) search = search.slice(1);
5731 parseURL(url, search, QUERY);
5733 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5735 // `URL.prototype.searchParams` getter
5736 // https://url.spec.whatwg.org/#dom-url-searchparams
5737 searchParams: accessorDescriptor(getSearchParams),
5738 // `URL.prototype.hash` accessors pair
5739 // https://url.spec.whatwg.org/#dom-url-hash
5740 hash: accessorDescriptor(getHash, function (hash) {
5741 var url = getInternalURLState(this);
5742 hash = String(hash);
5744 url.fragment = null;
5747 if ('#' == hash.charAt(0)) hash = hash.slice(1);
5749 parseURL(url, hash, FRAGMENT);
5754 // `URL.prototype.toJSON` method
5755 // https://url.spec.whatwg.org/#dom-url-tojson
5756 redefine(URLPrototype, 'toJSON', function toJSON() {
5757 return serializeURL.call(this);
5758 }, { enumerable: true });
5760 // `URL.prototype.toString` method
5761 // https://url.spec.whatwg.org/#URL-stringification-behavior
5762 redefine(URLPrototype, 'toString', function toString() {
5763 return serializeURL.call(this);
5764 }, { enumerable: true });
5767 var nativeCreateObjectURL = NativeURL.createObjectURL;
5768 var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
5769 // `URL.createObjectURL` method
5770 // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
5771 // eslint-disable-next-line no-unused-vars -- required for `.length`
5772 if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
5773 return nativeCreateObjectURL.apply(NativeURL, arguments);
5775 // `URL.revokeObjectURL` method
5776 // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
5777 // eslint-disable-next-line no-unused-vars -- required for `.length`
5778 if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
5779 return nativeRevokeObjectURL.apply(NativeURL, arguments);
5783 setToStringTag(URLConstructor, 'URL');
5785 _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
5789 // `RegExp.prototype.flags` getter implementation
5790 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
5791 var regexpFlags = function () {
5792 var that = anObject(this);
5794 if (that.global) result += 'g';
5795 if (that.ignoreCase) result += 'i';
5796 if (that.multiline) result += 'm';
5797 if (that.dotAll) result += 's';
5798 if (that.unicode) result += 'u';
5799 if (that.sticky) result += 'y';
5803 var TO_STRING = 'toString';
5804 var RegExpPrototype$2 = RegExp.prototype;
5805 var nativeToString = RegExpPrototype$2[TO_STRING];
5807 var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
5808 // FF44- RegExp#toString has a wrong name
5809 var INCORRECT_NAME = nativeToString.name != TO_STRING;
5811 // `RegExp.prototype.toString` method
5812 // https://tc39.es/ecma262/#sec-regexp.prototype.tostring
5813 if (NOT_GENERIC || INCORRECT_NAME) {
5814 redefine(RegExp.prototype, TO_STRING, function toString() {
5815 var R = anObject(this);
5816 var p = String(R.source);
5818 var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype$2) ? regexpFlags.call(R) : rf);
5819 return '/' + p + '/' + f;
5820 }, { unsafe: true });
5823 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
5824 var RE = function (s, f) {
5825 return RegExp(s, f);
5828 var UNSUPPORTED_Y$3 = fails(function () {
5829 var re = RE('a', 'y');
5831 return re.exec('abcd') != null;
5834 var BROKEN_CARET = fails(function () {
5835 // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
5836 var re = RE('^r', 'gy');
5838 return re.exec('str') != null;
5841 var regexpStickyHelpers = {
5842 UNSUPPORTED_Y: UNSUPPORTED_Y$3,
5843 BROKEN_CARET: BROKEN_CARET
5846 var regexpUnsupportedDotAll = fails(function () {
5847 // babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError
5848 var re = RegExp('.', (typeof '').charAt(0));
5849 return !(re.dotAll && re.exec('\n') && re.flags === 's');
5852 var regexpUnsupportedNcg = fails(function () {
5853 // babel-minify transpiles RegExp('.', 'g') -> /./g and it causes SyntaxError
5854 var re = RegExp('(?<a>b)', (typeof '').charAt(5));
5855 return re.exec('b').groups.a !== 'b' ||
5856 'b'.replace(re, '$<a>c') !== 'bc';
5859 /* eslint-disable regexp/no-assertion-capturing-group, regexp/no-empty-group, regexp/no-lazy-ends -- testing */
5860 /* eslint-disable regexp/no-useless-quantifier -- testing */
5865 var getInternalState = internalState.get;
5869 var nativeExec = RegExp.prototype.exec;
5870 var nativeReplace = shared('native-string-replace', String.prototype.replace);
5872 var patchedExec = nativeExec;
5874 var UPDATES_LAST_INDEX_WRONG = (function () {
5877 nativeExec.call(re1, 'a');
5878 nativeExec.call(re2, 'a');
5879 return re1.lastIndex !== 0 || re2.lastIndex !== 0;
5882 var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
5884 // nonparticipating capturing group, copied from es5-shim's String#split patch.
5885 var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
5887 var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$2 || regexpUnsupportedDotAll || regexpUnsupportedNcg;
5890 // eslint-disable-next-line max-statements -- TODO
5891 patchedExec = function exec(str) {
5893 var state = getInternalState(re);
5894 var raw = state.raw;
5895 var result, reCopy, lastIndex, match, i, object, group;
5898 raw.lastIndex = re.lastIndex;
5899 result = patchedExec.call(raw, str);
5900 re.lastIndex = raw.lastIndex;
5904 var groups = state.groups;
5905 var sticky = UNSUPPORTED_Y$2 && re.sticky;
5906 var flags = regexpFlags.call(re);
5907 var source = re.source;
5912 flags = flags.replace('y', '');
5913 if (flags.indexOf('g') === -1) {
5917 strCopy = String(str).slice(re.lastIndex);
5918 // Support anchored sticky behavior.
5919 if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
5920 source = '(?: ' + source + ')';
5921 strCopy = ' ' + strCopy;
5924 // ^(? + rx + ) is needed, in combination with some str slicing, to
5925 // simulate the 'y' flag.
5926 reCopy = new RegExp('^(?:' + source + ')', flags);
5929 if (NPCG_INCLUDED) {
5930 reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
5932 if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
5934 match = nativeExec.call(sticky ? reCopy : re, strCopy);
5938 match.input = match.input.slice(charsAdded);
5939 match[0] = match[0].slice(charsAdded);
5940 match.index = re.lastIndex;
5941 re.lastIndex += match[0].length;
5942 } else re.lastIndex = 0;
5943 } else if (UPDATES_LAST_INDEX_WRONG && match) {
5944 re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
5946 if (NPCG_INCLUDED && match && match.length > 1) {
5947 // Fix browsers whose `exec` methods don't consistently return `undefined`
5948 // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
5949 nativeReplace.call(match[0], reCopy, function () {
5950 for (i = 1; i < arguments.length - 2; i++) {
5951 if (arguments[i] === undefined) match[i] = undefined;
5956 if (match && groups) {
5957 match.groups = object = objectCreate(null);
5958 for (i = 0; i < groups.length; i++) {
5960 object[group[0]] = match[group[1]];
5968 var regexpExec = patchedExec;
5970 // `RegExp.prototype.exec` method
5971 // https://tc39.es/ecma262/#sec-regexp.prototype.exec
5972 _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
5976 // TODO: Remove from `core-js@4` since it's moved to entry points
5984 var SPECIES = wellKnownSymbol('species');
5985 var RegExpPrototype$1 = RegExp.prototype;
5987 var fixRegexpWellKnownSymbolLogic = function (KEY, exec, FORCED, SHAM) {
5988 var SYMBOL = wellKnownSymbol(KEY);
5990 var DELEGATES_TO_SYMBOL = !fails(function () {
5991 // String methods call symbol-named RegEp methods
5993 O[SYMBOL] = function () { return 7; };
5994 return ''[KEY](O) != 7;
5997 var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
5998 // Symbol-named RegExp methods call .exec
5999 var execCalled = false;
6002 if (KEY === 'split') {
6003 // We can't use real regex here since it causes deoptimization
6004 // and serious performance degradation in V8
6005 // https://github.com/zloirock/core-js/issues/306
6007 // RegExp[@@split] doesn't call the regex's exec method, but first creates
6008 // a new one. We need to return the patched regex when creating the new one.
6009 re.constructor = {};
6010 re.constructor[SPECIES] = function () { return re; };
6012 re[SYMBOL] = /./[SYMBOL];
6015 re.exec = function () { execCalled = true; return null; };
6022 !DELEGATES_TO_SYMBOL ||
6023 !DELEGATES_TO_EXEC ||
6026 var nativeRegExpMethod = /./[SYMBOL];
6027 var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
6028 var $exec = regexp.exec;
6029 if ($exec === regexpExec || $exec === RegExpPrototype$1.exec) {
6030 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
6031 // The native String method already delegates to @@method (this
6032 // polyfilled function), leasing to infinite recursion.
6033 // We avoid it by directly calling the native @@method method.
6034 return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
6036 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
6038 return { done: false };
6041 redefine(String.prototype, KEY, methods[0]);
6042 redefine(RegExpPrototype$1, SYMBOL, methods[1]);
6045 if (SHAM) createNonEnumerableProperty(RegExpPrototype$1[SYMBOL], 'sham', true);
6048 var charAt = stringMultibyte.charAt;
6050 // `AdvanceStringIndex` abstract operation
6051 // https://tc39.es/ecma262/#sec-advancestringindex
6052 var advanceStringIndex = function (S, index, unicode) {
6053 return index + (unicode ? charAt(S, index).length : 1);
6056 var floor$1 = Math.floor;
6057 var replace = ''.replace;
6058 var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d{1,2}|<[^>]*>)/g;
6059 var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d{1,2})/g;
6061 // `GetSubstitution` abstract operation
6062 // https://tc39.es/ecma262/#sec-getsubstitution
6063 var getSubstitution = function (matched, str, position, captures, namedCaptures, replacement) {
6064 var tailPos = position + matched.length;
6065 var m = captures.length;
6066 var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
6067 if (namedCaptures !== undefined) {
6068 namedCaptures = toObject(namedCaptures);
6069 symbols = SUBSTITUTION_SYMBOLS;
6071 return replace.call(replacement, symbols, function (match, ch) {
6073 switch (ch.charAt(0)) {
6074 case '$': return '$';
6075 case '&': return matched;
6076 case '`': return str.slice(0, position);
6077 case "'": return str.slice(tailPos);
6079 capture = namedCaptures[ch.slice(1, -1)];
6083 if (n === 0) return match;
6085 var f = floor$1(n / 10);
6086 if (f === 0) return match;
6087 if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
6090 capture = captures[n - 1];
6092 return capture === undefined ? '' : capture;
6096 // `RegExpExec` abstract operation
6097 // https://tc39.es/ecma262/#sec-regexpexec
6098 var regexpExecAbstract = function (R, S) {
6100 if (typeof exec === 'function') {
6101 var result = exec.call(R, S);
6102 if (typeof result !== 'object') {
6103 throw TypeError('RegExp exec method returned something other than an Object or null');
6108 if (classofRaw(R) !== 'RegExp') {
6109 throw TypeError('RegExp#exec called on incompatible receiver');
6112 return regexpExec.call(R, S);
6115 var REPLACE = wellKnownSymbol('replace');
6116 var max$2 = Math.max;
6117 var min$5 = Math.min;
6119 var maybeToString = function (it) {
6120 return it === undefined ? it : String(it);
6123 // IE <= 11 replaces $0 with the whole match, as if it was $&
6124 // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
6125 var REPLACE_KEEPS_$0 = (function () {
6126 // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing
6127 return 'a'.replace(/./, '$0') === '$0';
6130 // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
6131 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
6133 return /./[REPLACE]('a', '$0') === '';
6138 var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
6140 re.exec = function () {
6142 result.groups = { a: '7' };
6145 return ''.replace(re, '$<a>') !== '7';
6149 fixRegexpWellKnownSymbolLogic('replace', function (_, nativeReplace, maybeCallNative) {
6150 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
6153 // `String.prototype.replace` method
6154 // https://tc39.es/ecma262/#sec-string.prototype.replace
6155 function replace(searchValue, replaceValue) {
6156 var O = requireObjectCoercible(this);
6157 var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
6158 return replacer !== undefined
6159 ? replacer.call(searchValue, O, replaceValue)
6160 : nativeReplace.call(String(O), searchValue, replaceValue);
6162 // `RegExp.prototype[@@replace]` method
6163 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
6164 function (string, replaceValue) {
6166 typeof replaceValue === 'string' &&
6167 replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1 &&
6168 replaceValue.indexOf('$<') === -1
6170 var res = maybeCallNative(nativeReplace, this, string, replaceValue);
6171 if (res.done) return res.value;
6174 var rx = anObject(this);
6175 var S = String(string);
6177 var functionalReplace = typeof replaceValue === 'function';
6178 if (!functionalReplace) replaceValue = String(replaceValue);
6180 var global = rx.global;
6182 var fullUnicode = rx.unicode;
6187 var result = regexpExecAbstract(rx, S);
6188 if (result === null) break;
6190 results.push(result);
6193 var matchStr = String(result[0]);
6194 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
6197 var accumulatedResult = '';
6198 var nextSourcePosition = 0;
6199 for (var i = 0; i < results.length; i++) {
6200 result = results[i];
6202 var matched = String(result[0]);
6203 var position = max$2(min$5(toInteger(result.index), S.length), 0);
6205 // NOTE: This is equivalent to
6206 // captures = result.slice(1).map(maybeToString)
6207 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
6208 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
6209 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
6210 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
6211 var namedCaptures = result.groups;
6212 if (functionalReplace) {
6213 var replacerArgs = [matched].concat(captures, position, S);
6214 if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
6215 var replacement = String(replaceValue.apply(undefined, replacerArgs));
6217 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
6219 if (position >= nextSourcePosition) {
6220 accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
6221 nextSourcePosition = position + matched.length;
6224 return accumulatedResult + S.slice(nextSourcePosition);
6227 }, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE);
6229 var MATCH$2 = wellKnownSymbol('match');
6231 // `IsRegExp` abstract operation
6232 // https://tc39.es/ecma262/#sec-isregexp
6233 var isRegexp = function (it) {
6235 return isObject$4(it) && ((isRegExp = it[MATCH$2]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
6238 var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y;
6239 var arrayPush = [].push;
6240 var min$4 = Math.min;
6241 var MAX_UINT32 = 0xFFFFFFFF;
6243 // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
6244 // Weex JS has frozen built-in prototypes, so use try / catch wrapper
6245 var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
6246 // eslint-disable-next-line regexp/no-empty-group -- required for testing
6248 var originalExec = re.exec;
6249 re.exec = function () { return originalExec.apply(this, arguments); };
6250 var result = 'ab'.split(re);
6251 return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
6255 fixRegexpWellKnownSymbolLogic('split', function (SPLIT, nativeSplit, maybeCallNative) {
6258 'abbc'.split(/(b)*/)[1] == 'c' ||
6259 // eslint-disable-next-line regexp/no-empty-group -- required for testing
6260 'test'.split(/(?:)/, -1).length != 4 ||
6261 'ab'.split(/(?:ab)*/).length != 2 ||
6262 '.'.split(/(.?)(.?)/).length != 4 ||
6263 // eslint-disable-next-line regexp/no-assertion-capturing-group, regexp/no-empty-group -- required for testing
6264 '.'.split(/()()/).length > 1 ||
6265 ''.split(/.?/).length
6267 // based on es5-shim implementation, need to rework it
6268 internalSplit = function (separator, limit) {
6269 var string = String(requireObjectCoercible(this));
6270 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6271 if (lim === 0) return [];
6272 if (separator === undefined) return [string];
6273 // If `separator` is not a regex, use native split
6274 if (!isRegexp(separator)) {
6275 return nativeSplit.call(string, separator, lim);
6278 var flags = (separator.ignoreCase ? 'i' : '') +
6279 (separator.multiline ? 'm' : '') +
6280 (separator.unicode ? 'u' : '') +
6281 (separator.sticky ? 'y' : '');
6282 var lastLastIndex = 0;
6283 // Make `global` and avoid `lastIndex` issues by working with a copy
6284 var separatorCopy = new RegExp(separator.source, flags + 'g');
6285 var match, lastIndex, lastLength;
6286 while (match = regexpExec.call(separatorCopy, string)) {
6287 lastIndex = separatorCopy.lastIndex;
6288 if (lastIndex > lastLastIndex) {
6289 output.push(string.slice(lastLastIndex, match.index));
6290 if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
6291 lastLength = match[0].length;
6292 lastLastIndex = lastIndex;
6293 if (output.length >= lim) break;
6295 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
6297 if (lastLastIndex === string.length) {
6298 if (lastLength || !separatorCopy.test('')) output.push('');
6299 } else output.push(string.slice(lastLastIndex));
6300 return output.length > lim ? output.slice(0, lim) : output;
6303 } else if ('0'.split(undefined, 0).length) {
6304 internalSplit = function (separator, limit) {
6305 return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
6307 } else internalSplit = nativeSplit;
6310 // `String.prototype.split` method
6311 // https://tc39.es/ecma262/#sec-string.prototype.split
6312 function split(separator, limit) {
6313 var O = requireObjectCoercible(this);
6314 var splitter = separator == undefined ? undefined : separator[SPLIT];
6315 return splitter !== undefined
6316 ? splitter.call(separator, O, limit)
6317 : internalSplit.call(String(O), separator, limit);
6319 // `RegExp.prototype[@@split]` method
6320 // https://tc39.es/ecma262/#sec-regexp.prototype-@@split
6322 // NOTE: This cannot be properly polyfilled in engines that don't support
6324 function (string, limit) {
6325 var res = maybeCallNative(internalSplit, this, string, limit, internalSplit !== nativeSplit);
6326 if (res.done) return res.value;
6328 var rx = anObject(this);
6329 var S = String(string);
6330 var C = speciesConstructor(rx, RegExp);
6332 var unicodeMatching = rx.unicode;
6333 var flags = (rx.ignoreCase ? 'i' : '') +
6334 (rx.multiline ? 'm' : '') +
6335 (rx.unicode ? 'u' : '') +
6336 (UNSUPPORTED_Y$1 ? 'g' : 'y');
6338 // ^(? + rx + ) is needed, in combination with some S slicing, to
6339 // simulate the 'y' flag.
6340 var splitter = new C(UNSUPPORTED_Y$1 ? '^(?:' + rx.source + ')' : rx, flags);
6341 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
6342 if (lim === 0) return [];
6343 if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
6347 while (q < S.length) {
6348 splitter.lastIndex = UNSUPPORTED_Y$1 ? 0 : q;
6349 var z = regexpExecAbstract(splitter, UNSUPPORTED_Y$1 ? S.slice(q) : S);
6353 (e = min$4(toLength(splitter.lastIndex + (UNSUPPORTED_Y$1 ? q : 0)), S.length)) === p
6355 q = advanceStringIndex(S, q, unicodeMatching);
6357 A.push(S.slice(p, q));
6358 if (A.length === lim) return A;
6359 for (var i = 1; i <= z.length - 1; i++) {
6361 if (A.length === lim) return A;
6370 }, !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC, UNSUPPORTED_Y$1);
6372 // a string of all valid unicode whitespaces
6373 var whitespaces = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' +
6374 '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
6376 var whitespace = '[' + whitespaces + ']';
6377 var ltrim = RegExp('^' + whitespace + whitespace + '*');
6378 var rtrim$2 = RegExp(whitespace + whitespace + '*$');
6380 // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
6381 var createMethod$2 = function (TYPE) {
6382 return function ($this) {
6383 var string = String(requireObjectCoercible($this));
6384 if (TYPE & 1) string = string.replace(ltrim, '');
6385 if (TYPE & 2) string = string.replace(rtrim$2, '');
6391 // `String.prototype.{ trimLeft, trimStart }` methods
6392 // https://tc39.es/ecma262/#sec-string.prototype.trimstart
6393 start: createMethod$2(1),
6394 // `String.prototype.{ trimRight, trimEnd }` methods
6395 // https://tc39.es/ecma262/#sec-string.prototype.trimend
6396 end: createMethod$2(2),
6397 // `String.prototype.trim` method
6398 // https://tc39.es/ecma262/#sec-string.prototype.trim
6399 trim: createMethod$2(3)
6402 var non = '\u200B\u0085\u180E';
6404 // check that a method works with the correct list
6405 // of whitespaces and has a correct name
6406 var stringTrimForced = function (METHOD_NAME) {
6407 return fails(function () {
6408 return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
6412 var $trim = stringTrim.trim;
6415 // `String.prototype.trim` method
6416 // https://tc39.es/ecma262/#sec-string.prototype.trim
6417 _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
6418 trim: function trim() {
6423 var defineProperty$3 = objectDefineProperty.f;
6425 var FunctionPrototype = Function.prototype;
6426 var FunctionPrototypeToString = FunctionPrototype.toString;
6427 var nameRE = /^\s*function ([^ (]*)/;
6430 // Function instances `.name` property
6431 // https://tc39.es/ecma262/#sec-function-instances-name
6432 if (descriptors && !(NAME in FunctionPrototype)) {
6433 defineProperty$3(FunctionPrototype, NAME, {
6437 return FunctionPrototypeToString.call(this).match(nameRE)[1];
6445 // `Object.create` method
6446 // https://tc39.es/ecma262/#sec-object.create
6447 _export({ target: 'Object', stat: true, sham: !descriptors }, {
6448 create: objectCreate
6451 var slice$3 = [].slice;
6452 var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
6454 var wrap$1 = function (scheduler) {
6455 return function (handler, timeout /* , ...arguments */) {
6456 var boundArgs = arguments.length > 2;
6457 var args = boundArgs ? slice$3.call(arguments, 2) : undefined;
6458 return scheduler(boundArgs ? function () {
6459 // eslint-disable-next-line no-new-func -- spec requirement
6460 (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
6461 } : handler, timeout);
6465 // ie9- setTimeout & setInterval additional parameters fix
6466 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
6467 _export({ global: true, bind: true, forced: MSIE }, {
6468 // `setTimeout` method
6469 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
6470 setTimeout: wrap$1(global$2.setTimeout),
6471 // `setInterval` method
6472 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
6473 setInterval: wrap$1(global$2.setInterval)
6476 var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6478 searchParams: 'URLSearchParams' in global$1,
6479 iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6480 blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6488 formData: 'FormData' in global$1,
6489 arrayBuffer: 'ArrayBuffer' in global$1
6492 function isDataView(obj) {
6493 return obj && DataView.prototype.isPrototypeOf(obj);
6496 if (support.arrayBuffer) {
6497 var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6499 var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6500 return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6504 function normalizeName(name) {
6505 if (typeof name !== 'string') {
6506 name = String(name);
6509 if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6510 throw new TypeError('Invalid character in header field name: "' + name + '"');
6513 return name.toLowerCase();
6516 function normalizeValue(value) {
6517 if (typeof value !== 'string') {
6518 value = String(value);
6522 } // Build a destructive iterator for the value list
6525 function iteratorFor(items) {
6527 next: function next() {
6528 var value = items.shift();
6530 done: value === undefined,
6536 if (support.iterable) {
6537 iterator[Symbol.iterator] = function () {
6545 function Headers(headers) {
6548 if (headers instanceof Headers) {
6549 headers.forEach(function (value, name) {
6550 this.append(name, value);
6552 } else if (Array.isArray(headers)) {
6553 headers.forEach(function (header) {
6554 this.append(header[0], header[1]);
6556 } else if (headers) {
6557 Object.getOwnPropertyNames(headers).forEach(function (name) {
6558 this.append(name, headers[name]);
6563 Headers.prototype.append = function (name, value) {
6564 name = normalizeName(name);
6565 value = normalizeValue(value);
6566 var oldValue = this.map[name];
6567 this.map[name] = oldValue ? oldValue + ', ' + value : value;
6570 Headers.prototype['delete'] = function (name) {
6571 delete this.map[normalizeName(name)];
6574 Headers.prototype.get = function (name) {
6575 name = normalizeName(name);
6576 return this.has(name) ? this.map[name] : null;
6579 Headers.prototype.has = function (name) {
6580 return this.map.hasOwnProperty(normalizeName(name));
6583 Headers.prototype.set = function (name, value) {
6584 this.map[normalizeName(name)] = normalizeValue(value);
6587 Headers.prototype.forEach = function (callback, thisArg) {
6588 for (var name in this.map) {
6589 if (this.map.hasOwnProperty(name)) {
6590 callback.call(thisArg, this.map[name], name, this);
6595 Headers.prototype.keys = function () {
6597 this.forEach(function (value, name) {
6600 return iteratorFor(items);
6603 Headers.prototype.values = function () {
6605 this.forEach(function (value) {
6608 return iteratorFor(items);
6611 Headers.prototype.entries = function () {
6613 this.forEach(function (value, name) {
6614 items.push([name, value]);
6616 return iteratorFor(items);
6619 if (support.iterable) {
6620 Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
6623 function consumed(body) {
6624 if (body.bodyUsed) {
6625 return Promise.reject(new TypeError('Already read'));
6628 body.bodyUsed = true;
6631 function fileReaderReady(reader) {
6632 return new Promise(function (resolve, reject) {
6633 reader.onload = function () {
6634 resolve(reader.result);
6637 reader.onerror = function () {
6638 reject(reader.error);
6643 function readBlobAsArrayBuffer(blob) {
6644 var reader = new FileReader();
6645 var promise = fileReaderReady(reader);
6646 reader.readAsArrayBuffer(blob);
6650 function readBlobAsText(blob) {
6651 var reader = new FileReader();
6652 var promise = fileReaderReady(reader);
6653 reader.readAsText(blob);
6657 function readArrayBufferAsText(buf) {
6658 var view = new Uint8Array(buf);
6659 var chars = new Array(view.length);
6661 for (var i = 0; i < view.length; i++) {
6662 chars[i] = String.fromCharCode(view[i]);
6665 return chars.join('');
6668 function bufferClone(buf) {
6670 return buf.slice(0);
6672 var view = new Uint8Array(buf.byteLength);
6673 view.set(new Uint8Array(buf));
6679 this.bodyUsed = false;
6681 this._initBody = function (body) {
6683 fetch-mock wraps the Response object in an ES6 Proxy to
6684 provide useful test harness features such as flush. However, on
6685 ES5 browsers without fetch or Proxy support pollyfills must be used;
6686 the proxy-pollyfill is unable to proxy an attribute unless it exists
6687 on the object before the Proxy is created. This change ensures
6688 Response.bodyUsed exists on the instance, while maintaining the
6689 semantic of setting Request.bodyUsed in the constructor before
6690 _initBody is called.
6692 this.bodyUsed = this.bodyUsed;
6693 this._bodyInit = body;
6696 this._bodyText = '';
6697 } else if (typeof body === 'string') {
6698 this._bodyText = body;
6699 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6700 this._bodyBlob = body;
6701 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6702 this._bodyFormData = body;
6703 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6704 this._bodyText = body.toString();
6705 } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6706 this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6708 this._bodyInit = new Blob([this._bodyArrayBuffer]);
6709 } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6710 this._bodyArrayBuffer = bufferClone(body);
6712 this._bodyText = body = Object.prototype.toString.call(body);
6715 if (!this.headers.get('content-type')) {
6716 if (typeof body === 'string') {
6717 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6718 } else if (this._bodyBlob && this._bodyBlob.type) {
6719 this.headers.set('content-type', this._bodyBlob.type);
6720 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6721 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6727 this.blob = function () {
6728 var rejected = consumed(this);
6734 if (this._bodyBlob) {
6735 return Promise.resolve(this._bodyBlob);
6736 } else if (this._bodyArrayBuffer) {
6737 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6738 } else if (this._bodyFormData) {
6739 throw new Error('could not read FormData body as blob');
6741 return Promise.resolve(new Blob([this._bodyText]));
6745 this.arrayBuffer = function () {
6746 if (this._bodyArrayBuffer) {
6747 var isConsumed = consumed(this);
6753 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6754 return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6756 return Promise.resolve(this._bodyArrayBuffer);
6759 return this.blob().then(readBlobAsArrayBuffer);
6764 this.text = function () {
6765 var rejected = consumed(this);
6771 if (this._bodyBlob) {
6772 return readBlobAsText(this._bodyBlob);
6773 } else if (this._bodyArrayBuffer) {
6774 return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6775 } else if (this._bodyFormData) {
6776 throw new Error('could not read FormData body as text');
6778 return Promise.resolve(this._bodyText);
6782 if (support.formData) {
6783 this.formData = function () {
6784 return this.text().then(decode);
6788 this.json = function () {
6789 return this.text().then(JSON.parse);
6793 } // HTTP methods whose capitalization should be normalized
6796 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6798 function normalizeMethod(method) {
6799 var upcased = method.toUpperCase();
6800 return methods.indexOf(upcased) > -1 ? upcased : method;
6803 function Request(input, options) {
6804 if (!(this instanceof Request)) {
6805 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6808 options = options || {};
6809 var body = options.body;
6811 if (input instanceof Request) {
6812 if (input.bodyUsed) {
6813 throw new TypeError('Already read');
6816 this.url = input.url;
6817 this.credentials = input.credentials;
6819 if (!options.headers) {
6820 this.headers = new Headers(input.headers);
6823 this.method = input.method;
6824 this.mode = input.mode;
6825 this.signal = input.signal;
6827 if (!body && input._bodyInit != null) {
6828 body = input._bodyInit;
6829 input.bodyUsed = true;
6832 this.url = String(input);
6835 this.credentials = options.credentials || this.credentials || 'same-origin';
6837 if (options.headers || !this.headers) {
6838 this.headers = new Headers(options.headers);
6841 this.method = normalizeMethod(options.method || this.method || 'GET');
6842 this.mode = options.mode || this.mode || null;
6843 this.signal = options.signal || this.signal;
6844 this.referrer = null;
6846 if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6847 throw new TypeError('Body not allowed for GET or HEAD requests');
6850 this._initBody(body);
6852 if (this.method === 'GET' || this.method === 'HEAD') {
6853 if (options.cache === 'no-store' || options.cache === 'no-cache') {
6854 // Search for a '_' parameter in the query string
6855 var reParamSearch = /([?&])_=[^&]*/;
6857 if (reParamSearch.test(this.url)) {
6858 // If it already exists then set the value with the current time
6859 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6861 // Otherwise add a new '_' parameter to the end with the current time
6862 var reQueryString = /\?/;
6863 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6869 Request.prototype.clone = function () {
6870 return new Request(this, {
6871 body: this._bodyInit
6875 function decode(body) {
6876 var form = new FormData();
6877 body.trim().split('&').forEach(function (bytes) {
6879 var split = bytes.split('=');
6880 var name = split.shift().replace(/\+/g, ' ');
6881 var value = split.join('=').replace(/\+/g, ' ');
6882 form.append(decodeURIComponent(name), decodeURIComponent(value));
6888 function parseHeaders(rawHeaders) {
6889 var headers = new Headers(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6890 // https://tools.ietf.org/html/rfc7230#section-3.2
6892 var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
6893 // https://github.com/github/fetch/issues/748
6894 // https://github.com/zloirock/core-js/issues/751
6896 preProcessedHeaders.split('\r').map(function (header) {
6897 return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header;
6898 }).forEach(function (line) {
6899 var parts = line.split(':');
6900 var key = parts.shift().trim();
6903 var value = parts.join(':').trim();
6904 headers.append(key, value);
6910 Body.call(Request.prototype);
6911 function Response(bodyInit, options) {
6912 if (!(this instanceof Response)) {
6913 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6920 this.type = 'default';
6921 this.status = options.status === undefined ? 200 : options.status;
6922 this.ok = this.status >= 200 && this.status < 300;
6923 this.statusText = options.statusText === undefined ? '' : '' + options.statusText;
6924 this.headers = new Headers(options.headers);
6925 this.url = options.url || '';
6927 this._initBody(bodyInit);
6929 Body.call(Response.prototype);
6931 Response.prototype.clone = function () {
6932 return new Response(this._bodyInit, {
6933 status: this.status,
6934 statusText: this.statusText,
6935 headers: new Headers(this.headers),
6940 Response.error = function () {
6941 var response = new Response(null, {
6945 response.type = 'error';
6949 var redirectStatuses = [301, 302, 303, 307, 308];
6951 Response.redirect = function (url, status) {
6952 if (redirectStatuses.indexOf(status) === -1) {
6953 throw new RangeError('Invalid status code');
6956 return new Response(null, {
6964 var DOMException$1 = global$1.DOMException;
6967 new DOMException$1();
6969 DOMException$1 = function DOMException(message, name) {
6970 this.message = message;
6972 var error = Error(message);
6973 this.stack = error.stack;
6976 DOMException$1.prototype = Object.create(Error.prototype);
6977 DOMException$1.prototype.constructor = DOMException$1;
6980 function fetch$1(input, init) {
6981 return new Promise(function (resolve, reject) {
6982 var request = new Request(input, init);
6984 if (request.signal && request.signal.aborted) {
6985 return reject(new DOMException$1('Aborted', 'AbortError'));
6988 var xhr = new XMLHttpRequest();
6990 function abortXhr() {
6994 xhr.onload = function () {
6997 statusText: xhr.statusText,
6998 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
7000 options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
7001 var body = 'response' in xhr ? xhr.response : xhr.responseText;
7002 setTimeout(function () {
7003 resolve(new Response(body, options));
7007 xhr.onerror = function () {
7008 setTimeout(function () {
7009 reject(new TypeError('Network request failed'));
7013 xhr.ontimeout = function () {
7014 setTimeout(function () {
7015 reject(new TypeError('Network request failed'));
7019 xhr.onabort = function () {
7020 setTimeout(function () {
7021 reject(new DOMException$1('Aborted', 'AbortError'));
7025 function fixUrl(url) {
7027 return url === '' && global$1.location.href ? global$1.location.href : url;
7033 xhr.open(request.method, fixUrl(request.url), true);
7035 if (request.credentials === 'include') {
7036 xhr.withCredentials = true;
7037 } else if (request.credentials === 'omit') {
7038 xhr.withCredentials = false;
7041 if ('responseType' in xhr) {
7043 xhr.responseType = 'blob';
7044 } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
7045 xhr.responseType = 'arraybuffer';
7049 if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers)) {
7050 Object.getOwnPropertyNames(init.headers).forEach(function (name) {
7051 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
7054 request.headers.forEach(function (value, name) {
7055 xhr.setRequestHeader(name, value);
7059 if (request.signal) {
7060 request.signal.addEventListener('abort', abortXhr);
7062 xhr.onreadystatechange = function () {
7063 // DONE (success or failure)
7064 if (xhr.readyState === 4) {
7065 request.signal.removeEventListener('abort', abortXhr);
7070 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
7073 fetch$1.polyfill = true;
7075 if (!global$1.fetch) {
7076 global$1.fetch = fetch$1;
7077 global$1.Headers = Headers;
7078 global$1.Request = Request;
7079 global$1.Response = Response;
7082 // `Object.defineProperty` method
7083 // https://tc39.es/ecma262/#sec-object.defineproperty
7084 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
7085 defineProperty: objectDefineProperty.f
7088 // `Object.setPrototypeOf` method
7089 // https://tc39.es/ecma262/#sec-object.setprototypeof
7090 _export({ target: 'Object', stat: true }, {
7091 setPrototypeOf: objectSetPrototypeOf
7094 var FAILS_ON_PRIMITIVES$3 = fails(function () { objectGetPrototypeOf(1); });
7096 // `Object.getPrototypeOf` method
7097 // https://tc39.es/ecma262/#sec-object.getprototypeof
7098 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3, sham: !correctPrototypeGetter }, {
7099 getPrototypeOf: function getPrototypeOf(it) {
7100 return objectGetPrototypeOf(toObject(it));
7104 var slice$2 = [].slice;
7107 var construct = function (C, argsLength, args) {
7108 if (!(argsLength in factories)) {
7109 for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
7110 // eslint-disable-next-line no-new-func -- we have no proper alternatives, IE8- only
7111 factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
7112 } return factories[argsLength](C, args);
7115 // `Function.prototype.bind` method implementation
7116 // https://tc39.es/ecma262/#sec-function.prototype.bind
7117 var functionBind = Function.bind || function bind(that /* , ...args */) {
7118 var fn = aFunction(this);
7119 var partArgs = slice$2.call(arguments, 1);
7120 var boundFunction = function bound(/* args... */) {
7121 var args = partArgs.concat(slice$2.call(arguments));
7122 return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
7124 if (isObject$4(fn.prototype)) boundFunction.prototype = fn.prototype;
7125 return boundFunction;
7128 var nativeConstruct = getBuiltIn('Reflect', 'construct');
7130 // `Reflect.construct` method
7131 // https://tc39.es/ecma262/#sec-reflect.construct
7132 // MS Edge supports only 2 arguments and argumentsList argument is optional
7133 // FF Nightly sets third argument as `new.target`, but does not create `this` from it
7134 var NEW_TARGET_BUG = fails(function () {
7135 function F() { /* empty */ }
7136 return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
7138 var ARGS_BUG = !fails(function () {
7139 nativeConstruct(function () { /* empty */ });
7141 var FORCED$a = NEW_TARGET_BUG || ARGS_BUG;
7143 _export({ target: 'Reflect', stat: true, forced: FORCED$a, sham: FORCED$a }, {
7144 construct: function construct(Target, args /* , newTarget */) {
7147 var newTarget = arguments.length < 3 ? Target : aFunction(arguments[2]);
7148 if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
7149 if (Target == newTarget) {
7150 // w/o altered newTarget, optimization for 0-4 arguments
7151 switch (args.length) {
7152 case 0: return new Target();
7153 case 1: return new Target(args[0]);
7154 case 2: return new Target(args[0], args[1]);
7155 case 3: return new Target(args[0], args[1], args[2]);
7156 case 4: return new Target(args[0], args[1], args[2], args[3]);
7158 // w/o altered newTarget, lot of arguments case
7160 $args.push.apply($args, args);
7161 return new (functionBind.apply(Target, $args))();
7163 // with altered newTarget, not support built-in constructors
7164 var proto = newTarget.prototype;
7165 var instance = objectCreate(isObject$4(proto) ? proto : Object.prototype);
7166 var result = Function.apply.call(Target, instance, args);
7167 return isObject$4(result) ? result : instance;
7171 // `Reflect.get` method
7172 // https://tc39.es/ecma262/#sec-reflect.get
7173 function get$3(target, propertyKey /* , receiver */) {
7174 var receiver = arguments.length < 3 ? target : arguments[2];
7175 var descriptor, prototype;
7176 if (anObject(target) === receiver) return target[propertyKey];
7177 if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has$1(descriptor, 'value')
7179 : descriptor.get === undefined
7181 : descriptor.get.call(receiver);
7182 if (isObject$4(prototype = objectGetPrototypeOf(target))) return get$3(prototype, propertyKey, receiver);
7185 _export({ target: 'Reflect', stat: true }, {
7189 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
7192 var FAILS_ON_PRIMITIVES$2 = fails(function () { nativeGetOwnPropertyDescriptor(1); });
7193 var FORCED$9 = !descriptors || FAILS_ON_PRIMITIVES$2;
7195 // `Object.getOwnPropertyDescriptor` method
7196 // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
7197 _export({ target: 'Object', stat: true, forced: FORCED$9, sham: !descriptors }, {
7198 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
7199 return nativeGetOwnPropertyDescriptor(toIndexedObject(it), key);
7203 var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('splice');
7205 var max$1 = Math.max;
7206 var min$3 = Math.min;
7207 var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7208 var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
7210 // `Array.prototype.splice` method
7211 // https://tc39.es/ecma262/#sec-array.prototype.splice
7212 // with adding support of @@species
7213 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, {
7214 splice: function splice(start, deleteCount /* , ...items */) {
7215 var O = toObject(this);
7216 var len = toLength(O.length);
7217 var actualStart = toAbsoluteIndex(start, len);
7218 var argumentsLength = arguments.length;
7219 var insertCount, actualDeleteCount, A, k, from, to;
7220 if (argumentsLength === 0) {
7221 insertCount = actualDeleteCount = 0;
7222 } else if (argumentsLength === 1) {
7224 actualDeleteCount = len - actualStart;
7226 insertCount = argumentsLength - 2;
7227 actualDeleteCount = min$3(max$1(toInteger(deleteCount), 0), len - actualStart);
7229 if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER$1) {
7230 throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
7232 A = arraySpeciesCreate(O, actualDeleteCount);
7233 for (k = 0; k < actualDeleteCount; k++) {
7234 from = actualStart + k;
7235 if (from in O) createProperty(A, k, O[from]);
7237 A.length = actualDeleteCount;
7238 if (insertCount < actualDeleteCount) {
7239 for (k = actualStart; k < len - actualDeleteCount; k++) {
7240 from = k + actualDeleteCount;
7241 to = k + insertCount;
7242 if (from in O) O[to] = O[from];
7245 for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
7246 } else if (insertCount > actualDeleteCount) {
7247 for (k = len - actualDeleteCount; k > actualStart; k--) {
7248 from = k + actualDeleteCount - 1;
7249 to = k + insertCount - 1;
7250 if (from in O) O[to] = O[from];
7254 for (k = 0; k < insertCount; k++) {
7255 O[k + actualStart] = arguments[k + 2];
7257 O.length = len - actualDeleteCount + insertCount;
7262 // `Symbol.toStringTag` well-known symbol
7263 // https://tc39.es/ecma262/#sec-symbol.tostringtag
7264 defineWellKnownSymbol('toStringTag');
7266 // JSON[@@toStringTag] property
7267 // https://tc39.es/ecma262/#sec-json-@@tostringtag
7268 setToStringTag(global$2.JSON, 'JSON', true);
7270 // Math[@@toStringTag] property
7271 // https://tc39.es/ecma262/#sec-math-@@tostringtag
7272 setToStringTag(Math, 'Math', true);
7274 (function (factory) {
7278 function _classCallCheck(instance, Constructor) {
7279 if (!(instance instanceof Constructor)) {
7280 throw new TypeError("Cannot call a class as a function");
7284 function _defineProperties(target, props) {
7285 for (var i = 0; i < props.length; i++) {
7286 var descriptor = props[i];
7287 descriptor.enumerable = descriptor.enumerable || false;
7288 descriptor.configurable = true;
7289 if ("value" in descriptor) descriptor.writable = true;
7290 Object.defineProperty(target, descriptor.key, descriptor);
7294 function _createClass(Constructor, protoProps, staticProps) {
7295 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7296 if (staticProps) _defineProperties(Constructor, staticProps);
7300 function _inherits(subClass, superClass) {
7301 if (typeof superClass !== "function" && superClass !== null) {
7302 throw new TypeError("Super expression must either be null or a function");
7305 subClass.prototype = Object.create(superClass && superClass.prototype, {
7312 if (superClass) _setPrototypeOf(subClass, superClass);
7315 function _getPrototypeOf(o) {
7316 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7317 return o.__proto__ || Object.getPrototypeOf(o);
7319 return _getPrototypeOf(o);
7322 function _setPrototypeOf(o, p) {
7323 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7328 return _setPrototypeOf(o, p);
7331 function _isNativeReflectConstruct() {
7332 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7333 if (Reflect.construct.sham) return false;
7334 if (typeof Proxy === "function") return true;
7337 Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
7344 function _assertThisInitialized(self) {
7345 if (self === void 0) {
7346 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7352 function _possibleConstructorReturn(self, call) {
7353 if (call && (_typeof(call) === "object" || typeof call === "function")) {
7357 return _assertThisInitialized(self);
7360 function _createSuper(Derived) {
7361 var hasNativeReflectConstruct = _isNativeReflectConstruct();
7363 return function _createSuperInternal() {
7364 var Super = _getPrototypeOf(Derived),
7367 if (hasNativeReflectConstruct) {
7368 var NewTarget = _getPrototypeOf(this).constructor;
7370 result = Reflect.construct(Super, arguments, NewTarget);
7372 result = Super.apply(this, arguments);
7375 return _possibleConstructorReturn(this, result);
7379 function _superPropBase(object, property) {
7380 while (!Object.prototype.hasOwnProperty.call(object, property)) {
7381 object = _getPrototypeOf(object);
7382 if (object === null) break;
7388 function _get(target, property, receiver) {
7389 if (typeof Reflect !== "undefined" && Reflect.get) {
7392 _get = function _get(target, property, receiver) {
7393 var base = _superPropBase(target, property);
7396 var desc = Object.getOwnPropertyDescriptor(base, property);
7399 return desc.get.call(receiver);
7406 return _get(target, property, receiver || target);
7409 var Emitter = /*#__PURE__*/function () {
7410 function Emitter() {
7411 _classCallCheck(this, Emitter);
7413 Object.defineProperty(this, 'listeners', {
7420 _createClass(Emitter, [{
7421 key: "addEventListener",
7422 value: function addEventListener(type, callback, options) {
7423 if (!(type in this.listeners)) {
7424 this.listeners[type] = [];
7427 this.listeners[type].push({
7433 key: "removeEventListener",
7434 value: function removeEventListener(type, callback) {
7435 if (!(type in this.listeners)) {
7439 var stack = this.listeners[type];
7441 for (var i = 0, l = stack.length; i < l; i++) {
7442 if (stack[i].callback === callback) {
7449 key: "dispatchEvent",
7450 value: function dispatchEvent(event) {
7451 if (!(event.type in this.listeners)) {
7455 var stack = this.listeners[event.type];
7456 var stackToCall = stack.slice();
7458 for (var i = 0, l = stackToCall.length; i < l; i++) {
7459 var listener = stackToCall[i];
7462 listener.callback.call(this, event);
7464 Promise.resolve().then(function () {
7469 if (listener.options && listener.options.once) {
7470 this.removeEventListener(event.type, listener.callback);
7474 return !event.defaultPrevented;
7481 var AbortSignal = /*#__PURE__*/function (_Emitter) {
7482 _inherits(AbortSignal, _Emitter);
7484 var _super = _createSuper(AbortSignal);
7486 function AbortSignal() {
7489 _classCallCheck(this, AbortSignal);
7491 _this = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7492 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7493 // the parent constructor directly instead as a workaround. For general details, see babel bug:
7494 // https://github.com/babel/babel/issues/3041
7495 // This hack was added as a fix for the issue described here:
7496 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7498 if (!_this.listeners) {
7499 Emitter.call(_assertThisInitialized(_this));
7500 } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7501 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7504 Object.defineProperty(_assertThisInitialized(_this), 'aborted', {
7509 Object.defineProperty(_assertThisInitialized(_this), 'onabort', {
7517 _createClass(AbortSignal, [{
7519 value: function toString() {
7520 return '[object AbortSignal]';
7523 key: "dispatchEvent",
7524 value: function dispatchEvent(event) {
7525 if (event.type === 'abort') {
7526 this.aborted = true;
7528 if (typeof this.onabort === 'function') {
7529 this.onabort.call(this, event);
7533 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7540 var AbortController = /*#__PURE__*/function () {
7541 function AbortController() {
7542 _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7543 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7546 Object.defineProperty(this, 'signal', {
7547 value: new AbortSignal(),
7553 _createClass(AbortController, [{
7555 value: function abort() {
7559 event = new Event('abort');
7561 if (typeof document !== 'undefined') {
7562 if (!document.createEvent) {
7563 // For Internet Explorer 8:
7564 event = document.createEventObject();
7565 event.type = 'abort';
7567 // For Internet Explorer 11:
7568 event = document.createEvent('Event');
7569 event.initEvent('abort', false, false);
7572 // Fallback where document isn't available:
7581 this.signal.dispatchEvent(event);
7585 value: function toString() {
7586 return '[object AbortController]';
7590 return AbortController;
7593 if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7594 // These are necessary to make sure that we get correct output for:
7595 // Object.prototype.toString.call(new AbortController())
7596 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7597 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7600 function polyfillNeeded(self) {
7601 if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7602 console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7604 } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7605 // defining window.Request, and this polyfill need to work on top of unfetch
7606 // so the below feature detection needs the !self.AbortController part.
7607 // The Request.prototype check is also needed because Safari versions 11.1.2
7608 // up to and including 12.1.x has a window.AbortController present but still
7609 // does NOT correctly implement abortable fetch:
7610 // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7613 return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7616 * Note: the "fetch.Request" default value is available for fetch imported from
7617 * the "node-fetch" package and not in browsers. This is OK since browsers
7618 * will be importing umd-polyfill.js from that path "self" is passed the
7619 * decorator so the default value will not be used (because browsers that define
7620 * fetch also has Request). One quirky setup where self.fetch exists but
7621 * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7622 * on top of IE11; for this case the browser will try to use the fetch.Request
7623 * default value which in turn will be undefined but then then "if (Request)"
7624 * will ensure that you get a patched fetch but still no Request (as expected).
7625 * @param {fetch, Request = fetch.Request}
7626 * @returns {fetch: abortableFetch, Request: AbortableRequest}
7630 function abortableFetchDecorator(patchTargets) {
7631 if ('function' === typeof patchTargets) {
7637 var _patchTargets = patchTargets,
7638 fetch = _patchTargets.fetch,
7639 _patchTargets$Request = _patchTargets.Request,
7640 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7641 NativeAbortController = _patchTargets.AbortController,
7642 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7643 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7645 if (!polyfillNeeded({
7647 Request: NativeRequest,
7648 AbortController: NativeAbortController,
7649 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7657 var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7658 // defining window.Request, and this polyfill need to work on top of unfetch
7659 // hence we only patch it if it's available. Also we don't patch it if signal
7660 // is already available on the Request prototype because in this case support
7661 // is present and the patching below can cause a crash since it assigns to
7662 // request.signal which is technically a read-only property. This latter error
7663 // happens when you run the main5.js node-fetch example in the repo
7664 // "abortcontroller-polyfill-examples". The exact error is:
7665 // request.signal = init.signal;
7667 // TypeError: Cannot set property signal of #<Request> which has only a getter
7669 if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7670 Request = function Request(input, init) {
7673 if (init && init.signal) {
7674 signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7675 // been installed because if we're running on top of a browser with a
7676 // working native AbortController (i.e. the polyfill was installed due to
7677 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7678 // fake AbortSignal to the native fetch will trigger:
7679 // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7684 var request = new NativeRequest(input, init);
7687 Object.defineProperty(request, 'signal', {
7698 Request.prototype = NativeRequest.prototype;
7701 var realFetch = fetch;
7703 var abortableFetch = function abortableFetch(input, init) {
7704 var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7710 abortError = new DOMException('Aborted', 'AbortError');
7712 // IE 11 does not support calling the DOMException constructor, use a
7713 // regular error object on it instead.
7714 abortError = new Error('Aborted');
7715 abortError.name = 'AbortError';
7716 } // Return early if already aborted, thus avoiding making an HTTP request
7719 if (signal.aborted) {
7720 return Promise.reject(abortError);
7721 } // Turn an event into a promise, reject it once `abort` is dispatched
7724 var cancellation = new Promise(function (_, reject) {
7725 signal.addEventListener('abort', function () {
7726 return reject(abortError);
7732 if (init && init.signal) {
7733 // Never pass .signal to the native implementation when the polyfill has
7734 // been installed because if we're running on top of a browser with a
7735 // working native AbortController (i.e. the polyfill was installed due to
7736 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7737 // fake AbortSignal to the native fetch will trigger:
7738 // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7740 } // Return the fastest promise (don't need to wait for request to finish)
7743 return Promise.race([cancellation, realFetch(input, init)]);
7746 return realFetch(input, init);
7750 fetch: abortableFetch,
7756 if (!polyfillNeeded(self)) {
7761 console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7765 var _abortableFetch = abortableFetchDecorator(self),
7766 fetch = _abortableFetch.fetch,
7767 Request = _abortableFetch.Request;
7770 self.Request = Request;
7771 Object.defineProperty(self, 'AbortController', {
7775 value: AbortController
7777 Object.defineProperty(self, 'AbortSignal', {
7783 })(typeof self !== 'undefined' ? self : commonjsGlobal);
7786 function actionAddEntity(way) {
7787 return function (graph) {
7788 return graph.replace(way);
7792 var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7793 var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
7794 var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7796 // We can't use this feature detection in V8 since it causes
7797 // deoptimization and serious performance degradation
7798 // https://github.com/zloirock/core-js/issues/679
7799 var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7801 array[IS_CONCAT_SPREADABLE] = false;
7802 return array.concat()[0] !== array;
7805 var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7807 var isConcatSpreadable = function (O) {
7808 if (!isObject$4(O)) return false;
7809 var spreadable = O[IS_CONCAT_SPREADABLE];
7810 return spreadable !== undefined ? !!spreadable : isArray(O);
7813 var FORCED$8 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7815 // `Array.prototype.concat` method
7816 // https://tc39.es/ecma262/#sec-array.prototype.concat
7817 // with adding support of @@isConcatSpreadable and @@species
7818 _export({ target: 'Array', proto: true, forced: FORCED$8 }, {
7819 // eslint-disable-next-line no-unused-vars -- required for `.length`
7820 concat: function concat(arg) {
7821 var O = toObject(this);
7822 var A = arraySpeciesCreate(O, 0);
7824 var i, k, length, len, E;
7825 for (i = -1, length = arguments.length; i < length; i++) {
7826 E = i === -1 ? O : arguments[i];
7827 if (isConcatSpreadable(E)) {
7828 len = toLength(E.length);
7829 if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7830 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7832 if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7833 createProperty(A, n++, E);
7841 // `Object.assign` method
7842 // https://tc39.es/ecma262/#sec-object.assign
7843 // eslint-disable-next-line es/no-object-assign -- required for testing
7844 _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7845 assign: objectAssign
7848 var $filter = arrayIteration.filter;
7851 var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('filter');
7853 // `Array.prototype.filter` method
7854 // https://tc39.es/ecma262/#sec-array.prototype.filter
7855 // with adding support of @@species
7856 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, {
7857 filter: function filter(callbackfn /* , thisArg */) {
7858 return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7862 var FAILS_ON_PRIMITIVES$1 = fails(function () { objectKeys(1); });
7864 // `Object.keys` method
7865 // https://tc39.es/ecma262/#sec-object.keys
7866 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1 }, {
7867 keys: function keys(it) {
7868 return objectKeys(toObject(it));
7872 var nativeReverse = [].reverse;
7873 var test$1 = [1, 2];
7875 // `Array.prototype.reverse` method
7876 // https://tc39.es/ecma262/#sec-array.prototype.reverse
7877 // fix for Safari 12.0 bug
7878 // https://bugs.webkit.org/show_bug.cgi?id=188794
7879 _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7880 reverse: function reverse() {
7881 // eslint-disable-next-line no-self-assign -- dirty hack
7882 if (isArray(this)) this.length = this.length;
7883 return nativeReverse.call(this);
7887 var trim$4 = stringTrim.trim;
7890 var $parseFloat = global$2.parseFloat;
7891 var FORCED$7 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7893 // `parseFloat` method
7894 // https://tc39.es/ecma262/#sec-parsefloat-string
7895 var numberParseFloat = FORCED$7 ? function parseFloat(string) {
7896 var trimmedString = trim$4(String(string));
7897 var result = $parseFloat(trimmedString);
7898 return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7901 // `parseFloat` method
7902 // https://tc39.es/ecma262/#sec-parsefloat-string
7903 _export({ global: true, forced: parseFloat != numberParseFloat }, {
7904 parseFloat: numberParseFloat
7908 Order the nodes of a way in reverse order and reverse any direction dependent tags
7909 other than `oneway`. (We assume that correcting a backwards oneway is the primary
7910 reason for reversing a way.)
7912 In addition, numeric-valued `incline` tags are negated.
7914 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7915 or adjusted tags that don't seem to be used in practice were omitted.
7918 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7919 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7920 http://wiki.openstreetmap.org/wiki/Key:incline
7921 http://wiki.openstreetmap.org/wiki/Route#Members
7922 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7923 http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7924 http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7926 function actionReverse(entityID, options) {
7927 var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7928 var numeric = /^([+\-]?)(?=[\d.])/;
7929 var directionKey = /direction$/;
7930 var turn_lanes = /^turn:lanes:?/;
7931 var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7932 var valueReplacements = {
7937 forward: 'backward',
7938 backward: 'forward',
7939 forwards: 'backward',
7940 backwards: 'forward'
7942 var roleReplacements = {
7943 forward: 'backward',
7944 backward: 'forward',
7945 forwards: 'backward',
7946 backwards: 'forward'
7948 var onewayReplacements = {
7953 var compassReplacements = {
7972 function reverseKey(key) {
7973 for (var i = 0; i < keyReplacements.length; ++i) {
7974 var replacement = keyReplacements[i];
7976 if (replacement[0].test(key)) {
7977 return key.replace(replacement[0], replacement[1]);
7984 function reverseValue(key, value, includeAbsolute) {
7985 if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7987 if (turn_lanes.test(key)) {
7989 } else if (key === 'incline' && numeric.test(value)) {
7990 return value.replace(numeric, function (_, sign) {
7991 return sign === '-' ? '' : '-';
7993 } else if (options && options.reverseOneway && key === 'oneway') {
7994 return onewayReplacements[value] || value;
7995 } else if (includeAbsolute && directionKey.test(key)) {
7996 if (compassReplacements[value]) return compassReplacements[value];
7997 var degrees = parseFloat(value);
7999 if (typeof degrees === 'number' && !isNaN(degrees)) {
8000 if (degrees < 180) {
8006 return degrees.toString();
8010 return valueReplacements[value] || value;
8011 } // Reverse the direction of tags attached to the nodes - #3076
8014 function reverseNodeTags(graph, nodeIDs) {
8015 for (var i = 0; i < nodeIDs.length; i++) {
8016 var node = graph.hasEntity(nodeIDs[i]);
8017 if (!node || !Object.keys(node.tags).length) continue;
8020 for (var key in node.tags) {
8021 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
8024 graph = graph.replace(node.update({
8032 function reverseWay(graph, way) {
8033 var nodes = way.nodes.slice().reverse();
8037 for (var key in way.tags) {
8038 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
8041 graph.parentRelations(way).forEach(function (relation) {
8042 relation.members.forEach(function (member, index) {
8043 if (member.id === way.id && (role = roleReplacements[member.role])) {
8044 relation = relation.updateMember({
8047 graph = graph.replace(relation);
8050 }); // Reverse any associated directions on nodes on the way and then replace
8051 // the way itself with the reversed node ids and updated way tags
8053 return reverseNodeTags(graph, nodes).replace(way.update({
8059 var action = function action(graph) {
8060 var entity = graph.entity(entityID);
8062 if (entity.type === 'way') {
8063 return reverseWay(graph, entity);
8066 return reverseNodeTags(graph, [entityID]);
8069 action.disabled = function (graph) {
8070 var entity = graph.hasEntity(entityID);
8071 if (!entity || entity.type === 'way') return false;
8073 for (var key in entity.tags) {
8074 var value = entity.tags[key];
8076 if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
8081 return 'nondirectional_node';
8084 action.entityID = function () {
8091 function osmIsInterestingTag(key) {
8092 return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
8093 key.indexOf('tiger:') !== 0;
8095 var osmAreaKeys = {};
8096 function osmSetAreaKeys(value) {
8097 osmAreaKeys = value;
8098 } // returns an object with the tag from `tags` that implies an area geometry, if any
8100 function osmTagSuggestingArea(tags) {
8101 if (tags.area === 'yes') return {
8104 if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
8105 // are a few exceptions that should be treated as areas, even in the
8106 // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
8121 var returnTags = {};
8123 for (var key in tags) {
8124 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
8125 returnTags[key] = tags[key];
8129 if (key in lineKeys && tags[key] in lineKeys[key]) {
8130 returnTags[key] = tags[key];
8136 } // Tags that indicate a node can be a standalone point
8137 // e.g. { amenity: { bar: true, parking: true, ... } ... }
8139 var osmPointTags = {};
8140 function osmSetPointTags(value) {
8141 osmPointTags = value;
8142 } // Tags that indicate a node can be part of a way
8143 // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
8145 var osmVertexTags = {};
8146 function osmSetVertexTags(value) {
8147 osmVertexTags = value;
8149 function osmNodeGeometriesForTags(nodeTags) {
8150 var geometries = {};
8152 for (var key in nodeTags) {
8153 if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
8154 geometries.point = true;
8157 if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
8158 geometries.vertex = true;
8159 } // break early if both are already supported
8162 if (geometries.point && geometries.vertex) break;
8167 var osmOneWayTags = {
8172 'magic_carpet': true,
8187 'goods_conveyor': true,
8188 'piste:halfpipe': true
8202 'tidal_channel': true
8204 }; // solid and smooth surfaces akin to the assumed default road surface in OSM
8206 var osmPavedTags = {
8211 'concrete:lanes': true,
8212 'concrete:plates': true
8217 }; // solid, if somewhat uncommon surfaces with a high range of smoothness
8219 var osmSemipavedTags = {
8221 'cobblestone': true,
8222 'cobblestone:flattened': true,
8223 'unhewn_cobblestone': true,
8225 'paving_stones': true,
8230 var osmRightSideIsInsideTags = {
8233 'coastline': 'coastline'
8236 'retaining_wall': true,
8247 }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8248 // (does not include `raceway`)
8250 var osmRoutableHighwayTagValues = {
8257 motorway_link: true,
8260 secondary_link: true,
8261 tertiary_link: true,
8266 living_street: true,
8275 }; // "highway" tag values that generally do not allow motor vehicles
8277 var osmPathHighwayTagValues = {
8285 }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8287 var osmRailwayTrackTagValues = {
8298 }; // "waterway" tag values for line features representing water flow
8300 var osmFlowingWaterwayTagValues = {
8310 var trim$3 = stringTrim.trim;
8313 var $parseInt = global$2.parseInt;
8314 var hex$2 = /^[+-]?0[Xx]/;
8315 var FORCED$6 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8317 // `parseInt` method
8318 // https://tc39.es/ecma262/#sec-parseint-string-radix
8319 var numberParseInt = FORCED$6 ? function parseInt(string, radix) {
8320 var S = trim$3(String(string));
8321 return $parseInt(S, (radix >>> 0) || (hex$2.test(S) ? 16 : 10));
8324 // `parseInt` method
8325 // https://tc39.es/ecma262/#sec-parseint-string-radix
8326 _export({ global: true, forced: parseInt != numberParseInt }, {
8327 parseInt: numberParseInt
8330 var freezing = !fails(function () {
8331 // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing
8332 return Object.isExtensible(Object.preventExtensions({}));
8335 var internalMetadata = createCommonjsModule(function (module) {
8336 var defineProperty = objectDefineProperty.f;
8340 var METADATA = uid('meta');
8343 // eslint-disable-next-line es/no-object-isextensible -- safe
8344 var isExtensible = Object.isExtensible || function () {
8348 var setMetadata = function (it) {
8349 defineProperty(it, METADATA, { value: {
8350 objectID: 'O' + ++id, // object ID
8351 weakData: {} // weak collections IDs
8355 var fastKey = function (it, create) {
8356 // return a primitive with prefix
8357 if (!isObject$4(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8358 if (!has$1(it, METADATA)) {
8359 // can't set metadata to uncaught frozen object
8360 if (!isExtensible(it)) return 'F';
8361 // not necessary to add metadata
8362 if (!create) return 'E';
8363 // add missing metadata
8366 } return it[METADATA].objectID;
8369 var getWeakData = function (it, create) {
8370 if (!has$1(it, METADATA)) {
8371 // can't set metadata to uncaught frozen object
8372 if (!isExtensible(it)) return true;
8373 // not necessary to add metadata
8374 if (!create) return false;
8375 // add missing metadata
8377 // return the store of weak collections IDs
8378 } return it[METADATA].weakData;
8381 // add metadata on freeze-family methods calling
8382 var onFreeze = function (it) {
8383 if (freezing && meta.REQUIRED && isExtensible(it) && !has$1(it, METADATA)) setMetadata(it);
8387 var meta = module.exports = {
8390 getWeakData: getWeakData,
8394 hiddenKeys$1[METADATA] = true;
8397 var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8398 var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8399 var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8400 var ADDER = IS_MAP ? 'set' : 'add';
8401 var NativeConstructor = global$2[CONSTRUCTOR_NAME];
8402 var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8403 var Constructor = NativeConstructor;
8406 var fixMethod = function (KEY) {
8407 var nativeMethod = NativePrototype[KEY];
8408 redefine(NativePrototype, KEY,
8409 KEY == 'add' ? function add(value) {
8410 nativeMethod.call(this, value === 0 ? 0 : value);
8412 } : KEY == 'delete' ? function (key) {
8413 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8414 } : KEY == 'get' ? function get(key) {
8415 return IS_WEAK && !isObject$4(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8416 } : KEY == 'has' ? function has(key) {
8417 return IS_WEAK && !isObject$4(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8418 } : function set(key, value) {
8419 nativeMethod.call(this, key === 0 ? 0 : key, value);
8425 var REPLACE = isForced_1(
8427 typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8428 new NativeConstructor().entries().next();
8433 // create collection constructor
8434 Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8435 internalMetadata.REQUIRED = true;
8436 } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8437 var instance = new Constructor();
8438 // early implementations not supports chaining
8439 var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8440 // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8441 var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8442 // most early implementations doesn't supports iterables, most modern - not close it correctly
8443 // eslint-disable-next-line no-new -- required for testing
8444 var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8445 // for early implementations -0 and +0 not the same
8446 var BUGGY_ZERO = !IS_WEAK && fails(function () {
8447 // V8 ~ Chromium 42- fails only with 5+ elements
8448 var $instance = new NativeConstructor();
8450 while (index--) $instance[ADDER](index, index);
8451 return !$instance.has(-0);
8454 if (!ACCEPT_ITERABLES) {
8455 Constructor = wrapper(function (dummy, iterable) {
8456 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8457 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8458 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8461 Constructor.prototype = NativePrototype;
8462 NativePrototype.constructor = Constructor;
8465 if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8466 fixMethod('delete');
8468 IS_MAP && fixMethod('get');
8471 if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8473 // weak collections should not contains .clear method
8474 if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8477 exported[CONSTRUCTOR_NAME] = Constructor;
8478 _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8480 setToStringTag(Constructor, CONSTRUCTOR_NAME);
8482 if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8487 var defineProperty$2 = objectDefineProperty.f;
8496 var fastKey = internalMetadata.fastKey;
8499 var setInternalState = internalState.set;
8500 var internalStateGetterFor = internalState.getterFor;
8502 var collectionStrong = {
8503 getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8504 var C = wrapper(function (that, iterable) {
8505 anInstance(that, C, CONSTRUCTOR_NAME);
8506 setInternalState(that, {
8507 type: CONSTRUCTOR_NAME,
8508 index: objectCreate(null),
8513 if (!descriptors) that.size = 0;
8514 if (iterable != undefined) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP });
8517 var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8519 var define = function (that, key, value) {
8520 var state = getInternalState(that);
8521 var entry = getEntry(that, key);
8522 var previous, index;
8523 // change existing entry
8525 entry.value = value;
8528 state.last = entry = {
8529 index: index = fastKey(key, true),
8532 previous: previous = state.last,
8536 if (!state.first) state.first = entry;
8537 if (previous) previous.next = entry;
8538 if (descriptors) state.size++;
8541 if (index !== 'F') state.index[index] = entry;
8545 var getEntry = function (that, key) {
8546 var state = getInternalState(that);
8548 var index = fastKey(key);
8550 if (index !== 'F') return state.index[index];
8551 // frozen object case
8552 for (entry = state.first; entry; entry = entry.next) {
8553 if (entry.key == key) return entry;
8557 redefineAll(C.prototype, {
8558 // `{ Map, Set }.prototype.clear()` methods
8559 // https://tc39.es/ecma262/#sec-map.prototype.clear
8560 // https://tc39.es/ecma262/#sec-set.prototype.clear
8561 clear: function clear() {
8563 var state = getInternalState(that);
8564 var data = state.index;
8565 var entry = state.first;
8567 entry.removed = true;
8568 if (entry.previous) entry.previous = entry.previous.next = undefined;
8569 delete data[entry.index];
8572 state.first = state.last = undefined;
8573 if (descriptors) state.size = 0;
8576 // `{ Map, Set }.prototype.delete(key)` methods
8577 // https://tc39.es/ecma262/#sec-map.prototype.delete
8578 // https://tc39.es/ecma262/#sec-set.prototype.delete
8579 'delete': function (key) {
8581 var state = getInternalState(that);
8582 var entry = getEntry(that, key);
8584 var next = entry.next;
8585 var prev = entry.previous;
8586 delete state.index[entry.index];
8587 entry.removed = true;
8588 if (prev) prev.next = next;
8589 if (next) next.previous = prev;
8590 if (state.first == entry) state.first = next;
8591 if (state.last == entry) state.last = prev;
8592 if (descriptors) state.size--;
8596 // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods
8597 // https://tc39.es/ecma262/#sec-map.prototype.foreach
8598 // https://tc39.es/ecma262/#sec-set.prototype.foreach
8599 forEach: function forEach(callbackfn /* , that = undefined */) {
8600 var state = getInternalState(this);
8601 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8603 while (entry = entry ? entry.next : state.first) {
8604 boundFunction(entry.value, entry.key, this);
8605 // revert to the last existing entry
8606 while (entry && entry.removed) entry = entry.previous;
8609 // `{ Map, Set}.prototype.has(key)` methods
8610 // https://tc39.es/ecma262/#sec-map.prototype.has
8611 // https://tc39.es/ecma262/#sec-set.prototype.has
8612 has: function has(key) {
8613 return !!getEntry(this, key);
8617 redefineAll(C.prototype, IS_MAP ? {
8618 // `Map.prototype.get(key)` method
8619 // https://tc39.es/ecma262/#sec-map.prototype.get
8620 get: function get(key) {
8621 var entry = getEntry(this, key);
8622 return entry && entry.value;
8624 // `Map.prototype.set(key, value)` method
8625 // https://tc39.es/ecma262/#sec-map.prototype.set
8626 set: function set(key, value) {
8627 return define(this, key === 0 ? 0 : key, value);
8630 // `Set.prototype.add(value)` method
8631 // https://tc39.es/ecma262/#sec-set.prototype.add
8632 add: function add(value) {
8633 return define(this, value = value === 0 ? 0 : value, value);
8636 if (descriptors) defineProperty$2(C.prototype, 'size', {
8638 return getInternalState(this).size;
8643 setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8644 var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8645 var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8646 var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8647 // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods
8648 // https://tc39.es/ecma262/#sec-map.prototype.entries
8649 // https://tc39.es/ecma262/#sec-map.prototype.keys
8650 // https://tc39.es/ecma262/#sec-map.prototype.values
8651 // https://tc39.es/ecma262/#sec-map.prototype-@@iterator
8652 // https://tc39.es/ecma262/#sec-set.prototype.entries
8653 // https://tc39.es/ecma262/#sec-set.prototype.keys
8654 // https://tc39.es/ecma262/#sec-set.prototype.values
8655 // https://tc39.es/ecma262/#sec-set.prototype-@@iterator
8656 defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8657 setInternalState(this, {
8658 type: ITERATOR_NAME,
8660 state: getInternalCollectionState(iterated),
8665 var state = getInternalIteratorState(this);
8666 var kind = state.kind;
8667 var entry = state.last;
8668 // revert to the last existing entry
8669 while (entry && entry.removed) entry = entry.previous;
8671 if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8672 // or finish the iteration
8673 state.target = undefined;
8674 return { value: undefined, done: true };
8676 // return step by kind
8677 if (kind == 'keys') return { value: entry.key, done: false };
8678 if (kind == 'values') return { value: entry.value, done: false };
8679 return { value: [entry.key, entry.value], done: false };
8680 }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8682 // `{ Map, Set }.prototype[@@species]` accessors
8683 // https://tc39.es/ecma262/#sec-get-map-@@species
8684 // https://tc39.es/ecma262/#sec-get-set-@@species
8685 setSpecies(CONSTRUCTOR_NAME);
8689 // `Set` constructor
8690 // https://tc39.es/ecma262/#sec-set-objects
8691 collection('Set', function (init) {
8692 return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8693 }, collectionStrong);
8695 function d3_ascending (a, b) {
8696 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8699 function d3_bisector (f) {
8703 if (f.length === 1) {
8704 delta = function delta(d, x) {
8708 compare = ascendingComparator(f);
8711 function left(a, x, lo, hi) {
8712 if (lo == null) lo = 0;
8713 if (hi == null) hi = a.length;
8716 var mid = lo + hi >>> 1;
8717 if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8723 function right(a, x, lo, hi) {
8724 if (lo == null) lo = 0;
8725 if (hi == null) hi = a.length;
8728 var mid = lo + hi >>> 1;
8729 if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8735 function center(a, x, lo, hi) {
8736 if (lo == null) lo = 0;
8737 if (hi == null) hi = a.length;
8738 var i = left(a, x, lo, hi - 1);
8739 return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8749 function ascendingComparator(f) {
8750 return function (d, x) {
8751 return d3_ascending(f(d), x);
8755 // `Symbol.asyncIterator` well-known symbol
8756 // https://tc39.es/ecma262/#sec-symbol.asynciterator
8757 defineWellKnownSymbol('asyncIterator');
8759 createCommonjsModule(function (module) {
8760 var runtime = function (exports) {
8762 var Op = Object.prototype;
8763 var hasOwn = Op.hasOwnProperty;
8764 var undefined$1; // More compressible than void 0.
8766 var $Symbol = typeof Symbol === "function" ? Symbol : {};
8767 var iteratorSymbol = $Symbol.iterator || "@@iterator";
8768 var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8769 var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8771 function define(obj, key, value) {
8772 Object.defineProperty(obj, key, {
8782 // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8785 define = function define(obj, key, value) {
8786 return obj[key] = value;
8790 function wrap(innerFn, outerFn, self, tryLocsList) {
8791 // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8792 var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8793 var generator = Object.create(protoGenerator.prototype);
8794 var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8795 // .throw, and .return methods.
8797 generator._invoke = makeInvokeMethod(innerFn, self, context);
8801 exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8802 // record like context.tryEntries[i].completion. This interface could
8803 // have been (and was previously) designed to take a closure to be
8804 // invoked without arguments, but in all the cases we care about we
8805 // already have an existing method we want to call, so there's no need
8806 // to create a new function object. We can even get away with assuming
8807 // the method takes exactly one argument, since that happens to be true
8808 // in every case, so we don't have to touch the arguments object. The
8809 // only additional allocation required is the completion record, which
8810 // has a stable shape and so hopefully should be cheap to allocate.
8812 function tryCatch(fn, obj, arg) {
8816 arg: fn.call(obj, arg)
8826 var GenStateSuspendedStart = "suspendedStart";
8827 var GenStateSuspendedYield = "suspendedYield";
8828 var GenStateExecuting = "executing";
8829 var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8830 // breaking out of the dispatch switch statement.
8832 var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8833 // .constructor.prototype properties for functions that return Generator
8834 // objects. For full spec compliance, you may wish to configure your
8835 // minifier not to mangle the names of these two functions.
8837 function Generator() {}
8839 function GeneratorFunction() {}
8841 function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8842 // don't natively support it.
8845 var IteratorPrototype = {};
8847 IteratorPrototype[iteratorSymbol] = function () {
8851 var getProto = Object.getPrototypeOf;
8852 var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8854 if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8855 // This environment has a native %IteratorPrototype%; use it instead
8857 IteratorPrototype = NativeIteratorPrototype;
8860 var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8861 GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8862 GeneratorFunctionPrototype.constructor = GeneratorFunction;
8863 GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8864 // Iterator interface in terms of a single ._invoke method.
8866 function defineIteratorMethods(prototype) {
8867 ["next", "throw", "return"].forEach(function (method) {
8868 define(prototype, method, function (arg) {
8869 return this._invoke(method, arg);
8874 exports.isGeneratorFunction = function (genFun) {
8875 var ctor = typeof genFun === "function" && genFun.constructor;
8876 return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8877 // do is to check its .name property.
8878 (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8881 exports.mark = function (genFun) {
8882 if (Object.setPrototypeOf) {
8883 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8885 genFun.__proto__ = GeneratorFunctionPrototype;
8886 define(genFun, toStringTagSymbol, "GeneratorFunction");
8889 genFun.prototype = Object.create(Gp);
8891 }; // Within the body of any async function, `await x` is transformed to
8892 // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8893 // `hasOwn.call(value, "__await")` to determine if the yielded value is
8894 // meant to be awaited.
8897 exports.awrap = function (arg) {
8903 function AsyncIterator(generator, PromiseImpl) {
8904 function invoke(method, arg, resolve, reject) {
8905 var record = tryCatch(generator[method], generator, arg);
8907 if (record.type === "throw") {
8910 var result = record.arg;
8911 var value = result.value;
8913 if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8914 return PromiseImpl.resolve(value.__await).then(function (value) {
8915 invoke("next", value, resolve, reject);
8917 invoke("throw", err, resolve, reject);
8921 return PromiseImpl.resolve(value).then(function (unwrapped) {
8922 // When a yielded Promise is resolved, its final value becomes
8923 // the .value of the Promise<{value,done}> result for the
8924 // current iteration.
8925 result.value = unwrapped;
8927 }, function (error) {
8928 // If a rejected Promise was yielded, throw the rejection back
8929 // into the async generator function so it can be handled there.
8930 return invoke("throw", error, resolve, reject);
8935 var previousPromise;
8937 function enqueue(method, arg) {
8938 function callInvokeWithMethodAndArg() {
8939 return new PromiseImpl(function (resolve, reject) {
8940 invoke(method, arg, resolve, reject);
8944 return previousPromise = // If enqueue has been called before, then we want to wait until
8945 // all previous Promises have been resolved before calling invoke,
8946 // so that results are always delivered in the correct order. If
8947 // enqueue has not been called before, then it is important to
8948 // call invoke immediately, without waiting on a callback to fire,
8949 // so that the async generator function has the opportunity to do
8950 // any necessary setup in a predictable way. This predictability
8951 // is why the Promise constructor synchronously invokes its
8952 // executor callback, and why async functions synchronously
8953 // execute code before the first await. Since we implement simple
8954 // async functions in terms of async generators, it is especially
8955 // important to get this right, even though it requires care.
8956 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8957 // invocations of the iterator.
8958 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8959 } // Define the unified helper method that is used to implement .next,
8960 // .throw, and .return (see defineIteratorMethods).
8963 this._invoke = enqueue;
8966 defineIteratorMethods(AsyncIterator.prototype);
8968 AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8972 exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8973 // AsyncIterator objects; they just return a Promise for the value of
8974 // the final result produced by the iterator.
8976 exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8977 if (PromiseImpl === void 0) PromiseImpl = Promise;
8978 var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8979 return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8980 : iter.next().then(function (result) {
8981 return result.done ? result.value : iter.next();
8985 function makeInvokeMethod(innerFn, self, context) {
8986 var state = GenStateSuspendedStart;
8987 return function invoke(method, arg) {
8988 if (state === GenStateExecuting) {
8989 throw new Error("Generator is already running");
8992 if (state === GenStateCompleted) {
8993 if (method === "throw") {
8995 } // Be forgiving, per 25.3.3.3.3 of the spec:
8996 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
8999 return doneResult();
9002 context.method = method;
9006 var delegate = context.delegate;
9009 var delegateResult = maybeInvokeDelegate(delegate, context);
9011 if (delegateResult) {
9012 if (delegateResult === ContinueSentinel) continue;
9013 return delegateResult;
9017 if (context.method === "next") {
9018 // Setting context._sent for legacy support of Babel's
9019 // function.sent implementation.
9020 context.sent = context._sent = context.arg;
9021 } else if (context.method === "throw") {
9022 if (state === GenStateSuspendedStart) {
9023 state = GenStateCompleted;
9027 context.dispatchException(context.arg);
9028 } else if (context.method === "return") {
9029 context.abrupt("return", context.arg);
9032 state = GenStateExecuting;
9033 var record = tryCatch(innerFn, self, context);
9035 if (record.type === "normal") {
9036 // If an exception is thrown from innerFn, we leave state ===
9037 // GenStateExecuting and loop back for another invocation.
9038 state = context.done ? GenStateCompleted : GenStateSuspendedYield;
9040 if (record.arg === ContinueSentinel) {
9048 } else if (record.type === "throw") {
9049 state = GenStateCompleted; // Dispatch the exception by looping back around to the
9050 // context.dispatchException(context.arg) call above.
9052 context.method = "throw";
9053 context.arg = record.arg;
9057 } // Call delegate.iterator[context.method](context.arg) and handle the
9058 // result, either by returning a { value, done } result from the
9059 // delegate iterator, or by modifying context.method and context.arg,
9060 // setting context.delegate to null, and returning the ContinueSentinel.
9063 function maybeInvokeDelegate(delegate, context) {
9064 var method = delegate.iterator[context.method];
9066 if (method === undefined$1) {
9067 // A .throw or .return when the delegate iterator has no .throw
9068 // method always terminates the yield* loop.
9069 context.delegate = null;
9071 if (context.method === "throw") {
9072 // Note: ["return"] must be used for ES3 parsing compatibility.
9073 if (delegate.iterator["return"]) {
9074 // If the delegate iterator has a return method, give it a
9075 // chance to clean up.
9076 context.method = "return";
9077 context.arg = undefined$1;
9078 maybeInvokeDelegate(delegate, context);
9080 if (context.method === "throw") {
9081 // If maybeInvokeDelegate(context) changed context.method from
9082 // "return" to "throw", let that override the TypeError below.
9083 return ContinueSentinel;
9087 context.method = "throw";
9088 context.arg = new TypeError("The iterator does not provide a 'throw' method");
9091 return ContinueSentinel;
9094 var record = tryCatch(method, delegate.iterator, context.arg);
9096 if (record.type === "throw") {
9097 context.method = "throw";
9098 context.arg = record.arg;
9099 context.delegate = null;
9100 return ContinueSentinel;
9103 var info = record.arg;
9106 context.method = "throw";
9107 context.arg = new TypeError("iterator result is not an object");
9108 context.delegate = null;
9109 return ContinueSentinel;
9113 // Assign the result of the finished delegate to the temporary
9114 // variable specified by delegate.resultName (see delegateYield).
9115 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
9117 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
9118 // exception, let the outer generator proceed normally. If
9119 // context.method was "next", forget context.arg since it has been
9120 // "consumed" by the delegate iterator. If context.method was
9121 // "return", allow the original .return call to continue in the
9124 if (context.method !== "return") {
9125 context.method = "next";
9126 context.arg = undefined$1;
9129 // Re-yield the result returned by the delegate method.
9131 } // The delegate iterator is finished, so forget it and continue with
9132 // the outer generator.
9135 context.delegate = null;
9136 return ContinueSentinel;
9137 } // Define Generator.prototype.{next,throw,return} in terms of the
9138 // unified ._invoke helper method.
9141 defineIteratorMethods(Gp);
9142 define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
9143 // @@iterator function is called on it. Some browsers' implementations of the
9144 // iterator prototype chain incorrectly implement this, causing the Generator
9145 // object to not be returned from this call. This ensures that doesn't happen.
9146 // See https://github.com/facebook/regenerator/issues/274 for more details.
9148 Gp[iteratorSymbol] = function () {
9152 Gp.toString = function () {
9153 return "[object Generator]";
9156 function pushTryEntry(locs) {
9162 entry.catchLoc = locs[1];
9166 entry.finallyLoc = locs[2];
9167 entry.afterLoc = locs[3];
9170 this.tryEntries.push(entry);
9173 function resetTryEntry(entry) {
9174 var record = entry.completion || {};
9175 record.type = "normal";
9177 entry.completion = record;
9180 function Context(tryLocsList) {
9181 // The root entry object (effectively a try statement without a catch
9182 // or a finally block) gives us a place to store values thrown from
9183 // locations where there is no enclosing try statement.
9184 this.tryEntries = [{
9187 tryLocsList.forEach(pushTryEntry, this);
9191 exports.keys = function (object) {
9194 for (var key in object) {
9198 keys.reverse(); // Rather than returning an object with a next method, we keep
9199 // things simple and return the next function itself.
9201 return function next() {
9202 while (keys.length) {
9203 var key = keys.pop();
9205 if (key in object) {
9210 } // To avoid creating an additional object, we just hang the .value
9211 // and .done properties off the next function object itself. This
9212 // also ensures that the minifier will not anonymize the function.
9220 function values(iterable) {
9222 var iteratorMethod = iterable[iteratorSymbol];
9224 if (iteratorMethod) {
9225 return iteratorMethod.call(iterable);
9228 if (typeof iterable.next === "function") {
9232 if (!isNaN(iterable.length)) {
9234 next = function next() {
9235 while (++i < iterable.length) {
9236 if (hasOwn.call(iterable, i)) {
9237 next.value = iterable[i];
9243 next.value = undefined$1;
9248 return next.next = next;
9250 } // Return an iterator with no values.
9258 exports.values = values;
9260 function doneResult() {
9267 Context.prototype = {
9268 constructor: Context,
9269 reset: function reset(skipTempReset) {
9271 this.next = 0; // Resetting context._sent for legacy support of Babel's
9272 // function.sent implementation.
9274 this.sent = this._sent = undefined$1;
9276 this.delegate = null;
9277 this.method = "next";
9278 this.arg = undefined$1;
9279 this.tryEntries.forEach(resetTryEntry);
9281 if (!skipTempReset) {
9282 for (var name in this) {
9283 // Not sure about the optimal order of these conditions:
9284 if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9285 this[name] = undefined$1;
9290 stop: function stop() {
9292 var rootEntry = this.tryEntries[0];
9293 var rootRecord = rootEntry.completion;
9295 if (rootRecord.type === "throw") {
9296 throw rootRecord.arg;
9301 dispatchException: function dispatchException(exception) {
9308 function handle(loc, caught) {
9309 record.type = "throw";
9310 record.arg = exception;
9314 // If the dispatched exception was caught by a catch block,
9315 // then let that catch block handle the exception normally.
9316 context.method = "next";
9317 context.arg = undefined$1;
9323 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9324 var entry = this.tryEntries[i];
9325 var record = entry.completion;
9327 if (entry.tryLoc === "root") {
9328 // Exception thrown outside of any try block that could handle
9329 // it, so set the completion value of the entire function to
9330 // throw the exception.
9331 return handle("end");
9334 if (entry.tryLoc <= this.prev) {
9335 var hasCatch = hasOwn.call(entry, "catchLoc");
9336 var hasFinally = hasOwn.call(entry, "finallyLoc");
9338 if (hasCatch && hasFinally) {
9339 if (this.prev < entry.catchLoc) {
9340 return handle(entry.catchLoc, true);
9341 } else if (this.prev < entry.finallyLoc) {
9342 return handle(entry.finallyLoc);
9344 } else if (hasCatch) {
9345 if (this.prev < entry.catchLoc) {
9346 return handle(entry.catchLoc, true);
9348 } else if (hasFinally) {
9349 if (this.prev < entry.finallyLoc) {
9350 return handle(entry.finallyLoc);
9353 throw new Error("try statement without catch or finally");
9358 abrupt: function abrupt(type, arg) {
9359 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9360 var entry = this.tryEntries[i];
9362 if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9363 var finallyEntry = entry;
9368 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9369 // Ignore the finally entry if control is not jumping to a
9370 // location outside the try/catch block.
9371 finallyEntry = null;
9374 var record = finallyEntry ? finallyEntry.completion : {};
9379 this.method = "next";
9380 this.next = finallyEntry.finallyLoc;
9381 return ContinueSentinel;
9384 return this.complete(record);
9386 complete: function complete(record, afterLoc) {
9387 if (record.type === "throw") {
9391 if (record.type === "break" || record.type === "continue") {
9392 this.next = record.arg;
9393 } else if (record.type === "return") {
9394 this.rval = this.arg = record.arg;
9395 this.method = "return";
9397 } else if (record.type === "normal" && afterLoc) {
9398 this.next = afterLoc;
9401 return ContinueSentinel;
9403 finish: function finish(finallyLoc) {
9404 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9405 var entry = this.tryEntries[i];
9407 if (entry.finallyLoc === finallyLoc) {
9408 this.complete(entry.completion, entry.afterLoc);
9409 resetTryEntry(entry);
9410 return ContinueSentinel;
9414 "catch": function _catch(tryLoc) {
9415 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9416 var entry = this.tryEntries[i];
9418 if (entry.tryLoc === tryLoc) {
9419 var record = entry.completion;
9421 if (record.type === "throw") {
9422 var thrown = record.arg;
9423 resetTryEntry(entry);
9428 } // The context.catch method must only be called with a location
9429 // argument that corresponds to a known catch block.
9432 throw new Error("illegal catch attempt");
9434 delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9436 iterator: values(iterable),
9437 resultName: resultName,
9441 if (this.method === "next") {
9442 // Deliberately forget the last sent value so that we don't
9443 // accidentally pass it on to the delegate.
9444 this.arg = undefined$1;
9447 return ContinueSentinel;
9449 }; // Regardless of whether this script is executing as a CommonJS module
9450 // or not, return the runtime object so that we can declare the variable
9451 // regeneratorRuntime in the outer scope, which allows this module to be
9452 // injected easily by `bin/regenerator --include-runtime script.js`.
9455 }( // If this script is executing as a CommonJS module, use module.exports
9456 // as the regeneratorRuntime namespace. Otherwise create a new empty
9457 // object. Either way, the resulting object will be used to initialize
9458 // the regeneratorRuntime variable at the top of this file.
9462 regeneratorRuntime = runtime;
9463 } catch (accidentalStrictMode) {
9464 // This module should not be running in strict mode, so the above
9465 // assignment should always work unless something is misconfigured. Just
9466 // in case runtime.js accidentally runs in strict mode, we can escape
9467 // strict mode using a global Function call. This could conceivably fail
9468 // if a Content Security Policy forbids using Function, but in that case
9469 // the proper solution is to fix the accidental strict mode problem. If
9470 // you've misconfigured your bundler to force strict mode and applied a
9471 // CSP to forbid Function, and you're not willing to fix either of those
9472 // problems, please detail your unique predicament in a GitHub issue.
9473 Function("r", "regeneratorRuntime = r")(runtime);
9477 var _marked$3 = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9479 function number$1 (x) {
9480 return x === null ? NaN : +x;
9482 function numbers(values, valueof) {
9483 var _iterator, _step, value, index, _iterator2, _step2, _value;
9485 return regeneratorRuntime.wrap(function numbers$(_context) {
9487 switch (_context.prev = _context.next) {
9489 if (!(valueof === undefined)) {
9494 _iterator = _createForOfIteratorHelper(values);
9500 if ((_step = _iterator.n()).done) {
9505 value = _step.value;
9507 if (!(value != null && (value = +value) >= value)) {
9525 _context.t0 = _context["catch"](2);
9527 _iterator.e(_context.t0);
9534 return _context.finish(16);
9542 _iterator2 = _createForOfIteratorHelper(values);
9548 if ((_step2 = _iterator2.n()).done) {
9553 _value = _step2.value;
9555 if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9573 _context.t1 = _context["catch"](23);
9575 _iterator2.e(_context.t1);
9582 return _context.finish(37);
9586 return _context.stop();
9589 }, _marked$3, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9592 var ascendingBisect = d3_bisector(d3_ascending);
9593 var bisectRight = ascendingBisect.right;
9594 d3_bisector(number$1).center;
9596 var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function (iterable) {
9597 // eslint-disable-next-line es/no-array-from -- required for testing
9598 Array.from(iterable);
9601 // `Array.from` method
9602 // https://tc39.es/ecma262/#sec-array.from
9603 _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, {
9607 // `Array.prototype.fill` method
9608 // https://tc39.es/ecma262/#sec-array.prototype.fill
9609 _export({ target: 'Array', proto: true }, {
9613 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
9614 addToUnscopables('fill');
9616 var $some = arrayIteration.some;
9619 var STRICT_METHOD$3 = arrayMethodIsStrict('some');
9621 // `Array.prototype.some` method
9622 // https://tc39.es/ecma262/#sec-array.prototype.some
9623 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$3 }, {
9624 some: function some(callbackfn /* , thisArg */) {
9625 return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9629 var exportTypedArrayStaticMethod = arrayBufferViewCore.exportTypedArrayStaticMethod;
9632 // `%TypedArray%.from` method
9633 // https://tc39.es/ecma262/#sec-%typedarray%.from
9634 exportTypedArrayStaticMethod('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9636 // `Float64Array` constructor
9637 // https://tc39.es/ecma262/#sec-typedarray-objects
9638 typedArrayConstructor('Float64', function (init) {
9639 return function Float64Array(data, byteOffset, length) {
9640 return init(this, data, byteOffset, length);
9644 function d3_descending (a, b) {
9645 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9648 // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9649 var Adder = /*#__PURE__*/function () {
9651 _classCallCheck$1(this, Adder);
9653 this._partials = new Float64Array(32);
9657 _createClass$1(Adder, [{
9659 value: function add(x) {
9660 var p = this._partials;
9663 for (var j = 0; j < this._n && j < 32; j++) {
9666 lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9667 if (lo) p[i++] = lo;
9677 value: function valueOf() {
9678 var p = this._partials;
9696 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9699 if (y == x - hi) hi = x;
9710 // `Object.defineProperties` method
9711 // https://tc39.es/ecma262/#sec-object.defineproperties
9712 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
9713 defineProperties: objectDefineProperties
9716 // `Map` constructor
9717 // https://tc39.es/ecma262/#sec-map-objects
9718 collection('Map', function (init) {
9719 return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9720 }, collectionStrong);
9723 var nativeSort = test.sort;
9726 var FAILS_ON_UNDEFINED = fails(function () {
9727 test.sort(undefined);
9730 var FAILS_ON_NULL = fails(function () {
9734 var STRICT_METHOD$2 = arrayMethodIsStrict('sort');
9736 var STABLE_SORT = !fails(function () {
9737 // feature detection can be too slow, so check engines versions
9738 if (engineV8Version) return engineV8Version < 70;
9739 if (engineFfVersion && engineFfVersion > 3) return;
9740 if (engineIsIeOrEdge) return true;
9741 if (engineWebkitVersion) return engineWebkitVersion < 603;
9744 var code, chr, value, index;
9746 // generate an array with more 512 elements (Chakra and old V8 fails only in this case)
9747 for (code = 65; code < 76; code++) {
9748 chr = String.fromCharCode(code);
9751 case 66: case 69: case 70: case 72: value = 3; break;
9752 case 68: case 71: value = 4; break;
9756 for (index = 0; index < 47; index++) {
9757 test.push({ k: chr + index, v: value });
9761 test.sort(function (a, b) { return b.v - a.v; });
9763 for (index = 0; index < test.length; index++) {
9764 chr = test[index].k.charAt(0);
9765 if (result.charAt(result.length - 1) !== chr) result += chr;
9768 return result !== 'DGBEFHACIJK';
9771 var FORCED$5 = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$2 || !STABLE_SORT;
9773 var getSortCompare = function (comparefn) {
9774 return function (x, y) {
9775 if (y === undefined) return -1;
9776 if (x === undefined) return 1;
9777 if (comparefn !== undefined) return +comparefn(x, y) || 0;
9778 return String(x) > String(y) ? 1 : -1;
9782 // `Array.prototype.sort` method
9783 // https://tc39.es/ecma262/#sec-array.prototype.sort
9784 _export({ target: 'Array', proto: true, forced: FORCED$5 }, {
9785 sort: function sort(comparefn) {
9786 if (comparefn !== undefined) aFunction(comparefn);
9788 var array = toObject(this);
9790 if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn);
9793 var arrayLength = toLength(array.length);
9794 var itemsLength, index;
9796 for (index = 0; index < arrayLength; index++) {
9797 if (index in array) items.push(array[index]);
9800 items = arraySort(items, getSortCompare(comparefn));
9801 itemsLength = items.length;
9804 while (index < itemsLength) array[index] = items[index++];
9805 while (index < arrayLength) delete array[index++];
9811 var e10 = Math.sqrt(50),
9814 function ticks (start, stop, count) {
9820 stop = +stop, start = +start, count = +count;
9821 if (start === stop && count > 0) return [start];
9822 if (reverse = stop < start) n = start, start = stop, stop = n;
9823 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9826 var r0 = Math.round(start / step),
9827 r1 = Math.round(stop / step);
9828 if (r0 * step < start) ++r0;
9829 if (r1 * step > stop) --r1;
9830 ticks = new Array(n = r1 - r0 + 1);
9833 ticks[i] = (r0 + i) * step;
9838 var _r = Math.round(start * step),
9839 _r2 = Math.round(stop * step);
9841 if (_r / step < start) ++_r;
9842 if (_r2 / step > stop) --_r2;
9843 ticks = new Array(n = _r2 - _r + 1);
9846 ticks[i] = (_r + i) / step;
9850 if (reverse) ticks.reverse();
9853 function tickIncrement(start, stop, count) {
9854 var step = (stop - start) / Math.max(0, count),
9855 power = Math.floor(Math.log(step) / Math.LN10),
9856 error = step / Math.pow(10, power);
9857 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);
9859 function tickStep(start, stop, count) {
9860 var step0 = Math.abs(stop - start) / Math.max(0, count),
9861 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9862 error = step0 / step1;
9863 if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9864 return stop < start ? -step1 : step1;
9867 function max(values, valueof) {
9870 if (valueof === undefined) {
9871 var _iterator = _createForOfIteratorHelper(values),
9875 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9876 var value = _step.value;
9878 if (value != null && (max < value || max === undefined && value >= value)) {
9890 var _iterator2 = _createForOfIteratorHelper(values),
9894 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9895 var _value = _step2.value;
9897 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9911 function min$2(values, valueof) {
9914 if (valueof === undefined) {
9915 var _iterator = _createForOfIteratorHelper(values),
9919 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9920 var value = _step.value;
9922 if (value != null && (min > value || min === undefined && value >= value)) {
9934 var _iterator2 = _createForOfIteratorHelper(values),
9938 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9939 var _value = _step2.value;
9941 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9955 // ISC license, Copyright 2018 Vladimir Agafonkin.
9957 function quickselect$2(array, k) {
9958 var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9959 var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9960 var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9962 while (right > left) {
9963 if (right - left > 600) {
9964 var n = right - left + 1;
9965 var m = k - left + 1;
9966 var z = Math.log(n);
9967 var s = 0.5 * Math.exp(2 * z / 3);
9968 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9969 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9970 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9971 quickselect$2(array, k, newLeft, newRight, compare);
9977 swap$1(array, left, k);
9978 if (compare(array[right], t) > 0) swap$1(array, left, right);
9981 swap$1(array, i, j), ++i, --j;
9983 while (compare(array[i], t) < 0) {
9987 while (compare(array[j], t) > 0) {
9992 if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right);
9993 if (j <= k) left = j + 1;
9994 if (k <= j) right = j - 1;
10000 function swap$1(array, i, j) {
10002 array[i] = array[j];
10006 function quantile(values, p, valueof) {
10007 values = Float64Array.from(numbers(values, valueof));
10008 if (!(n = values.length)) return;
10009 if ((p = +p) <= 0 || n < 2) return min$2(values);
10010 if (p >= 1) return max(values);
10013 i0 = Math.floor(i),
10014 value0 = max(quickselect$2(values, i0).subarray(0, i0 + 1)),
10015 value1 = min$2(values.subarray(i0 + 1));
10016 return value0 + (value1 - value0) * (i - i0);
10019 function d3_median (values, valueof) {
10020 return quantile(values, 0.5, valueof);
10023 var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
10025 function flatten(arrays) {
10026 var _iterator, _step, array;
10028 return regeneratorRuntime.wrap(function flatten$(_context) {
10030 switch (_context.prev = _context.next) {
10032 _iterator = _createForOfIteratorHelper(arrays);
10038 if ((_step = _iterator.n()).done) {
10043 array = _step.value;
10044 return _context.delegateYield(array, "t0", 6);
10051 _context.next = 13;
10055 _context.prev = 10;
10056 _context.t1 = _context["catch"](1);
10058 _iterator.e(_context.t1);
10061 _context.prev = 13;
10065 return _context.finish(13);
10069 return _context.stop();
10072 }, _marked$2, null, [[1, 10, 13, 16]]);
10075 function merge$4(arrays) {
10076 return Array.from(flatten(arrays));
10079 function range$1 (start, stop, step) {
10080 start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
10082 n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
10083 range = new Array(n);
10086 range[i] = start + i * step;
10092 // `SameValue` abstract operation
10093 // https://tc39.es/ecma262/#sec-samevalue
10094 // eslint-disable-next-line es/no-object-is -- safe
10095 var sameValue = Object.is || function is(x, y) {
10096 // eslint-disable-next-line no-self-compare -- NaN check
10097 return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
10100 // eslint-disable-next-line es/no-math-hypot -- required for testing
10101 var $hypot = Math.hypot;
10102 var abs$3 = Math.abs;
10103 var sqrt$1 = Math.sqrt;
10106 // https://bugs.chromium.org/p/v8/issues/detail?id=9546
10107 var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
10109 // `Math.hypot` method
10110 // https://tc39.es/ecma262/#sec-math.hypot
10111 _export({ target: 'Math', stat: true, forced: BUGGY }, {
10112 // eslint-disable-next-line no-unused-vars -- required for `.length`
10113 hypot: function hypot(value1, value2) {
10116 var aLen = arguments.length;
10120 arg = abs$3(arguments[i++]);
10123 sum = sum * div * div + 1;
10125 } else if (arg > 0) {
10130 return larg === Infinity ? Infinity : larg * sqrt$1(sum);
10134 // `Math.sign` method implementation
10135 // https://tc39.es/ecma262/#sec-math.sign
10136 // eslint-disable-next-line es/no-math-sign -- safe
10137 var mathSign = Math.sign || function sign(x) {
10138 // eslint-disable-next-line no-self-compare -- NaN check
10139 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
10142 // `Math.sign` method
10143 // https://tc39.es/ecma262/#sec-math.sign
10144 _export({ target: 'Math', stat: true }, {
10148 var epsilon$1 = 1e-6;
10149 var epsilon2$1 = 1e-12;
10151 var halfPi = pi / 2;
10152 var quarterPi = pi / 4;
10154 var degrees$1 = 180 / pi;
10155 var radians = pi / 180;
10156 var abs$2 = Math.abs;
10157 var atan = Math.atan;
10158 var atan2 = Math.atan2;
10159 var cos = Math.cos;
10160 var exp$2 = Math.exp;
10161 var log$1 = Math.log;
10162 var sin = Math.sin;
10163 var sign = Math.sign || function (x) {
10164 return x > 0 ? 1 : x < 0 ? -1 : 0;
10166 var sqrt = Math.sqrt;
10167 var tan = Math.tan;
10169 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
10172 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
10175 function noop$1() {}
10177 function streamGeometry(geometry, stream) {
10178 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
10179 streamGeometryType[geometry.type](geometry, stream);
10183 var streamObjectType = {
10184 Feature: function Feature(object, stream) {
10185 streamGeometry(object.geometry, stream);
10187 FeatureCollection: function FeatureCollection(object, stream) {
10188 var features = object.features,
10190 n = features.length;
10193 streamGeometry(features[i].geometry, stream);
10197 var streamGeometryType = {
10198 Sphere: function Sphere(object, stream) {
10201 Point: function Point(object, stream) {
10202 object = object.coordinates;
10203 stream.point(object[0], object[1], object[2]);
10205 MultiPoint: function MultiPoint(object, stream) {
10206 var coordinates = object.coordinates,
10208 n = coordinates.length;
10211 object = coordinates[i], stream.point(object[0], object[1], object[2]);
10214 LineString: function LineString(object, stream) {
10215 streamLine(object.coordinates, stream, 0);
10217 MultiLineString: function MultiLineString(object, stream) {
10218 var coordinates = object.coordinates,
10220 n = coordinates.length;
10223 streamLine(coordinates[i], stream, 0);
10226 Polygon: function Polygon(object, stream) {
10227 streamPolygon(object.coordinates, stream);
10229 MultiPolygon: function MultiPolygon(object, stream) {
10230 var coordinates = object.coordinates,
10232 n = coordinates.length;
10235 streamPolygon(coordinates[i], stream);
10238 GeometryCollection: function GeometryCollection(object, stream) {
10239 var geometries = object.geometries,
10241 n = geometries.length;
10244 streamGeometry(geometries[i], stream);
10249 function streamLine(coordinates, stream, closed) {
10251 n = coordinates.length - closed,
10253 stream.lineStart();
10256 coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
10262 function streamPolygon(coordinates, stream) {
10264 n = coordinates.length;
10265 stream.polygonStart();
10268 streamLine(coordinates[i], stream, 1);
10271 stream.polygonEnd();
10274 function d3_geoStream (object, stream) {
10275 if (object && streamObjectType.hasOwnProperty(object.type)) {
10276 streamObjectType[object.type](object, stream);
10278 streamGeometry(object, stream);
10282 var areaRingSum$1 = new Adder(); // hello?
10284 var areaSum$1 = new Adder(),
10290 var areaStream$1 = {
10294 polygonStart: function polygonStart() {
10295 areaRingSum$1 = new Adder();
10296 areaStream$1.lineStart = areaRingStart$1;
10297 areaStream$1.lineEnd = areaRingEnd$1;
10299 polygonEnd: function polygonEnd() {
10300 var areaRing = +areaRingSum$1;
10301 areaSum$1.add(areaRing < 0 ? tau + areaRing : areaRing);
10302 this.lineStart = this.lineEnd = this.point = noop$1;
10304 sphere: function sphere() {
10305 areaSum$1.add(tau);
10309 function areaRingStart$1() {
10310 areaStream$1.point = areaPointFirst$1;
10313 function areaRingEnd$1() {
10314 areaPoint$1(lambda00$1, phi00$1);
10317 function areaPointFirst$1(lambda, phi) {
10318 areaStream$1.point = areaPoint$1;
10319 lambda00$1 = lambda, phi00$1 = phi;
10320 lambda *= radians, phi *= radians;
10321 lambda0$2 = lambda, cosPhi0$1 = cos(phi = phi / 2 + quarterPi), sinPhi0$1 = sin(phi);
10324 function areaPoint$1(lambda, phi) {
10325 lambda *= radians, phi *= radians;
10326 phi = phi / 2 + quarterPi; // half the angular distance from south pole
10327 // Spherical excess E for a spherical triangle with vertices: south pole,
10328 // previous point, current point. Uses a formula derived from Cagnoli’s
10329 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10331 var dLambda = lambda - lambda0$2,
10332 sdLambda = dLambda >= 0 ? 1 : -1,
10333 adLambda = sdLambda * dLambda,
10336 k = sinPhi0$1 * sinPhi,
10337 u = cosPhi0$1 * cosPhi + k * cos(adLambda),
10338 v = k * sdLambda * sin(adLambda);
10339 areaRingSum$1.add(atan2(v, u)); // Advance the previous points.
10341 lambda0$2 = lambda, cosPhi0$1 = cosPhi, sinPhi0$1 = sinPhi;
10344 function d3_geoArea (object) {
10345 areaSum$1 = new Adder();
10346 d3_geoStream(object, areaStream$1);
10347 return areaSum$1 * 2;
10350 function spherical(cartesian) {
10351 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10353 function cartesian(spherical) {
10354 var lambda = spherical[0],
10355 phi = spherical[1],
10357 return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10359 function cartesianDot(a, b) {
10360 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10362 function cartesianCross(a, b) {
10363 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]];
10366 function cartesianAddInPlace(a, b) {
10367 a[0] += b[0], a[1] += b[1], a[2] += b[2];
10369 function cartesianScale(vector, k) {
10370 return [vector[0] * k, vector[1] * k, vector[2] * k];
10373 function cartesianNormalizeInPlace(d) {
10374 var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10375 d[0] /= l, d[1] /= l, d[2] /= l;
10378 var lambda0$1, phi0, lambda1, phi1, // bounds
10379 lambda2, // previous lambda-coordinate
10380 lambda00, phi00, // first point
10381 p0, // previous 3D point
10382 deltaSum, ranges, range;
10383 var boundsStream$1 = {
10384 point: boundsPoint$1,
10385 lineStart: boundsLineStart,
10386 lineEnd: boundsLineEnd,
10387 polygonStart: function polygonStart() {
10388 boundsStream$1.point = boundsRingPoint;
10389 boundsStream$1.lineStart = boundsRingStart;
10390 boundsStream$1.lineEnd = boundsRingEnd;
10391 deltaSum = new Adder();
10392 areaStream$1.polygonStart();
10394 polygonEnd: function polygonEnd() {
10395 areaStream$1.polygonEnd();
10396 boundsStream$1.point = boundsPoint$1;
10397 boundsStream$1.lineStart = boundsLineStart;
10398 boundsStream$1.lineEnd = boundsLineEnd;
10399 if (areaRingSum$1 < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon$1) phi1 = 90;else if (deltaSum < -epsilon$1) phi0 = -90;
10400 range[0] = lambda0$1, range[1] = lambda1;
10402 sphere: function sphere() {
10403 lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10407 function boundsPoint$1(lambda, phi) {
10408 ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10409 if (phi < phi0) phi0 = phi;
10410 if (phi > phi1) phi1 = phi;
10413 function linePoint(lambda, phi) {
10414 var p = cartesian([lambda * radians, phi * radians]);
10417 var normal = cartesianCross(p0, p),
10418 equatorial = [normal[1], -normal[0], 0],
10419 inflection = cartesianCross(equatorial, normal);
10420 cartesianNormalizeInPlace(inflection);
10421 inflection = spherical(inflection);
10422 var delta = lambda - lambda2,
10423 sign = delta > 0 ? 1 : -1,
10424 lambdai = inflection[0] * degrees$1 * sign,
10426 antimeridian = abs$2(delta) > 180;
10428 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10429 phii = inflection[1] * degrees$1;
10430 if (phii > phi1) phi1 = phii;
10431 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10432 phii = -inflection[1] * degrees$1;
10433 if (phii < phi0) phi0 = phii;
10435 if (phi < phi0) phi0 = phi;
10436 if (phi > phi1) phi1 = phi;
10439 if (antimeridian) {
10440 if (lambda < lambda2) {
10441 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10443 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10446 if (lambda1 >= lambda0$1) {
10447 if (lambda < lambda0$1) lambda0$1 = lambda;
10448 if (lambda > lambda1) lambda1 = lambda;
10450 if (lambda > lambda2) {
10451 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10453 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10458 ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
10461 if (phi < phi0) phi0 = phi;
10462 if (phi > phi1) phi1 = phi;
10463 p0 = p, lambda2 = lambda;
10466 function boundsLineStart() {
10467 boundsStream$1.point = linePoint;
10470 function boundsLineEnd() {
10471 range[0] = lambda0$1, range[1] = lambda1;
10472 boundsStream$1.point = boundsPoint$1;
10476 function boundsRingPoint(lambda, phi) {
10478 var delta = lambda - lambda2;
10479 deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10481 lambda00 = lambda, phi00 = phi;
10484 areaStream$1.point(lambda, phi);
10485 linePoint(lambda, phi);
10488 function boundsRingStart() {
10489 areaStream$1.lineStart();
10492 function boundsRingEnd() {
10493 boundsRingPoint(lambda00, phi00);
10494 areaStream$1.lineEnd();
10495 if (abs$2(deltaSum) > epsilon$1) lambda0$1 = -(lambda1 = 180);
10496 range[0] = lambda0$1, range[1] = lambda1;
10498 } // Finds the left-right distance between two longitudes.
10499 // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10500 // the distance between ±180° to be 360°.
10503 function angle(lambda0, lambda1) {
10504 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10507 function rangeCompare(a, b) {
10508 return a[0] - b[0];
10511 function rangeContains(range, x) {
10512 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10515 function d3_geoBounds (feature) {
10516 var i, n, a, b, merged, deltaMax, delta;
10517 phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10519 d3_geoStream(feature, boundsStream$1); // First, sort ranges by their minimum longitudes.
10521 if (n = ranges.length) {
10522 ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10524 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10527 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10528 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10529 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10531 merged.push(a = b);
10533 } // Finally, find the largest gap between the merged ranges.
10534 // The final bounding box will be the inverse of this gap.
10537 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10539 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10543 ranges = range = null;
10544 return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10547 function compose (a, b) {
10548 function compose(x, y) {
10549 return x = a(x, y), b(x[0], x[1]);
10552 if (a.invert && b.invert) compose.invert = function (x, y) {
10553 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10558 function rotationIdentity(lambda, phi) {
10559 return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10562 rotationIdentity.invert = rotationIdentity;
10563 function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10564 return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10567 function forwardRotationLambda(deltaLambda) {
10568 return function (lambda, phi) {
10569 return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10573 function rotationLambda(deltaLambda) {
10574 var rotation = forwardRotationLambda(deltaLambda);
10575 rotation.invert = forwardRotationLambda(-deltaLambda);
10579 function rotationPhiGamma(deltaPhi, deltaGamma) {
10580 var cosDeltaPhi = cos(deltaPhi),
10581 sinDeltaPhi = sin(deltaPhi),
10582 cosDeltaGamma = cos(deltaGamma),
10583 sinDeltaGamma = sin(deltaGamma);
10585 function rotation(lambda, phi) {
10586 var cosPhi = cos(phi),
10587 x = cos(lambda) * cosPhi,
10588 y = sin(lambda) * cosPhi,
10590 k = z * cosDeltaPhi + x * sinDeltaPhi;
10591 return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10594 rotation.invert = function (lambda, phi) {
10595 var cosPhi = cos(phi),
10596 x = cos(lambda) * cosPhi,
10597 y = sin(lambda) * cosPhi,
10599 k = z * cosDeltaGamma - y * sinDeltaGamma;
10600 return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10606 function rotation (rotate) {
10607 rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10609 function forward(coordinates) {
10610 coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10611 return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10614 forward.invert = function (coordinates) {
10615 coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10616 return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
10622 function circleStream(stream, radius, delta, direction, t0, t1) {
10623 if (!delta) return;
10624 var cosRadius = cos(radius),
10625 sinRadius = sin(radius),
10626 step = direction * delta;
10629 t0 = radius + direction * tau;
10630 t1 = radius - step / 2;
10632 t0 = circleRadius(cosRadius, t0);
10633 t1 = circleRadius(cosRadius, t1);
10634 if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10637 for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10638 point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10639 stream.point(point[0], point[1]);
10641 } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10643 function circleRadius(cosRadius, point) {
10644 point = cartesian(point), point[0] -= cosRadius;
10645 cartesianNormalizeInPlace(point);
10646 var radius = acos(-point[1]);
10647 return ((-point[2] < 0 ? -radius : radius) + tau - epsilon$1) % tau;
10650 function clipBuffer () {
10654 point: function point(x, y, m) {
10655 line.push([x, y, m]);
10657 lineStart: function lineStart() {
10658 lines.push(line = []);
10661 rejoin: function rejoin() {
10662 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10664 result: function result() {
10665 var result = lines;
10673 function pointEqual (a, b) {
10674 return abs$2(a[0] - b[0]) < epsilon$1 && abs$2(a[1] - b[1]) < epsilon$1;
10677 function Intersection(point, points, other, entry) {
10680 this.o = other; // another intersection
10682 this.e = entry; // is an entry?
10684 this.v = false; // visited
10686 this.n = this.p = null; // next & previous
10687 } // A generalized polygon clipping algorithm: given a polygon that has been cut
10688 // into its visible line segments, and rejoins the segments by interpolating
10689 // along the clip edge.
10692 function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10697 segments.forEach(function (segment) {
10698 if ((n = segment.length - 1) <= 0) return;
10704 if (pointEqual(p0, p1)) {
10705 if (!p0[2] && !p1[2]) {
10706 stream.lineStart();
10708 for (i = 0; i < n; ++i) {
10709 stream.point((p0 = segment[i])[0], p0[1]);
10714 } // handle degenerate cases by moving the point
10717 p1[0] += 2 * epsilon$1;
10720 subject.push(x = new Intersection(p0, segment, null, true));
10721 clip.push(x.o = new Intersection(p0, null, x, false));
10722 subject.push(x = new Intersection(p1, segment, null, false));
10723 clip.push(x.o = new Intersection(p1, null, x, true));
10725 if (!subject.length) return;
10726 clip.sort(compareIntersection);
10730 for (i = 0, n = clip.length; i < n; ++i) {
10731 clip[i].e = startInside = !startInside;
10734 var start = subject[0],
10739 // Find first unvisited intersection.
10740 var current = start,
10743 while (current.v) {
10744 if ((current = current.n) === start) return;
10747 points = current.z;
10748 stream.lineStart();
10751 current.v = current.o.v = true;
10755 for (i = 0, n = points.length; i < n; ++i) {
10756 stream.point((point = points[i])[0], point[1]);
10759 interpolate(current.x, current.n.x, 1, stream);
10762 current = current.n;
10765 points = current.p.z;
10767 for (i = points.length - 1; i >= 0; --i) {
10768 stream.point((point = points[i])[0], point[1]);
10771 interpolate(current.x, current.p.x, -1, stream);
10774 current = current.p;
10777 current = current.o;
10778 points = current.z;
10779 isSubject = !isSubject;
10780 } while (!current.v);
10786 function link(array) {
10787 if (!(n = array.length)) return;
10794 a.n = b = array[i];
10799 a.n = b = array[0];
10803 function longitude(point) {
10804 if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10807 function polygonContains (polygon, point) {
10808 var lambda = longitude(point),
10811 normal = [sin(lambda), -cos(lambda), 0],
10814 var sum = new Adder();
10815 if (sinPhi === 1) phi = halfPi + epsilon$1;else if (sinPhi === -1) phi = -halfPi - epsilon$1;
10817 for (var i = 0, n = polygon.length; i < n; ++i) {
10818 if (!(m = (ring = polygon[i]).length)) continue;
10821 point0 = ring[m - 1],
10822 lambda0 = longitude(point0),
10823 phi0 = point0[1] / 2 + quarterPi,
10824 sinPhi0 = sin(phi0),
10825 cosPhi0 = cos(phi0);
10827 for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10828 var point1 = ring[j],
10829 lambda1 = longitude(point1),
10830 phi1 = point1[1] / 2 + quarterPi,
10831 sinPhi1 = sin(phi1),
10832 cosPhi1 = cos(phi1),
10833 delta = lambda1 - lambda0,
10834 sign = delta >= 0 ? 1 : -1,
10835 absDelta = sign * delta,
10836 antimeridian = absDelta > pi,
10837 k = sinPhi0 * sinPhi1;
10838 sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10839 angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10840 // and are the latitudes smaller than the parallel (phi)?
10842 if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10843 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10844 cartesianNormalizeInPlace(arc);
10845 var intersection = cartesianCross(normal, arc);
10846 cartesianNormalizeInPlace(intersection);
10847 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10849 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10850 winding += antimeridian ^ delta >= 0 ? 1 : -1;
10854 } // First, determine whether the South pole is inside or outside:
10856 // It is inside if:
10857 // * the polygon winds around it in a clockwise direction.
10858 // * the polygon does not (cumulatively) wind around it, but has a negative
10859 // (counter-clockwise) area.
10861 // Second, count the (signed) number of times a segment crosses a lambda
10862 // from the point to the South pole. If it is zero, then the point is the
10863 // same side as the South pole.
10866 return (angle < -epsilon$1 || angle < epsilon$1 && sum < -epsilon2$1) ^ winding & 1;
10869 function clip (pointVisible, clipLine, interpolate, start) {
10870 return function (sink) {
10871 var line = clipLine(sink),
10872 ringBuffer = clipBuffer(),
10873 ringSink = clipLine(ringBuffer),
10874 polygonStarted = false,
10880 lineStart: lineStart,
10882 polygonStart: function polygonStart() {
10883 clip.point = pointRing;
10884 clip.lineStart = ringStart;
10885 clip.lineEnd = ringEnd;
10889 polygonEnd: function polygonEnd() {
10890 clip.point = point;
10891 clip.lineStart = lineStart;
10892 clip.lineEnd = lineEnd;
10893 segments = merge$4(segments);
10894 var startInside = polygonContains(polygon, start);
10896 if (segments.length) {
10897 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10898 clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10899 } else if (startInside) {
10900 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10902 interpolate(null, null, 1, sink);
10906 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10907 segments = polygon = null;
10909 sphere: function sphere() {
10910 sink.polygonStart();
10912 interpolate(null, null, 1, sink);
10918 function point(lambda, phi) {
10919 if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10922 function pointLine(lambda, phi) {
10923 line.point(lambda, phi);
10926 function lineStart() {
10927 clip.point = pointLine;
10931 function lineEnd() {
10932 clip.point = point;
10936 function pointRing(lambda, phi) {
10937 ring.push([lambda, phi]);
10938 ringSink.point(lambda, phi);
10941 function ringStart() {
10942 ringSink.lineStart();
10946 function ringEnd() {
10947 pointRing(ring[0][0], ring[0][1]);
10948 ringSink.lineEnd();
10949 var clean = ringSink.clean(),
10950 ringSegments = ringBuffer.result(),
10952 n = ringSegments.length,
10957 polygon.push(ring);
10959 if (!n) return; // No intersections.
10962 segment = ringSegments[0];
10964 if ((m = segment.length - 1) > 0) {
10965 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10968 for (i = 0; i < m; ++i) {
10969 sink.point((point = segment[i])[0], point[1]);
10976 } // Rejoin connected segments.
10977 // TODO reuse ringBuffer.rejoin()?
10980 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10981 segments.push(ringSegments.filter(validSegment));
10988 function validSegment(segment) {
10989 return segment.length > 1;
10990 } // Intersections are sorted along the clip edge. For both antimeridian cutting
10991 // and circle clipping, the same comparison is used.
10994 function compareIntersection(a, b) {
10995 return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon$1 : halfPi - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon$1 : halfPi - b[1]);
10998 var clipAntimeridian = clip(function () {
11000 }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
11001 // intersections or the line was empty; 1 - no intersections; 2 - there were
11002 // intersections, and the first and last segments should be rejoined.
11004 function clipAntimeridianLine(stream) {
11008 _clean; // no intersections
11012 lineStart: function lineStart() {
11013 stream.lineStart();
11016 point: function point(lambda1, phi1) {
11017 var sign1 = lambda1 > 0 ? pi : -pi,
11018 delta = abs$2(lambda1 - lambda0);
11020 if (abs$2(delta - pi) < epsilon$1) {
11021 // line crosses a pole
11022 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
11023 stream.point(sign0, phi0);
11025 stream.lineStart();
11026 stream.point(sign1, phi0);
11027 stream.point(lambda1, phi0);
11029 } else if (sign0 !== sign1 && delta >= pi) {
11030 // line crosses antimeridian
11031 if (abs$2(lambda0 - sign0) < epsilon$1) lambda0 -= sign0 * epsilon$1; // handle degeneracies
11033 if (abs$2(lambda1 - sign1) < epsilon$1) lambda1 -= sign1 * epsilon$1;
11034 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
11035 stream.point(sign0, phi0);
11037 stream.lineStart();
11038 stream.point(sign1, phi0);
11042 stream.point(lambda0 = lambda1, phi0 = phi1);
11045 lineEnd: function lineEnd() {
11047 lambda0 = phi0 = NaN;
11049 clean: function clean() {
11050 return 2 - _clean; // if intersections, rejoin first and last segments
11055 function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
11058 sinLambda0Lambda1 = sin(lambda0 - lambda1);
11059 return abs$2(sinLambda0Lambda1) > epsilon$1 ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2;
11062 function clipAntimeridianInterpolate(from, to, direction, stream) {
11065 if (from == null) {
11066 phi = direction * halfPi;
11067 stream.point(-pi, phi);
11068 stream.point(0, phi);
11069 stream.point(pi, phi);
11070 stream.point(pi, 0);
11071 stream.point(pi, -phi);
11072 stream.point(0, -phi);
11073 stream.point(-pi, -phi);
11074 stream.point(-pi, 0);
11075 stream.point(-pi, phi);
11076 } else if (abs$2(from[0] - to[0]) > epsilon$1) {
11077 var lambda = from[0] < to[0] ? pi : -pi;
11078 phi = direction * lambda / 2;
11079 stream.point(-lambda, phi);
11080 stream.point(0, phi);
11081 stream.point(lambda, phi);
11083 stream.point(to[0], to[1]);
11087 function clipCircle (radius) {
11088 var cr = cos(radius),
11089 delta = 6 * radians,
11090 smallRadius = cr > 0,
11091 notHemisphere = abs$2(cr) > epsilon$1; // TODO optimise for this common case
11093 function interpolate(from, to, direction, stream) {
11094 circleStream(stream, radius, delta, direction, from, to);
11097 function visible(lambda, phi) {
11098 return cos(lambda) * cos(phi) > cr;
11099 } // Takes a line and cuts into visible segments. Return values used for polygon
11100 // clipping: 0 - there were intersections or the line was empty; 1 - no
11101 // intersections 2 - there were intersections, and the first and last segments
11102 // should be rejoined.
11105 function clipLine(stream) {
11106 var point0, // previous point
11107 c0, // code for previous point
11108 v0, // visibility of previous point
11109 v00, // visibility of first point
11110 _clean; // no intersections
11114 lineStart: function lineStart() {
11118 point: function point(lambda, phi) {
11119 var point1 = [lambda, phi],
11121 v = visible(lambda, phi),
11122 c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
11123 if (!point0 && (v00 = v0 = v)) stream.lineStart();
11126 point2 = intersect(point0, point1);
11127 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
11134 // outside going in
11135 stream.lineStart();
11136 point2 = intersect(point1, point0);
11137 stream.point(point2[0], point2[1]);
11139 // inside going out
11140 point2 = intersect(point0, point1);
11141 stream.point(point2[0], point2[1], 2);
11146 } else if (notHemisphere && point0 && smallRadius ^ v) {
11147 var t; // If the codes for two points are different, or are both zero,
11148 // and there this segment intersects with the small circle.
11150 if (!(c & c0) && (t = intersect(point1, point0, true))) {
11154 stream.lineStart();
11155 stream.point(t[0][0], t[0][1]);
11156 stream.point(t[1][0], t[1][1]);
11159 stream.point(t[1][0], t[1][1]);
11161 stream.lineStart();
11162 stream.point(t[0][0], t[0][1], 3);
11167 if (v && (!point0 || !pointEqual(point0, point1))) {
11168 stream.point(point1[0], point1[1]);
11171 point0 = point1, v0 = v, c0 = c;
11173 lineEnd: function lineEnd() {
11174 if (v0) stream.lineEnd();
11177 // Rejoin first and last segments if there were intersections and the first
11178 // and last points were visible.
11179 clean: function clean() {
11180 return _clean | (v00 && v0) << 1;
11183 } // Intersects the great circle between a and b with the clip circle.
11186 function intersect(a, b, two) {
11187 var pa = cartesian(a),
11188 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
11189 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
11191 var n1 = [1, 0, 0],
11193 n2 = cartesianCross(pa, pb),
11194 n2n2 = cartesianDot(n2, n2),
11196 // cartesianDot(n1, n2),
11197 determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11199 if (!determinant) return !two && a;
11200 var c1 = cr * n2n2 / determinant,
11201 c2 = -cr * n1n2 / determinant,
11202 n1xn2 = cartesianCross(n1, n2),
11203 A = cartesianScale(n1, c1),
11204 B = cartesianScale(n2, c2);
11205 cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11208 w = cartesianDot(A, u),
11209 uu = cartesianDot(u, u),
11210 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11211 if (t2 < 0) return;
11213 q = cartesianScale(u, (-w - t) / uu);
11214 cartesianAddInPlace(q, A);
11216 if (!two) return q; // Two intersection points.
11218 var lambda0 = a[0],
11223 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11224 var delta = lambda1 - lambda0,
11225 polar = abs$2(delta - pi) < epsilon$1,
11226 meridian = polar || delta < epsilon$1;
11227 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11229 if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon$1 ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
11230 var q1 = cartesianScale(u, (-w + t) / uu);
11231 cartesianAddInPlace(q1, A);
11232 return [q, spherical(q1)];
11234 } // Generates a 4-bit vector representing the location of a point relative to
11235 // the small circle's bounding box.
11238 function code(lambda, phi) {
11239 var r = smallRadius ? radius : pi - radius,
11241 if (lambda < -r) code |= 1; // left
11242 else if (lambda > r) code |= 2; // right
11244 if (phi < -r) code |= 4; // below
11245 else if (phi > r) code |= 8; // above
11250 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11253 function clipLine (a, b, x0, y0, x1, y1) {
11264 if (!dx && r > 0) return;
11268 if (r < t0) return;
11269 if (r < t1) t1 = r;
11270 } else if (dx > 0) {
11271 if (r > t1) return;
11272 if (r > t0) t0 = r;
11276 if (!dx && r < 0) return;
11280 if (r > t1) return;
11281 if (r > t0) t0 = r;
11282 } else if (dx > 0) {
11283 if (r < t0) return;
11284 if (r < t1) t1 = r;
11288 if (!dy && r > 0) return;
11292 if (r < t0) return;
11293 if (r < t1) t1 = r;
11294 } else if (dy > 0) {
11295 if (r > t1) return;
11296 if (r > t0) t0 = r;
11300 if (!dy && r < 0) return;
11304 if (r > t1) return;
11305 if (r > t0) t0 = r;
11306 } else if (dy > 0) {
11307 if (r < t0) return;
11308 if (r < t1) t1 = r;
11311 if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11312 if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11317 clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11318 // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11320 function clipRectangle(x0, y0, x1, y1) {
11321 function visible(x, y) {
11322 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11325 function interpolate(from, to, direction, stream) {
11329 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11331 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11332 } while ((a = (a + direction + 4) % 4) !== a1);
11334 stream.point(to[0], to[1]);
11338 function corner(p, direction) {
11339 return abs$2(p[0] - x0) < epsilon$1 ? direction > 0 ? 0 : 3 : abs$2(p[0] - x1) < epsilon$1 ? direction > 0 ? 2 : 1 : abs$2(p[1] - y0) < epsilon$1 ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
11342 function compareIntersection(a, b) {
11343 return comparePoint(a.x, b.x);
11346 function comparePoint(a, b) {
11347 var ca = corner(a, 1),
11349 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];
11352 return function (stream) {
11353 var activeStream = stream,
11354 bufferStream = clipBuffer(),
11370 lineStart: lineStart,
11372 polygonStart: polygonStart,
11373 polygonEnd: polygonEnd
11376 function point(x, y) {
11377 if (visible(x, y)) activeStream.point(x, y);
11380 function polygonInside() {
11383 for (var i = 0, n = polygon.length; i < n; ++i) {
11384 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11385 a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11388 if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11390 if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11396 } // Buffer geometry within a polygon and then clip it en masse.
11399 function polygonStart() {
11400 activeStream = bufferStream, segments = [], polygon = [], clean = true;
11403 function polygonEnd() {
11404 var startInside = polygonInside(),
11405 cleanInside = clean && startInside,
11406 visible = (segments = merge$4(segments)).length;
11408 if (cleanInside || visible) {
11409 stream.polygonStart();
11412 stream.lineStart();
11413 interpolate(null, null, 1, stream);
11418 clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11421 stream.polygonEnd();
11424 activeStream = stream, segments = polygon = ring = null;
11427 function lineStart() {
11428 clipStream.point = linePoint;
11429 if (polygon) polygon.push(ring = []);
11433 } // TODO rather than special-case polygons, simply handle them separately.
11434 // Ideally, coincident intersection points should be jittered to avoid
11435 // clipping issues.
11438 function lineEnd() {
11440 linePoint(x__, y__);
11441 if (v__ && v_) bufferStream.rejoin();
11442 segments.push(bufferStream.result());
11445 clipStream.point = point;
11446 if (v_) activeStream.lineEnd();
11449 function linePoint(x, y) {
11450 var v = visible(x, y);
11451 if (polygon) ring.push([x, y]);
11454 x__ = x, y__ = y, v__ = v;
11458 activeStream.lineStart();
11459 activeStream.point(x, y);
11462 if (v && v_) activeStream.point(x, y);else {
11463 var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11464 b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11466 if (clipLine(a, b, x0, y0, x1, y1)) {
11468 activeStream.lineStart();
11469 activeStream.point(a[0], a[1]);
11472 activeStream.point(b[0], b[1]);
11473 if (!v) activeStream.lineEnd();
11476 activeStream.lineStart();
11477 activeStream.point(x, y);
11483 x_ = x, y_ = y, v_ = v;
11490 var lengthSum$1, lambda0, sinPhi0, cosPhi0;
11491 var lengthStream$1 = {
11494 lineStart: lengthLineStart,
11496 polygonStart: noop$1,
11500 function lengthLineStart() {
11501 lengthStream$1.point = lengthPointFirst$1;
11502 lengthStream$1.lineEnd = lengthLineEnd;
11505 function lengthLineEnd() {
11506 lengthStream$1.point = lengthStream$1.lineEnd = noop$1;
11509 function lengthPointFirst$1(lambda, phi) {
11510 lambda *= radians, phi *= radians;
11511 lambda0 = lambda, sinPhi0 = sin(phi), cosPhi0 = cos(phi);
11512 lengthStream$1.point = lengthPoint$1;
11515 function lengthPoint$1(lambda, phi) {
11516 lambda *= radians, phi *= radians;
11517 var sinPhi = sin(phi),
11519 delta = abs$2(lambda - lambda0),
11520 cosDelta = cos(delta),
11521 sinDelta = sin(delta),
11522 x = cosPhi * sinDelta,
11523 y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,
11524 z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;
11525 lengthSum$1.add(atan2(sqrt(x * x + y * y), z));
11526 lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;
11529 function d3_geoLength (object) {
11530 lengthSum$1 = new Adder();
11531 d3_geoStream(object, lengthStream$1);
11532 return +lengthSum$1;
11535 var identity$4 = (function (x) {
11539 var areaSum = new Adder(),
11540 areaRingSum = new Adder(),
11549 polygonStart: function polygonStart() {
11550 areaStream.lineStart = areaRingStart;
11551 areaStream.lineEnd = areaRingEnd;
11553 polygonEnd: function polygonEnd() {
11554 areaStream.lineStart = areaStream.lineEnd = areaStream.point = noop$1;
11555 areaSum.add(abs$2(areaRingSum));
11556 areaRingSum = new Adder();
11558 result: function result() {
11559 var area = areaSum / 2;
11560 areaSum = new Adder();
11565 function areaRingStart() {
11566 areaStream.point = areaPointFirst;
11569 function areaPointFirst(x, y) {
11570 areaStream.point = areaPoint;
11571 x00$2 = x0$3 = x, y00$2 = y0$3 = y;
11574 function areaPoint(x, y) {
11575 areaRingSum.add(y0$3 * x - x0$3 * y);
11576 x0$3 = x, y0$3 = y;
11579 function areaRingEnd() {
11580 areaPoint(x00$2, y00$2);
11583 var x0$2 = Infinity,
11587 var boundsStream = {
11588 point: boundsPoint,
11591 polygonStart: noop$1,
11592 polygonEnd: noop$1,
11593 result: function result() {
11594 var bounds = [[x0$2, y0$2], [x1, y1]];
11595 x1 = y1 = -(y0$2 = x0$2 = Infinity);
11600 function boundsPoint(x, y) {
11601 if (x < x0$2) x0$2 = x;
11602 if (x > x1) x1 = x;
11603 if (y < y0$2) y0$2 = y;
11604 if (y > y1) y1 = y;
11620 var centroidStream = {
11621 point: centroidPoint,
11622 lineStart: centroidLineStart,
11623 lineEnd: centroidLineEnd,
11624 polygonStart: function polygonStart() {
11625 centroidStream.lineStart = centroidRingStart;
11626 centroidStream.lineEnd = centroidRingEnd;
11628 polygonEnd: function polygonEnd() {
11629 centroidStream.point = centroidPoint;
11630 centroidStream.lineStart = centroidLineStart;
11631 centroidStream.lineEnd = centroidLineEnd;
11633 result: function result() {
11634 var centroid = Z2 ? [X2 / Z2, Y2 / Z2] : Z1 ? [X1 / Z1, Y1 / Z1] : Z0 ? [X0 / Z0, Y0 / Z0] : [NaN, NaN];
11635 X0 = Y0 = Z0 = X1 = Y1 = Z1 = X2 = Y2 = Z2 = 0;
11640 function centroidPoint(x, y) {
11646 function centroidLineStart() {
11647 centroidStream.point = centroidPointFirstLine;
11650 function centroidPointFirstLine(x, y) {
11651 centroidStream.point = centroidPointLine;
11652 centroidPoint(x0$1 = x, y0$1 = y);
11655 function centroidPointLine(x, y) {
11658 z = sqrt(dx * dx + dy * dy);
11659 X1 += z * (x0$1 + x) / 2;
11660 Y1 += z * (y0$1 + y) / 2;
11662 centroidPoint(x0$1 = x, y0$1 = y);
11665 function centroidLineEnd() {
11666 centroidStream.point = centroidPoint;
11669 function centroidRingStart() {
11670 centroidStream.point = centroidPointFirstRing;
11673 function centroidRingEnd() {
11674 centroidPointRing(x00$1, y00$1);
11677 function centroidPointFirstRing(x, y) {
11678 centroidStream.point = centroidPointRing;
11679 centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
11682 function centroidPointRing(x, y) {
11685 z = sqrt(dx * dx + dy * dy);
11686 X1 += z * (x0$1 + x) / 2;
11687 Y1 += z * (y0$1 + y) / 2;
11689 z = y0$1 * x - x0$1 * y;
11690 X2 += z * (x0$1 + x);
11691 Y2 += z * (y0$1 + y);
11693 centroidPoint(x0$1 = x, y0$1 = y);
11696 function PathContext(context) {
11697 this._context = context;
11699 PathContext.prototype = {
11701 pointRadius: function pointRadius(_) {
11702 return this._radius = _, this;
11704 polygonStart: function polygonStart() {
11707 polygonEnd: function polygonEnd() {
11710 lineStart: function lineStart() {
11713 lineEnd: function lineEnd() {
11714 if (this._line === 0) this._context.closePath();
11717 point: function point(x, y) {
11718 switch (this._point) {
11721 this._context.moveTo(x, y);
11729 this._context.lineTo(x, y);
11736 this._context.moveTo(x + this._radius, y);
11738 this._context.arc(x, y, this._radius, 0, tau);
11747 var lengthSum = new Adder(),
11753 var lengthStream = {
11755 lineStart: function lineStart() {
11756 lengthStream.point = lengthPointFirst;
11758 lineEnd: function lineEnd() {
11759 if (lengthRing) lengthPoint(x00, y00);
11760 lengthStream.point = noop$1;
11762 polygonStart: function polygonStart() {
11765 polygonEnd: function polygonEnd() {
11768 result: function result() {
11769 var length = +lengthSum;
11770 lengthSum = new Adder();
11775 function lengthPointFirst(x, y) {
11776 lengthStream.point = lengthPoint;
11777 x00 = x0 = x, y00 = y0 = y;
11780 function lengthPoint(x, y) {
11782 lengthSum.add(sqrt(x0 * x0 + y0 * y0));
11786 function PathString() {
11789 PathString.prototype = {
11791 _circle: circle(4.5),
11792 pointRadius: function pointRadius(_) {
11793 if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11796 polygonStart: function polygonStart() {
11799 polygonEnd: function polygonEnd() {
11802 lineStart: function lineStart() {
11805 lineEnd: function lineEnd() {
11806 if (this._line === 0) this._string.push("Z");
11809 point: function point(x, y) {
11810 switch (this._point) {
11813 this._string.push("M", x, ",", y);
11821 this._string.push("L", x, ",", y);
11828 if (this._circle == null) this._circle = circle(this._radius);
11830 this._string.push("M", x, ",", y, this._circle);
11836 result: function result() {
11837 if (this._string.length) {
11838 var result = this._string.join("");
11848 function circle(radius) {
11849 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11852 function d3_geoPath (projection, context) {
11853 var pointRadius = 4.5,
11857 function path(object) {
11859 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11860 d3_geoStream(object, projectionStream(contextStream));
11863 return contextStream.result();
11866 path.area = function (object) {
11867 d3_geoStream(object, projectionStream(areaStream));
11868 return areaStream.result();
11871 path.measure = function (object) {
11872 d3_geoStream(object, projectionStream(lengthStream));
11873 return lengthStream.result();
11876 path.bounds = function (object) {
11877 d3_geoStream(object, projectionStream(boundsStream));
11878 return boundsStream.result();
11881 path.centroid = function (object) {
11882 d3_geoStream(object, projectionStream(centroidStream));
11883 return centroidStream.result();
11886 path.projection = function (_) {
11887 return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
11890 path.context = function (_) {
11891 if (!arguments.length) return context;
11892 contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11893 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11897 path.pointRadius = function (_) {
11898 if (!arguments.length) return pointRadius;
11899 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11903 return path.projection(projection).context(context);
11906 function d3_geoTransform (methods) {
11908 stream: transformer$1(methods)
11911 function transformer$1(methods) {
11912 return function (stream) {
11913 var s = new TransformStream();
11915 for (var key in methods) {
11916 s[key] = methods[key];
11924 function TransformStream() {}
11926 TransformStream.prototype = {
11927 constructor: TransformStream,
11928 point: function point(x, y) {
11929 this.stream.point(x, y);
11931 sphere: function sphere() {
11932 this.stream.sphere();
11934 lineStart: function lineStart() {
11935 this.stream.lineStart();
11937 lineEnd: function lineEnd() {
11938 this.stream.lineEnd();
11940 polygonStart: function polygonStart() {
11941 this.stream.polygonStart();
11943 polygonEnd: function polygonEnd() {
11944 this.stream.polygonEnd();
11948 function fit(projection, fitBounds, object) {
11949 var clip = projection.clipExtent && projection.clipExtent();
11950 projection.scale(150).translate([0, 0]);
11951 if (clip != null) projection.clipExtent(null);
11952 d3_geoStream(object, projection.stream(boundsStream));
11953 fitBounds(boundsStream.result());
11954 if (clip != null) projection.clipExtent(clip);
11958 function fitExtent(projection, extent, object) {
11959 return fit(projection, function (b) {
11960 var w = extent[1][0] - extent[0][0],
11961 h = extent[1][1] - extent[0][1],
11962 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11963 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11964 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11965 projection.scale(150 * k).translate([x, y]);
11968 function fitSize(projection, size, object) {
11969 return fitExtent(projection, [[0, 0], size], object);
11971 function fitWidth(projection, width, object) {
11972 return fit(projection, function (b) {
11974 k = w / (b[1][0] - b[0][0]),
11975 x = (w - k * (b[1][0] + b[0][0])) / 2,
11977 projection.scale(150 * k).translate([x, y]);
11980 function fitHeight(projection, height, object) {
11981 return fit(projection, function (b) {
11983 k = h / (b[1][1] - b[0][1]),
11985 y = (h - k * (b[1][1] + b[0][1])) / 2;
11986 projection.scale(150 * k).translate([x, y]);
11991 // maximum depth of subdivision
11992 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11994 function resample (project, delta2) {
11995 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11998 function resampleNone(project) {
11999 return transformer$1({
12000 point: function point(x, y) {
12002 this.stream.point(x[0], x[1]);
12007 function resample$1(project, delta2) {
12008 function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
12011 d2 = dx * dx + dy * dy;
12013 if (d2 > 4 * delta2 && depth--) {
12017 m = sqrt(a * a + b * b + c * c),
12018 phi2 = asin(c /= m),
12019 lambda2 = abs$2(abs$2(c) - 1) < epsilon$1 || abs$2(lambda0 - lambda1) < epsilon$1 ? (lambda0 + lambda1) / 2 : atan2(b, a),
12020 p = project(lambda2, phi2),
12025 dz = dy * dx2 - dx * dy2;
12027 if (dz * dz / d2 > delta2 // perpendicular projected distance
12028 || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
12029 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
12030 // angular distance
12031 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
12032 stream.point(x2, y2);
12033 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
12038 return function (stream) {
12039 var lambda00, x00, y00, a00, b00, c00, // first point
12040 lambda0, x0, y0, a0, b0, c0; // previous point
12042 var resampleStream = {
12044 lineStart: lineStart,
12046 polygonStart: function polygonStart() {
12047 stream.polygonStart();
12048 resampleStream.lineStart = ringStart;
12050 polygonEnd: function polygonEnd() {
12051 stream.polygonEnd();
12052 resampleStream.lineStart = lineStart;
12056 function point(x, y) {
12058 stream.point(x[0], x[1]);
12061 function lineStart() {
12063 resampleStream.point = linePoint;
12064 stream.lineStart();
12067 function linePoint(lambda, phi) {
12068 var c = cartesian([lambda, phi]),
12069 p = project(lambda, phi);
12070 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);
12071 stream.point(x0, y0);
12074 function lineEnd() {
12075 resampleStream.point = point;
12079 function ringStart() {
12081 resampleStream.point = ringPoint;
12082 resampleStream.lineEnd = ringEnd;
12085 function ringPoint(lambda, phi) {
12086 linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
12087 resampleStream.point = linePoint;
12090 function ringEnd() {
12091 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
12092 resampleStream.lineEnd = lineEnd;
12096 return resampleStream;
12100 var transformRadians = transformer$1({
12101 point: function point(x, y) {
12102 this.stream.point(x * radians, y * radians);
12106 function transformRotate(rotate) {
12107 return transformer$1({
12108 point: function point(x, y) {
12109 var r = rotate(x, y);
12110 return this.stream.point(r[0], r[1]);
12115 function scaleTranslate(k, dx, dy, sx, sy) {
12116 function transform(x, y) {
12119 return [dx + k * x, dy - k * y];
12122 transform.invert = function (x, y) {
12123 return [(x - dx) / k * sx, (dy - y) / k * sy];
12129 function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
12130 if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
12131 var cosAlpha = cos(alpha),
12132 sinAlpha = sin(alpha),
12137 ci = (sinAlpha * dy - cosAlpha * dx) / k,
12138 fi = (sinAlpha * dx + cosAlpha * dy) / k;
12140 function transform(x, y) {
12143 return [a * x - b * y + dx, dy - b * x - a * y];
12146 transform.invert = function (x, y) {
12147 return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
12153 function projection(project) {
12154 return projectionMutator(function () {
12158 function projectionMutator(projectAt) {
12174 // post-rotate angle
12180 preclip = clipAntimeridian,
12186 postclip = identity$4,
12187 // post-clip extent
12192 projectRotateTransform,
12196 function projection(point) {
12197 return projectRotateTransform(point[0] * radians, point[1] * radians);
12200 function invert(point) {
12201 point = projectRotateTransform.invert(point[0], point[1]);
12202 return point && [point[0] * degrees$1, point[1] * degrees$1];
12205 projection.stream = function (stream) {
12206 return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12209 projection.preclip = function (_) {
12210 return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12213 projection.postclip = function (_) {
12214 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12217 projection.clipAngle = function (_) {
12218 return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
12221 projection.clipExtent = function (_) {
12222 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12225 projection.scale = function (_) {
12226 return arguments.length ? (k = +_, recenter()) : k;
12229 projection.translate = function (_) {
12230 return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12233 projection.center = function (_) {
12234 return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
12237 projection.rotate = function (_) {
12238 return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
12241 projection.angle = function (_) {
12242 return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1;
12245 projection.reflectX = function (_) {
12246 return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12249 projection.reflectY = function (_) {
12250 return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12253 projection.precision = function (_) {
12254 return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
12257 projection.fitExtent = function (extent, object) {
12258 return fitExtent(projection, extent, object);
12261 projection.fitSize = function (size, object) {
12262 return fitSize(projection, size, object);
12265 projection.fitWidth = function (width, object) {
12266 return fitWidth(projection, width, object);
12269 projection.fitHeight = function (height, object) {
12270 return fitHeight(projection, height, object);
12273 function recenter() {
12274 var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12275 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12276 rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12277 projectTransform = compose(project, transform);
12278 projectRotateTransform = compose(rotate, projectTransform);
12279 projectResample = resample(projectTransform, delta2);
12284 cache = cacheStream = null;
12288 return function () {
12289 project = projectAt.apply(this, arguments);
12290 projection.invert = project.invert && invert;
12295 function mercatorRaw(lambda, phi) {
12296 return [lambda, log$1(tan((halfPi + phi) / 2))];
12299 mercatorRaw.invert = function (x, y) {
12300 return [x, 2 * atan(exp$2(y)) - halfPi];
12303 function mercator () {
12304 return mercatorProjection(mercatorRaw).scale(961 / tau);
12306 function mercatorProjection(project) {
12307 var m = projection(project),
12310 translate = m.translate,
12311 clipExtent = m.clipExtent,
12317 m.scale = function (_) {
12318 return arguments.length ? (scale(_), reclip()) : scale();
12321 m.translate = function (_) {
12322 return arguments.length ? (translate(_), reclip()) : translate();
12325 m.center = function (_) {
12326 return arguments.length ? (center(_), reclip()) : center();
12329 m.clipExtent = function (_) {
12330 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]];
12333 function reclip() {
12334 var k = pi * scale(),
12335 t = m(rotation(m.rotate()).invert([0, 0]));
12336 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)]]);
12342 function d3_geoIdentity () {
12348 // scale, translate and reflect
12360 transform = transformer$1({
12361 point: function point(x, y) {
12362 var p = projection([x, y]);
12363 this.stream.point(p[0], p[1]);
12366 postclip = identity$4,
12373 cache = cacheStream = null;
12377 function projection(p) {
12382 var t = y * ca - x * sa;
12383 x = x * ca + y * sa;
12387 return [x + tx, y + ty];
12390 projection.invert = function (p) {
12395 var t = y * ca + x * sa;
12396 x = x * ca - y * sa;
12400 return [x / kx, y / ky];
12403 projection.stream = function (stream) {
12404 return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12407 projection.postclip = function (_) {
12408 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12411 projection.clipExtent = function (_) {
12412 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12415 projection.scale = function (_) {
12416 return arguments.length ? (k = +_, reset()) : k;
12419 projection.translate = function (_) {
12420 return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12423 projection.angle = function (_) {
12424 return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees$1;
12427 projection.reflectX = function (_) {
12428 return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12431 projection.reflectY = function (_) {
12432 return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12435 projection.fitExtent = function (extent, object) {
12436 return fitExtent(projection, extent, object);
12439 projection.fitSize = function (size, object) {
12440 return fitSize(projection, size, object);
12443 projection.fitWidth = function (width, object) {
12444 return fitWidth(projection, width, object);
12447 projection.fitHeight = function (height, object) {
12448 return fitHeight(projection, height, object);
12455 var TAU = 2 * Math.PI;
12456 var EQUATORIAL_RADIUS = 6356752.314245179;
12457 var POLAR_RADIUS$1 = 6378137.0;
12458 function geoLatToMeters(dLat) {
12459 return dLat * (TAU * POLAR_RADIUS$1 / 360);
12461 function geoLonToMeters(dLon, atLat) {
12462 return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12464 function geoMetersToLat(m) {
12465 return m / (TAU * POLAR_RADIUS$1 / 360);
12467 function geoMetersToLon(m, atLat) {
12468 return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12470 function geoMetersToOffset(meters, tileSize) {
12471 tileSize = tileSize || 256;
12472 return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS$1)];
12474 function geoOffsetToMeters(offset, tileSize) {
12475 tileSize = tileSize || 256;
12476 return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS$1 / tileSize];
12477 } // Equirectangular approximation of spherical distances on Earth
12479 function geoSphericalDistance(a, b) {
12480 var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12481 var y = geoLatToMeters(a[1] - b[1]);
12482 return Math.sqrt(x * x + y * y);
12485 function geoScaleToZoom(k, tileSize) {
12486 tileSize = tileSize || 256;
12487 var log2ts = Math.log(tileSize) * Math.LOG2E;
12488 return Math.log(k * TAU) / Math.LN2 - log2ts;
12491 function geoZoomToScale(z, tileSize) {
12492 tileSize = tileSize || 256;
12493 return tileSize * Math.pow(2, z) / TAU;
12494 } // returns info about the node from `nodes` closest to the given `point`
12496 function geoSphericalClosestNode(nodes, point) {
12497 var minDistance = Infinity,
12501 for (var i in nodes) {
12502 distance = geoSphericalDistance(nodes[i].loc, point);
12504 if (distance < minDistance) {
12505 minDistance = distance;
12510 if (indexOfMin !== undefined) {
12513 distance: minDistance,
12514 node: nodes[indexOfMin]
12521 function geoExtent(min, max) {
12522 if (!(this instanceof geoExtent)) {
12523 return new geoExtent(min, max);
12524 } else if (min instanceof geoExtent) {
12526 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12530 this[0] = min || [Infinity, Infinity];
12531 this[1] = max || min || [-Infinity, -Infinity];
12534 geoExtent.prototype = new Array(2);
12535 Object.assign(geoExtent.prototype, {
12536 equals: function equals(obj) {
12537 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];
12539 extend: function extend(obj) {
12540 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12541 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])]);
12543 _extend: function _extend(extent) {
12544 this[0][0] = Math.min(extent[0][0], this[0][0]);
12545 this[0][1] = Math.min(extent[0][1], this[0][1]);
12546 this[1][0] = Math.max(extent[1][0], this[1][0]);
12547 this[1][1] = Math.max(extent[1][1], this[1][1]);
12549 area: function area() {
12550 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12552 center: function center() {
12553 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12555 rectangle: function rectangle() {
12556 return [this[0][0], this[0][1], this[1][0], this[1][1]];
12558 bbox: function bbox() {
12566 polygon: function polygon() {
12567 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]]];
12569 contains: function contains(obj) {
12570 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12571 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];
12573 intersects: function intersects(obj) {
12574 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12575 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];
12577 intersection: function intersection(obj) {
12578 if (!this.intersects(obj)) return new geoExtent();
12579 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])]);
12581 percentContainedIn: function percentContainedIn(obj) {
12582 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12583 var a1 = this.intersection(obj).area();
12584 var a2 = this.area();
12586 if (a1 === Infinity || a2 === Infinity) {
12588 } else if (a1 === 0 || a2 === 0) {
12589 if (obj.contains(this)) {
12598 padByMeters: function padByMeters(meters) {
12599 var dLat = geoMetersToLat(meters);
12600 var dLon = geoMetersToLon(meters, this.center()[1]);
12601 return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12603 toParam: function toParam() {
12604 return this.rectangle().join(',');
12608 var $every = arrayIteration.every;
12611 var STRICT_METHOD$1 = arrayMethodIsStrict('every');
12613 // `Array.prototype.every` method
12614 // https://tc39.es/ecma262/#sec-array.prototype.every
12615 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$1 }, {
12616 every: function every(callbackfn /* , thisArg */) {
12617 return $every(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12621 var $reduce = arrayReduce.left;
12626 var STRICT_METHOD = arrayMethodIsStrict('reduce');
12627 // Chrome 80-82 has a critical bug
12628 // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
12629 var CHROME_BUG = !engineIsNode && engineV8Version > 79 && engineV8Version < 83;
12631 // `Array.prototype.reduce` method
12632 // https://tc39.es/ecma262/#sec-array.prototype.reduce
12633 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD || CHROME_BUG }, {
12634 reduce: function reduce(callbackfn /* , initialValue */) {
12635 return $reduce(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12639 function d3_polygonArea (polygon) {
12641 n = polygon.length,
12643 b = polygon[n - 1],
12649 area += a[1] * b[0] - a[0] * b[1];
12655 function d3_polygonCentroid (polygon) {
12657 n = polygon.length,
12661 b = polygon[n - 1],
12668 k += c = a[0] * b[1] - b[0] * a[1];
12669 x += (a[0] + b[0]) * c;
12670 y += (a[1] + b[1]) * c;
12673 return k *= 3, [x / k, y / k];
12676 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12677 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12678 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12679 // negative if clockwise, and zero if the points are collinear.
12680 function cross (a, b, c) {
12681 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12684 function lexicographicOrder(a, b) {
12685 return a[0] - b[0] || a[1] - b[1];
12686 } // Computes the upper convex hull per the monotone chain algorithm.
12687 // Assumes points.length >= 3, is sorted by x, unique in y.
12688 // Returns an array of indices into points in left-to-right order.
12691 function computeUpperHullIndexes(points) {
12692 var n = points.length,
12697 for (i = 2; i < n; ++i) {
12698 while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12702 indexes[size++] = i;
12705 return indexes.slice(0, size); // remove popped points
12708 function d3_polygonHull (points) {
12709 if ((n = points.length) < 3) return null;
12712 sortedPoints = new Array(n),
12713 flippedPoints = new Array(n);
12715 for (i = 0; i < n; ++i) {
12716 sortedPoints[i] = [+points[i][0], +points[i][1], i];
12719 sortedPoints.sort(lexicographicOrder);
12721 for (i = 0; i < n; ++i) {
12722 flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12725 var upperIndexes = computeUpperHullIndexes(sortedPoints),
12726 lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12728 var skipLeft = lowerIndexes[0] === upperIndexes[0],
12729 skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12730 hull = []; // Add upper hull in right-to-l order.
12731 // Then add lower hull in left-to-right order.
12733 for (i = upperIndexes.length - 1; i >= 0; --i) {
12734 hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12737 for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12738 hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12745 function geoVecEqual(a, b, epsilon) {
12747 return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12749 return a[0] === b[0] && a[1] === b[1];
12751 } // vector addition
12753 function geoVecAdd(a, b) {
12754 return [a[0] + b[0], a[1] + b[1]];
12755 } // vector subtraction
12757 function geoVecSubtract(a, b) {
12758 return [a[0] - b[0], a[1] - b[1]];
12759 } // vector scaling
12761 function geoVecScale(a, mag) {
12762 return [a[0] * mag, a[1] * mag];
12763 } // vector rounding (was: geoRoundCoordinates)
12765 function geoVecFloor(a) {
12766 return [Math.floor(a[0]), Math.floor(a[1])];
12767 } // linear interpolation
12769 function geoVecInterp(a, b, t) {
12770 return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12771 } // http://jsperf.com/id-dist-optimization
12773 function geoVecLength(a, b) {
12774 return Math.sqrt(geoVecLengthSquare(a, b));
12775 } // length of vector raised to the power two
12777 function geoVecLengthSquare(a, b) {
12779 var x = a[0] - b[0];
12780 var y = a[1] - b[1];
12781 return x * x + y * y;
12782 } // get a unit vector
12784 function geoVecNormalize(a) {
12785 var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12787 if (length !== 0) {
12788 return geoVecScale(a, 1 / length);
12792 } // Return the counterclockwise angle in the range (-pi, pi)
12793 // between the positive X axis and the line intersecting a and b.
12795 function geoVecAngle(a, b) {
12796 return Math.atan2(b[1] - a[1], b[0] - a[0]);
12799 function geoVecDot(a, b, origin) {
12800 origin = origin || [0, 0];
12801 var p = geoVecSubtract(a, origin);
12802 var q = geoVecSubtract(b, origin);
12803 return p[0] * q[0] + p[1] * q[1];
12804 } // normalized dot product
12806 function geoVecNormalizedDot(a, b, origin) {
12807 origin = origin || [0, 0];
12808 var p = geoVecNormalize(geoVecSubtract(a, origin));
12809 var q = geoVecNormalize(geoVecSubtract(b, origin));
12810 return geoVecDot(p, q);
12811 } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12812 // Returns a positive value, if OAB makes a counter-clockwise turn,
12813 // negative for clockwise turn, and zero if the points are collinear.
12815 function geoVecCross(a, b, origin) {
12816 origin = origin || [0, 0];
12817 var p = geoVecSubtract(a, origin);
12818 var q = geoVecSubtract(b, origin);
12819 return p[0] * q[1] - p[1] * q[0];
12820 } // find closest orthogonal projection of point onto points array
12822 function geoVecProject(a, points) {
12823 var min = Infinity;
12827 for (var i = 0; i < points.length - 1; i++) {
12829 var s = geoVecSubtract(points[i + 1], o);
12830 var v = geoVecSubtract(a, o);
12831 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12836 } else if (proj > 1) {
12839 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12842 var dist = geoVecLength(p, a);
12851 if (idx !== undefined) {
12862 // between the positive X axis and the line intersecting a and b.
12864 function geoAngle(a, b, projection) {
12865 return geoVecAngle(projection(a.loc), projection(b.loc));
12867 function geoEdgeEqual(a, b) {
12868 return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12869 } // Rotate all points counterclockwise around a pivot point by given angle
12871 function geoRotate(points, angle, around) {
12872 return points.map(function (point) {
12873 var radial = geoVecSubtract(point, around);
12874 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]];
12876 } // Choose the edge with the minimal distance from `point` to its orthogonal
12877 // projection onto that edge, if such a projection exists, or the distance to
12878 // the closest vertex on that edge. Returns an object with the `index` of the
12879 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12881 function geoChooseEdge(nodes, point, projection, activeID) {
12882 var dist = geoVecLength;
12883 var points = nodes.map(function (n) {
12884 return projection(n.loc);
12886 var ids = nodes.map(function (n) {
12889 var min = Infinity;
12893 for (var i = 0; i < points.length - 1; i++) {
12894 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12896 var s = geoVecSubtract(points[i + 1], o);
12897 var v = geoVecSubtract(point, o);
12898 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12903 } else if (proj > 1) {
12906 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12909 var d = dist(p, point);
12914 loc = projection.invert(p);
12918 if (idx !== undefined) {
12927 } // Test active (dragged or drawing) segments against inactive segments
12928 // This is used to test e.g. multipolygon rings that cross
12929 // `activeNodes` is the ring containing the activeID being dragged.
12930 // `inactiveNodes` is the other ring to test against
12932 function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12934 var inactives = [];
12935 var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12937 for (j = 0; j < activeNodes.length - 1; j++) {
12938 n1 = activeNodes[j];
12939 n2 = activeNodes[j + 1];
12940 segment = [n1.loc, n2.loc];
12942 if (n1.id === activeID || n2.id === activeID) {
12943 actives.push(segment);
12945 } // gather inactive segments
12948 for (j = 0; j < inactiveNodes.length - 1; j++) {
12949 n1 = inactiveNodes[j];
12950 n2 = inactiveNodes[j + 1];
12951 segment = [n1.loc, n2.loc];
12952 inactives.push(segment);
12956 for (j = 0; j < actives.length; j++) {
12957 for (k = 0; k < inactives.length; k++) {
12958 var p = actives[j];
12959 var q = inactives[k];
12960 var hit = geoLineIntersection(p, q);
12969 } // Test active (dragged or drawing) segments against inactive segments
12970 // This is used to test whether a way intersects with itself.
12972 function geoHasSelfIntersections(nodes, activeID) {
12974 var inactives = [];
12975 var j, k; // group active and passive segments along the nodes
12977 for (j = 0; j < nodes.length - 1; j++) {
12979 var n2 = nodes[j + 1];
12980 var segment = [n1.loc, n2.loc];
12982 if (n1.id === activeID || n2.id === activeID) {
12983 actives.push(segment);
12985 inactives.push(segment);
12990 for (j = 0; j < actives.length; j++) {
12991 for (k = 0; k < inactives.length; k++) {
12992 var p = actives[j];
12993 var q = inactives[k]; // skip if segments share an endpoint
12995 if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
12999 var hit = geoLineIntersection(p, q);
13002 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
13004 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
13014 } // Return the intersection point of 2 line segments.
13015 // From https://github.com/pgkelley4/line-segments-intersect
13016 // This uses the vector cross product approach described below:
13017 // http://stackoverflow.com/a/565282/786339
13019 function geoLineIntersection(a, b) {
13020 var p = [a[0][0], a[0][1]];
13021 var p2 = [a[1][0], a[1][1]];
13022 var q = [b[0][0], b[0][1]];
13023 var q2 = [b[1][0], b[1][1]];
13024 var r = geoVecSubtract(p2, p);
13025 var s = geoVecSubtract(q2, q);
13026 var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
13027 var denominator = geoVecCross(r, s);
13029 if (uNumerator && denominator) {
13030 var u = uNumerator / denominator;
13031 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
13033 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
13034 return geoVecInterp(p, p2, t);
13040 function geoPathIntersections(path1, path2) {
13041 var intersections = [];
13043 for (var i = 0; i < path1.length - 1; i++) {
13044 for (var j = 0; j < path2.length - 1; j++) {
13045 var a = [path1[i], path1[i + 1]];
13046 var b = [path2[j], path2[j + 1]];
13047 var hit = geoLineIntersection(a, b);
13050 intersections.push(hit);
13055 return intersections;
13057 function geoPathHasIntersections(path1, path2) {
13058 for (var i = 0; i < path1.length - 1; i++) {
13059 for (var j = 0; j < path2.length - 1; j++) {
13060 var a = [path1[i], path1[i + 1]];
13061 var b = [path2[j], path2[j + 1]];
13062 var hit = geoLineIntersection(a, b);
13071 } // Return whether point is contained in polygon.
13073 // `point` should be a 2-item array of coordinates.
13074 // `polygon` should be an array of 2-item arrays of coordinates.
13076 // From https://github.com/substack/point-in-polygon.
13077 // ray-casting algorithm based on
13078 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
13081 function geoPointInPolygon(point, polygon) {
13084 var inside = false;
13086 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
13087 var xi = polygon[i][0];
13088 var yi = polygon[i][1];
13089 var xj = polygon[j][0];
13090 var yj = polygon[j][1];
13091 var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
13092 if (intersect) inside = !inside;
13097 function geoPolygonContainsPolygon(outer, inner) {
13098 return inner.every(function (point) {
13099 return geoPointInPolygon(point, outer);
13102 function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
13103 function testPoints(outer, inner) {
13104 return inner.some(function (point) {
13105 return geoPointInPolygon(point, outer);
13109 return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
13110 } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
13111 // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
13113 function geoGetSmallestSurroundingRectangle(points) {
13114 var hull = d3_polygonHull(points);
13115 var centroid = d3_polygonCentroid(hull);
13116 var minArea = Infinity;
13117 var ssrExtent = [];
13121 for (var i = 0; i <= hull.length - 1; i++) {
13122 var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
13123 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
13124 var poly = geoRotate(hull, -angle, centroid);
13125 var extent = poly.reduce(function (extent, point) {
13126 return extent.extend(geoExtent(point));
13128 var area = extent.area();
13130 if (area < minArea) {
13132 ssrExtent = extent;
13140 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
13144 function geoPathLength(path) {
13147 for (var i = 0; i < path.length - 1; i++) {
13148 length += geoVecLength(path[i], path[i + 1]);
13152 } // If the given point is at the edge of the padded viewport,
13153 // return a vector that will nudge the viewport in that direction
13155 function geoViewportEdge(point, dimensions) {
13156 var pad = [80, 20, 50, 20]; // top, right, bottom, left
13161 if (point[0] > dimensions[0] - pad[1]) {
13165 if (point[0] < pad[3]) {
13169 if (point[1] > dimensions[1] - pad[2]) {
13173 if (point[1] < pad[0]) {
13185 value: function value() {}
13188 function dispatch$8() {
13189 for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
13190 if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
13194 return new Dispatch(_);
13197 function Dispatch(_) {
13201 function parseTypenames$1(typenames, types) {
13202 return typenames.trim().split(/^|\s+/).map(function (t) {
13204 i = t.indexOf(".");
13205 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13206 if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13214 Dispatch.prototype = dispatch$8.prototype = {
13215 constructor: Dispatch,
13216 on: function on(typename, callback) {
13218 T = parseTypenames$1(typename + "", _),
13221 n = T.length; // If no callback was specified, return the callback of the given type and name.
13223 if (arguments.length < 2) {
13225 if ((t = (typename = T[i]).type) && (t = get$2(_[t], typename.name))) return t;
13229 } // If a type was specified, set the callback for the given type and name.
13230 // Otherwise, if a null callback was specified, remove callbacks of the given name.
13233 if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13236 if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13237 _[t] = set$1(_[t], typename.name, null);
13243 copy: function copy() {
13248 copy[t] = _[t].slice();
13251 return new Dispatch(copy);
13253 call: function call(type, that) {
13254 if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13255 args[i] = arguments[i + 2];
13257 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13259 for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13260 t[i].value.apply(that, args);
13263 apply: function apply(type, that, args) {
13264 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13266 for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13267 t[i].value.apply(that, args);
13272 function get$2(type, name) {
13273 for (var i = 0, n = type.length, c; i < n; ++i) {
13274 if ((c = type[i]).name === name) {
13280 function set$1(type, name, callback) {
13281 for (var i = 0, n = type.length; i < n; ++i) {
13282 if (type[i].name === name) {
13283 type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
13288 if (callback != null) type.push({
13295 var xhtml = "http://www.w3.org/1999/xhtml";
13297 svg: "http://www.w3.org/2000/svg",
13299 xlink: "http://www.w3.org/1999/xlink",
13300 xml: "http://www.w3.org/XML/1998/namespace",
13301 xmlns: "http://www.w3.org/2000/xmlns/"
13304 function namespace (name) {
13305 var prefix = name += "",
13306 i = prefix.indexOf(":");
13307 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13308 return namespaces.hasOwnProperty(prefix) ? {
13309 space: namespaces[prefix],
13311 } : name; // eslint-disable-line no-prototype-builtins
13314 function creatorInherit(name) {
13315 return function () {
13316 var document = this.ownerDocument,
13317 uri = this.namespaceURI;
13318 return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13322 function creatorFixed(fullname) {
13323 return function () {
13324 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13328 function creator (name) {
13329 var fullname = namespace(name);
13330 return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13335 function selector (selector) {
13336 return selector == null ? none : function () {
13337 return this.querySelector(selector);
13341 function selection_select (select) {
13342 if (typeof select !== "function") select = selector(select);
13344 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13345 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13346 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13347 if ("__data__" in node) subnode.__data__ = node.__data__;
13348 subgroup[i] = subnode;
13353 return new Selection$1(subgroups, this._parents);
13356 function array (x) {
13357 return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13358 : Array.from(x); // Map, Set, iterable, string, or anything else
13365 function selectorAll (selector) {
13366 return selector == null ? empty : function () {
13367 return this.querySelectorAll(selector);
13371 function arrayAll(select) {
13372 return function () {
13373 var group = select.apply(this, arguments);
13374 return group == null ? [] : array(group);
13378 function selection_selectAll (select) {
13379 if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13381 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13382 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13383 if (node = group[i]) {
13384 subgroups.push(select.call(node, node.__data__, i, group));
13385 parents.push(node);
13390 return new Selection$1(subgroups, parents);
13393 var $find = arrayIteration.find;
13397 var SKIPS_HOLES$1 = true;
13399 // Shouldn't skip holes
13400 if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES$1 = false; });
13402 // `Array.prototype.find` method
13403 // https://tc39.es/ecma262/#sec-array.prototype.find
13404 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 }, {
13405 find: function find(callbackfn /* , that = undefined */) {
13406 return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13410 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
13411 addToUnscopables(FIND);
13413 function matcher (selector) {
13414 return function () {
13415 return this.matches(selector);
13418 function childMatcher(selector) {
13419 return function (node) {
13420 return node.matches(selector);
13424 var find = Array.prototype.find;
13426 function childFind(match) {
13427 return function () {
13428 return find.call(this.children, match);
13432 function childFirst() {
13433 return this.firstElementChild;
13436 function selection_selectChild (match) {
13437 return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13440 var filter = Array.prototype.filter;
13442 function children() {
13443 return this.children;
13446 function childrenFilter(match) {
13447 return function () {
13448 return filter.call(this.children, match);
13452 function selection_selectChildren (match) {
13453 return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13456 function selection_filter (match) {
13457 if (typeof match !== "function") match = matcher(match);
13459 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13460 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13461 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13462 subgroup.push(node);
13467 return new Selection$1(subgroups, this._parents);
13470 function sparse (update) {
13471 return new Array(update.length);
13474 function selection_enter () {
13475 return new Selection$1(this._enter || this._groups.map(sparse), this._parents);
13477 function EnterNode(parent, datum) {
13478 this.ownerDocument = parent.ownerDocument;
13479 this.namespaceURI = parent.namespaceURI;
13481 this._parent = parent;
13482 this.__data__ = datum;
13484 EnterNode.prototype = {
13485 constructor: EnterNode,
13486 appendChild: function appendChild(child) {
13487 return this._parent.insertBefore(child, this._next);
13489 insertBefore: function insertBefore(child, next) {
13490 return this._parent.insertBefore(child, next);
13492 querySelector: function querySelector(selector) {
13493 return this._parent.querySelector(selector);
13495 querySelectorAll: function querySelectorAll(selector) {
13496 return this._parent.querySelectorAll(selector);
13500 function constant$3 (x) {
13501 return function () {
13506 function bindIndex(parent, group, enter, update, exit, data) {
13509 groupLength = group.length,
13510 dataLength = data.length; // Put any non-null nodes that fit into update.
13511 // Put any null nodes into enter.
13512 // Put any remaining data into enter.
13514 for (; i < dataLength; ++i) {
13515 if (node = group[i]) {
13516 node.__data__ = data[i];
13519 enter[i] = new EnterNode(parent, data[i]);
13521 } // Put any non-null nodes that don’t fit into exit.
13524 for (; i < groupLength; ++i) {
13525 if (node = group[i]) {
13531 function bindKey(parent, group, enter, update, exit, data, key) {
13534 nodeByKeyValue = new Map(),
13535 groupLength = group.length,
13536 dataLength = data.length,
13537 keyValues = new Array(groupLength),
13538 keyValue; // Compute the key for each node.
13539 // If multiple nodes have the same key, the duplicates are added to exit.
13541 for (i = 0; i < groupLength; ++i) {
13542 if (node = group[i]) {
13543 keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13545 if (nodeByKeyValue.has(keyValue)) {
13548 nodeByKeyValue.set(keyValue, node);
13551 } // Compute the key for each datum.
13552 // If there a node associated with this key, join and add it to update.
13553 // If there is not (or the key is a duplicate), add it to enter.
13556 for (i = 0; i < dataLength; ++i) {
13557 keyValue = key.call(parent, data[i], i, data) + "";
13559 if (node = nodeByKeyValue.get(keyValue)) {
13561 node.__data__ = data[i];
13562 nodeByKeyValue["delete"](keyValue);
13564 enter[i] = new EnterNode(parent, data[i]);
13566 } // Add any remaining nodes that were not bound to data to exit.
13569 for (i = 0; i < groupLength; ++i) {
13570 if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13576 function datum(node) {
13577 return node.__data__;
13580 function selection_data (value, key) {
13581 if (!arguments.length) return Array.from(this, datum);
13582 var bind = key ? bindKey : bindIndex,
13583 parents = this._parents,
13584 groups = this._groups;
13585 if (typeof value !== "function") value = constant$3(value);
13587 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13588 var parent = parents[j],
13590 groupLength = group.length,
13591 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13592 dataLength = data.length,
13593 enterGroup = enter[j] = new Array(dataLength),
13594 updateGroup = update[j] = new Array(dataLength),
13595 exitGroup = exit[j] = new Array(groupLength);
13596 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13597 // appendChild can insert the materialized enter node before this node,
13598 // rather than at the end of the parent node.
13600 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13601 if (previous = enterGroup[i0]) {
13602 if (i0 >= i1) i1 = i0 + 1;
13604 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13607 previous._next = next || null;
13612 update = new Selection$1(update, parents);
13613 update._enter = enter;
13614 update._exit = exit;
13618 function selection_exit () {
13619 return new Selection$1(this._exit || this._groups.map(sparse), this._parents);
13622 function selection_join (onenter, onupdate, onexit) {
13623 var enter = this.enter(),
13625 exit = this.exit();
13626 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13627 if (onupdate != null) update = onupdate(update);
13628 if (onexit == null) exit.remove();else onexit(exit);
13629 return enter && update ? enter.merge(update).order() : update;
13632 function selection_merge (selection) {
13633 if (!(selection instanceof Selection$1)) throw new Error("invalid merge");
13635 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) {
13636 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13637 if (node = group0[i] || group1[i]) {
13643 for (; j < m0; ++j) {
13644 merges[j] = groups0[j];
13647 return new Selection$1(merges, this._parents);
13650 function selection_order () {
13651 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13652 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13653 if (node = group[i]) {
13654 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13663 function selection_sort (compare) {
13664 if (!compare) compare = ascending;
13666 function compareNode(a, b) {
13667 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13670 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13671 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13672 if (node = group[i]) {
13673 sortgroup[i] = node;
13677 sortgroup.sort(compareNode);
13680 return new Selection$1(sortgroups, this._parents).order();
13683 function ascending(a, b) {
13684 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13687 function selection_call () {
13688 var callback = arguments[0];
13689 arguments[0] = this;
13690 callback.apply(null, arguments);
13694 function selection_nodes () {
13695 return Array.from(this);
13698 function selection_node () {
13699 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13700 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13701 var node = group[i];
13702 if (node) return node;
13709 function selection_size () {
13712 var _iterator = _createForOfIteratorHelper(this),
13716 for (_iterator.s(); !(_step = _iterator.n()).done;) {
13717 var node = _step.value;
13719 } // eslint-disable-line no-unused-vars
13730 function selection_empty () {
13731 return !this.node();
13734 function selection_each (callback) {
13735 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13736 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13737 if (node = group[i]) callback.call(node, node.__data__, i, group);
13744 function attrRemove$1(name) {
13745 return function () {
13746 this.removeAttribute(name);
13750 function attrRemoveNS$1(fullname) {
13751 return function () {
13752 this.removeAttributeNS(fullname.space, fullname.local);
13756 function attrConstant$1(name, value) {
13757 return function () {
13758 this.setAttribute(name, value);
13762 function attrConstantNS$1(fullname, value) {
13763 return function () {
13764 this.setAttributeNS(fullname.space, fullname.local, value);
13768 function attrFunction$1(name, value) {
13769 return function () {
13770 var v = value.apply(this, arguments);
13771 if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13775 function attrFunctionNS$1(fullname, value) {
13776 return function () {
13777 var v = value.apply(this, arguments);
13778 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13782 function selection_attr (name, value) {
13783 var fullname = namespace(name);
13785 if (arguments.length < 2) {
13786 var node = this.node();
13787 return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13790 return this.each((value == null ? fullname.local ? attrRemoveNS$1 : attrRemove$1 : typeof value === "function" ? fullname.local ? attrFunctionNS$1 : attrFunction$1 : fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, value));
13793 function defaultView (node) {
13794 return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13795 node.document && node // node is a Window
13796 || node.defaultView; // node is a Document
13799 function styleRemove$1(name) {
13800 return function () {
13801 this.style.removeProperty(name);
13805 function styleConstant$1(name, value, priority) {
13806 return function () {
13807 this.style.setProperty(name, value, priority);
13811 function styleFunction$1(name, value, priority) {
13812 return function () {
13813 var v = value.apply(this, arguments);
13814 if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13818 function selection_style (name, value, priority) {
13819 return arguments.length > 1 ? this.each((value == null ? styleRemove$1 : typeof value === "function" ? styleFunction$1 : styleConstant$1)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13821 function styleValue(node, name) {
13822 return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13825 function propertyRemove(name) {
13826 return function () {
13831 function propertyConstant(name, value) {
13832 return function () {
13833 this[name] = value;
13837 function propertyFunction(name, value) {
13838 return function () {
13839 var v = value.apply(this, arguments);
13840 if (v == null) delete this[name];else this[name] = v;
13844 function selection_property (name, value) {
13845 return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13848 function classArray(string) {
13849 return string.trim().split(/^|\s+/);
13852 function classList(node) {
13853 return node.classList || new ClassList(node);
13856 function ClassList(node) {
13858 this._names = classArray(node.getAttribute("class") || "");
13861 ClassList.prototype = {
13862 add: function add(name) {
13863 var i = this._names.indexOf(name);
13866 this._names.push(name);
13868 this._node.setAttribute("class", this._names.join(" "));
13871 remove: function remove(name) {
13872 var i = this._names.indexOf(name);
13875 this._names.splice(i, 1);
13877 this._node.setAttribute("class", this._names.join(" "));
13880 contains: function contains(name) {
13881 return this._names.indexOf(name) >= 0;
13885 function classedAdd(node, names) {
13886 var list = classList(node),
13891 list.add(names[i]);
13895 function classedRemove(node, names) {
13896 var list = classList(node),
13901 list.remove(names[i]);
13905 function classedTrue(names) {
13906 return function () {
13907 classedAdd(this, names);
13911 function classedFalse(names) {
13912 return function () {
13913 classedRemove(this, names);
13917 function classedFunction(names, value) {
13918 return function () {
13919 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13923 function selection_classed (name, value) {
13924 var names = classArray(name + "");
13926 if (arguments.length < 2) {
13927 var list = classList(this.node()),
13932 if (!list.contains(names[i])) return false;
13938 return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13941 function textRemove() {
13942 this.textContent = "";
13945 function textConstant$1(value) {
13946 return function () {
13947 this.textContent = value;
13951 function textFunction$1(value) {
13952 return function () {
13953 var v = value.apply(this, arguments);
13954 this.textContent = v == null ? "" : v;
13958 function selection_text (value) {
13959 return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction$1 : textConstant$1)(value)) : this.node().textContent;
13962 function htmlRemove() {
13963 this.innerHTML = "";
13966 function htmlConstant(value) {
13967 return function () {
13968 this.innerHTML = value;
13972 function htmlFunction(value) {
13973 return function () {
13974 var v = value.apply(this, arguments);
13975 this.innerHTML = v == null ? "" : v;
13979 function selection_html (value) {
13980 return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13984 if (this.nextSibling) this.parentNode.appendChild(this);
13987 function selection_raise () {
13988 return this.each(raise);
13992 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13995 function selection_lower () {
13996 return this.each(lower);
13999 function selection_append (name) {
14000 var create = typeof name === "function" ? name : creator(name);
14001 return this.select(function () {
14002 return this.appendChild(create.apply(this, arguments));
14006 function constantNull() {
14010 function selection_insert (name, before) {
14011 var create = typeof name === "function" ? name : creator(name),
14012 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
14013 return this.select(function () {
14014 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
14018 function remove$7() {
14019 var parent = this.parentNode;
14020 if (parent) parent.removeChild(this);
14023 function selection_remove () {
14024 return this.each(remove$7);
14027 function selection_cloneShallow() {
14028 var clone = this.cloneNode(false),
14029 parent = this.parentNode;
14030 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14033 function selection_cloneDeep() {
14034 var clone = this.cloneNode(true),
14035 parent = this.parentNode;
14036 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
14039 function selection_clone (deep) {
14040 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
14043 function selection_datum (value) {
14044 return arguments.length ? this.property("__data__", value) : this.node().__data__;
14047 function contextListener(listener) {
14048 return function (event) {
14049 listener.call(this, event, this.__data__);
14053 function parseTypenames(typenames) {
14054 return typenames.trim().split(/^|\s+/).map(function (t) {
14056 i = t.indexOf(".");
14057 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
14065 function onRemove(typename) {
14066 return function () {
14067 var on = this.__on;
14070 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
14071 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
14072 this.removeEventListener(o.type, o.listener, o.options);
14078 if (++i) on.length = i;else delete this.__on;
14082 function onAdd(typename, value, options) {
14083 return function () {
14084 var on = this.__on,
14086 listener = contextListener(value);
14087 if (on) for (var j = 0, m = on.length; j < m; ++j) {
14088 if ((o = on[j]).type === typename.type && o.name === typename.name) {
14089 this.removeEventListener(o.type, o.listener, o.options);
14090 this.addEventListener(o.type, o.listener = listener, o.options = options);
14095 this.addEventListener(typename.type, listener, options);
14097 type: typename.type,
14098 name: typename.name,
14100 listener: listener,
14103 if (!on) this.__on = [o];else on.push(o);
14107 function selection_on (typename, value, options) {
14108 var typenames = parseTypenames(typename + ""),
14110 n = typenames.length,
14113 if (arguments.length < 2) {
14114 var on = this.node().__on;
14116 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
14117 for (i = 0, o = on[j]; i < n; ++i) {
14118 if ((t = typenames[i]).type === o.type && t.name === o.name) {
14126 on = value ? onAdd : onRemove;
14128 for (i = 0; i < n; ++i) {
14129 this.each(on(typenames[i], value, options));
14135 function dispatchEvent(node, type, params) {
14136 var window = defaultView(node),
14137 event = window.CustomEvent;
14139 if (typeof event === "function") {
14140 event = new event(type, params);
14142 event = window.document.createEvent("Event");
14143 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
14146 node.dispatchEvent(event);
14149 function dispatchConstant(type, params) {
14150 return function () {
14151 return dispatchEvent(this, type, params);
14155 function dispatchFunction(type, params) {
14156 return function () {
14157 return dispatchEvent(this, type, params.apply(this, arguments));
14161 function selection_dispatch (type, params) {
14162 return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
14165 var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(_callee);
14167 function _callee() {
14168 var groups, j, m, group, i, n, node;
14169 return regeneratorRuntime.wrap(function _callee$(_context) {
14171 switch (_context.prev = _context.next) {
14173 groups = this._groups, j = 0, m = groups.length;
14177 _context.next = 13;
14181 group = groups[j], i = 0, n = group.length;
14185 _context.next = 10;
14189 if (!(node = group[i])) {
14209 return _context.stop();
14212 }, _marked$1, this);
14215 var root$1 = [null];
14216 function Selection$1(groups, parents) {
14217 this._groups = groups;
14218 this._parents = parents;
14221 function selection() {
14222 return new Selection$1([[document.documentElement]], root$1);
14225 function selection_selection() {
14229 Selection$1.prototype = selection.prototype = _defineProperty({
14230 constructor: Selection$1,
14231 select: selection_select,
14232 selectAll: selection_selectAll,
14233 selectChild: selection_selectChild,
14234 selectChildren: selection_selectChildren,
14235 filter: selection_filter,
14236 data: selection_data,
14237 enter: selection_enter,
14238 exit: selection_exit,
14239 join: selection_join,
14240 merge: selection_merge,
14241 selection: selection_selection,
14242 order: selection_order,
14243 sort: selection_sort,
14244 call: selection_call,
14245 nodes: selection_nodes,
14246 node: selection_node,
14247 size: selection_size,
14248 empty: selection_empty,
14249 each: selection_each,
14250 attr: selection_attr,
14251 style: selection_style,
14252 property: selection_property,
14253 classed: selection_classed,
14254 text: selection_text,
14255 html: selection_html,
14256 raise: selection_raise,
14257 lower: selection_lower,
14258 append: selection_append,
14259 insert: selection_insert,
14260 remove: selection_remove,
14261 clone: selection_clone,
14262 datum: selection_datum,
14264 dispatch: selection_dispatch
14265 }, Symbol.iterator, _callee);
14267 function select (selector) {
14268 return typeof selector === "string" ? new Selection$1([[document.querySelector(selector)]], [document.documentElement]) : new Selection$1([[selector]], root$1);
14271 function sourceEvent (event) {
14274 while (sourceEvent = event.sourceEvent) {
14275 event = sourceEvent;
14281 function pointer (event, node) {
14282 event = sourceEvent(event);
14283 if (node === undefined) node = event.currentTarget;
14286 var svg = node.ownerSVGElement || node;
14288 if (svg.createSVGPoint) {
14289 var point = svg.createSVGPoint();
14290 point.x = event.clientX, point.y = event.clientY;
14291 point = point.matrixTransform(node.getScreenCTM().inverse());
14292 return [point.x, point.y];
14295 if (node.getBoundingClientRect) {
14296 var rect = node.getBoundingClientRect();
14297 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14301 return [event.pageX, event.pageY];
14304 function selectAll (selector) {
14305 return typeof selector === "string" ? new Selection$1([document.querySelectorAll(selector)], [document.documentElement]) : new Selection$1([selector == null ? [] : array(selector)], root$1);
14308 function nopropagation$1(event) {
14309 event.stopImmediatePropagation();
14311 function noevent$1 (event) {
14312 event.preventDefault();
14313 event.stopImmediatePropagation();
14316 function dragDisable (view) {
14317 var root = view.document.documentElement,
14318 selection = select(view).on("dragstart.drag", noevent$1, true);
14320 if ("onselectstart" in root) {
14321 selection.on("selectstart.drag", noevent$1, true);
14323 root.__noselect = root.style.MozUserSelect;
14324 root.style.MozUserSelect = "none";
14327 function yesdrag(view, noclick) {
14328 var root = view.document.documentElement,
14329 selection = select(view).on("dragstart.drag", null);
14332 selection.on("click.drag", noevent$1, true);
14333 setTimeout(function () {
14334 selection.on("click.drag", null);
14338 if ("onselectstart" in root) {
14339 selection.on("selectstart.drag", null);
14341 root.style.MozUserSelect = root.__noselect;
14342 delete root.__noselect;
14346 var constant$2 = (function (x) {
14347 return function () {
14352 function DragEvent(type, _ref) {
14353 var sourceEvent = _ref.sourceEvent,
14354 subject = _ref.subject,
14355 target = _ref.target,
14356 identifier = _ref.identifier,
14357 active = _ref.active,
14362 dispatch = _ref.dispatch;
14363 Object.defineProperties(this, {
14370 value: sourceEvent,
14420 DragEvent.prototype.on = function () {
14421 var value = this._.on.apply(this._, arguments);
14423 return value === this._ ? this : value;
14426 function defaultFilter$2(event) {
14427 return !event.ctrlKey && !event.button;
14430 function defaultContainer() {
14431 return this.parentNode;
14434 function defaultSubject(event, d) {
14435 return d == null ? {
14441 function defaultTouchable$1() {
14442 return navigator.maxTouchPoints || "ontouchstart" in this;
14445 function d3_drag () {
14446 var filter = defaultFilter$2,
14447 container = defaultContainer,
14448 subject = defaultSubject,
14449 touchable = defaultTouchable$1,
14451 listeners = dispatch$8("start", "drag", "end"),
14457 clickDistance2 = 0;
14459 function drag(selection) {
14460 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)");
14463 function mousedowned(event, d) {
14464 if (touchending || !filter.call(this, event, d)) return;
14465 var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14466 if (!gesture) return;
14467 select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14468 dragDisable(event.view);
14469 nopropagation$1(event);
14470 mousemoving = false;
14471 mousedownx = event.clientX;
14472 mousedowny = event.clientY;
14473 gesture("start", event);
14476 function mousemoved(event) {
14479 if (!mousemoving) {
14480 var dx = event.clientX - mousedownx,
14481 dy = event.clientY - mousedowny;
14482 mousemoving = dx * dx + dy * dy > clickDistance2;
14485 gestures.mouse("drag", event);
14488 function mouseupped(event) {
14489 select(event.view).on("mousemove.drag mouseup.drag", null);
14490 yesdrag(event.view, mousemoving);
14492 gestures.mouse("end", event);
14495 function touchstarted(event, d) {
14496 if (!filter.call(this, event, d)) return;
14497 var touches = event.changedTouches,
14498 c = container.call(this, event, d),
14499 n = touches.length,
14503 for (i = 0; i < n; ++i) {
14504 if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14505 nopropagation$1(event);
14506 gesture("start", event, touches[i]);
14511 function touchmoved(event) {
14512 var touches = event.changedTouches,
14513 n = touches.length,
14517 for (i = 0; i < n; ++i) {
14518 if (gesture = gestures[touches[i].identifier]) {
14520 gesture("drag", event, touches[i]);
14525 function touchended(event) {
14526 var touches = event.changedTouches,
14527 n = touches.length,
14530 if (touchending) clearTimeout(touchending);
14531 touchending = setTimeout(function () {
14532 touchending = null;
14533 }, 500); // Ghost clicks are delayed!
14535 for (i = 0; i < n; ++i) {
14536 if (gesture = gestures[touches[i].identifier]) {
14537 nopropagation$1(event);
14538 gesture("end", event, touches[i]);
14543 function beforestart(that, container, event, d, identifier, touch) {
14544 var dispatch = listeners.copy(),
14545 p = pointer(touch || event, container),
14549 if ((s = subject.call(that, new DragEvent("beforestart", {
14550 sourceEvent: event,
14552 identifier: identifier,
14559 }), d)) == null) return;
14560 dx = s.x - p[0] || 0;
14561 dy = s.y - p[1] || 0;
14562 return function gesture(type, event, touch) {
14568 gestures[identifier] = gesture, n = active++;
14572 delete gestures[identifier], --active;
14576 p = pointer(touch || event, container), n = active;
14580 dispatch.call(type, that, new DragEvent(type, {
14581 sourceEvent: event,
14584 identifier: identifier,
14595 drag.filter = function (_) {
14596 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter;
14599 drag.container = function (_) {
14600 return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container;
14603 drag.subject = function (_) {
14604 return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject;
14607 drag.touchable = function (_) {
14608 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable;
14611 drag.on = function () {
14612 var value = listeners.on.apply(listeners, arguments);
14613 return value === listeners ? drag : value;
14616 drag.clickDistance = function (_) {
14617 return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14623 var defineProperty$1 = objectDefineProperty.f;
14624 var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14631 var enforceInternalState = internalState.enforce;
14637 var MATCH$1 = wellKnownSymbol('match');
14638 var NativeRegExp = global$2.RegExp;
14639 var RegExpPrototype = NativeRegExp.prototype;
14640 // TODO: Use only propper RegExpIdentifierName
14641 var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/;
14645 // "new" should create a new object, old webkit bug
14646 var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14648 var UNSUPPORTED_Y = regexpStickyHelpers.UNSUPPORTED_Y;
14650 var BASE_FORCED = descriptors &&
14651 (!CORRECT_NEW || UNSUPPORTED_Y || regexpUnsupportedDotAll || regexpUnsupportedNcg || fails(function () {
14652 re2[MATCH$1] = false;
14653 // RegExp constructor can alter flags and IsRegExp works correct with @@match
14654 return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14657 var handleDotAll = function (string) {
14658 var length = string.length;
14661 var brackets = false;
14663 for (; index <= length; index++) {
14664 chr = string.charAt(index);
14665 if (chr === '\\') {
14666 result += chr + string.charAt(++index);
14669 if (!brackets && chr === '.') {
14670 result += '[\\s\\S]';
14674 } else if (chr === ']') {
14681 var handleNCG = function (string) {
14682 var length = string.length;
14687 var brackets = false;
14690 var groupname = '';
14692 for (; index <= length; index++) {
14693 chr = string.charAt(index);
14694 if (chr === '\\') {
14695 chr = chr + string.charAt(++index);
14696 } else if (chr === ']') {
14698 } else if (!brackets) switch (true) {
14703 if (IS_NCG.test(string.slice(index + 1))) {
14710 case chr === '>' && ncg:
14711 if (groupname === '' || has$1(names, groupname)) {
14712 throw new SyntaxError('Invalid capture group name');
14714 names[groupname] = true;
14715 named.push([groupname, groupid]);
14720 if (ncg) groupname += chr;
14721 else result += chr;
14722 } return [result, named];
14725 // `RegExp` constructor
14726 // https://tc39.es/ecma262/#sec-regexp-constructor
14727 if (isForced_1('RegExp', BASE_FORCED)) {
14728 var RegExpWrapper = function RegExp(pattern, flags) {
14729 var thisIsRegExp = this instanceof RegExpWrapper;
14730 var patternIsRegExp = isRegexp(pattern);
14731 var flagsAreUndefined = flags === undefined;
14733 var rawPattern, rawFlags, dotAll, sticky, handled, result, state;
14735 if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
14740 if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;
14741 } else if (pattern instanceof RegExpWrapper) {
14742 if (flagsAreUndefined) flags = regexpFlags.call(pattern);
14743 pattern = pattern.source;
14746 pattern = pattern === undefined ? '' : String(pattern);
14747 flags = flags === undefined ? '' : String(flags);
14748 rawPattern = pattern;
14750 if (regexpUnsupportedDotAll && 'dotAll' in re1) {
14751 dotAll = !!flags && flags.indexOf('s') > -1;
14752 if (dotAll) flags = flags.replace(/s/g, '');
14757 if (UNSUPPORTED_Y && 'sticky' in re1) {
14758 sticky = !!flags && flags.indexOf('y') > -1;
14759 if (sticky) flags = flags.replace(/y/g, '');
14762 if (regexpUnsupportedNcg) {
14763 handled = handleNCG(pattern);
14764 pattern = handled[0];
14765 groups = handled[1];
14768 result = inheritIfRequired(
14769 CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
14770 thisIsRegExp ? this : RegExpPrototype,
14774 if (dotAll || sticky || groups.length) {
14775 state = enforceInternalState(result);
14777 state.dotAll = true;
14778 state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags);
14780 if (sticky) state.sticky = true;
14781 if (groups.length) state.groups = groups;
14784 if (pattern !== rawPattern) try {
14785 // fails in old engines, but we have no alternatives for unsupported regex syntax
14786 createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern);
14787 } catch (error) { /* empty */ }
14792 var proxy = function (key) {
14793 key in RegExpWrapper || defineProperty$1(RegExpWrapper, key, {
14794 configurable: true,
14795 get: function () { return NativeRegExp[key]; },
14796 set: function (it) { NativeRegExp[key] = it; }
14800 for (var keys$1 = getOwnPropertyNames$1(NativeRegExp), index$1 = 0; keys$1.length > index$1;) {
14801 proxy(keys$1[index$1++]);
14804 RegExpPrototype.constructor = RegExpWrapper;
14805 RegExpWrapper.prototype = RegExpPrototype;
14806 redefine(global$2, 'RegExp', RegExpWrapper);
14809 // https://tc39.es/ecma262/#sec-get-regexp-@@species
14810 setSpecies('RegExp');
14812 function define (constructor, factory, prototype) {
14813 constructor.prototype = factory.prototype = prototype;
14814 prototype.constructor = constructor;
14816 function extend$3(parent, definition) {
14817 var prototype = Object.create(parent.prototype);
14819 for (var key in definition) {
14820 prototype[key] = definition[key];
14826 function Color() {}
14829 var _brighter = 1 / _darker;
14830 var reI = "\\s*([+-]?\\d+)\\s*",
14831 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14832 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14833 reHex = /^#([0-9a-f]{3,8})$/,
14834 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14835 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14836 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14837 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14838 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14839 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14841 aliceblue: 0xf0f8ff,
14842 antiquewhite: 0xfaebd7,
14844 aquamarine: 0x7fffd4,
14849 blanchedalmond: 0xffebcd,
14851 blueviolet: 0x8a2be2,
14853 burlywood: 0xdeb887,
14854 cadetblue: 0x5f9ea0,
14855 chartreuse: 0x7fff00,
14856 chocolate: 0xd2691e,
14858 cornflowerblue: 0x6495ed,
14859 cornsilk: 0xfff8dc,
14862 darkblue: 0x00008b,
14863 darkcyan: 0x008b8b,
14864 darkgoldenrod: 0xb8860b,
14865 darkgray: 0xa9a9a9,
14866 darkgreen: 0x006400,
14867 darkgrey: 0xa9a9a9,
14868 darkkhaki: 0xbdb76b,
14869 darkmagenta: 0x8b008b,
14870 darkolivegreen: 0x556b2f,
14871 darkorange: 0xff8c00,
14872 darkorchid: 0x9932cc,
14874 darksalmon: 0xe9967a,
14875 darkseagreen: 0x8fbc8f,
14876 darkslateblue: 0x483d8b,
14877 darkslategray: 0x2f4f4f,
14878 darkslategrey: 0x2f4f4f,
14879 darkturquoise: 0x00ced1,
14880 darkviolet: 0x9400d3,
14881 deeppink: 0xff1493,
14882 deepskyblue: 0x00bfff,
14885 dodgerblue: 0x1e90ff,
14886 firebrick: 0xb22222,
14887 floralwhite: 0xfffaf0,
14888 forestgreen: 0x228b22,
14890 gainsboro: 0xdcdcdc,
14891 ghostwhite: 0xf8f8ff,
14893 goldenrod: 0xdaa520,
14896 greenyellow: 0xadff2f,
14898 honeydew: 0xf0fff0,
14900 indianred: 0xcd5c5c,
14904 lavender: 0xe6e6fa,
14905 lavenderblush: 0xfff0f5,
14906 lawngreen: 0x7cfc00,
14907 lemonchiffon: 0xfffacd,
14908 lightblue: 0xadd8e6,
14909 lightcoral: 0xf08080,
14910 lightcyan: 0xe0ffff,
14911 lightgoldenrodyellow: 0xfafad2,
14912 lightgray: 0xd3d3d3,
14913 lightgreen: 0x90ee90,
14914 lightgrey: 0xd3d3d3,
14915 lightpink: 0xffb6c1,
14916 lightsalmon: 0xffa07a,
14917 lightseagreen: 0x20b2aa,
14918 lightskyblue: 0x87cefa,
14919 lightslategray: 0x778899,
14920 lightslategrey: 0x778899,
14921 lightsteelblue: 0xb0c4de,
14922 lightyellow: 0xffffe0,
14924 limegreen: 0x32cd32,
14928 mediumaquamarine: 0x66cdaa,
14929 mediumblue: 0x0000cd,
14930 mediumorchid: 0xba55d3,
14931 mediumpurple: 0x9370db,
14932 mediumseagreen: 0x3cb371,
14933 mediumslateblue: 0x7b68ee,
14934 mediumspringgreen: 0x00fa9a,
14935 mediumturquoise: 0x48d1cc,
14936 mediumvioletred: 0xc71585,
14937 midnightblue: 0x191970,
14938 mintcream: 0xf5fffa,
14939 mistyrose: 0xffe4e1,
14940 moccasin: 0xffe4b5,
14941 navajowhite: 0xffdead,
14945 olivedrab: 0x6b8e23,
14947 orangered: 0xff4500,
14949 palegoldenrod: 0xeee8aa,
14950 palegreen: 0x98fb98,
14951 paleturquoise: 0xafeeee,
14952 palevioletred: 0xdb7093,
14953 papayawhip: 0xffefd5,
14954 peachpuff: 0xffdab9,
14958 powderblue: 0xb0e0e6,
14960 rebeccapurple: 0x663399,
14962 rosybrown: 0xbc8f8f,
14963 royalblue: 0x4169e1,
14964 saddlebrown: 0x8b4513,
14966 sandybrown: 0xf4a460,
14967 seagreen: 0x2e8b57,
14968 seashell: 0xfff5ee,
14972 slateblue: 0x6a5acd,
14973 slategray: 0x708090,
14974 slategrey: 0x708090,
14976 springgreen: 0x00ff7f,
14977 steelblue: 0x4682b4,
14982 turquoise: 0x40e0d0,
14986 whitesmoke: 0xf5f5f5,
14988 yellowgreen: 0x9acd32
14990 define(Color, color, {
14991 copy: function copy(channels) {
14992 return Object.assign(new this.constructor(), this, channels);
14994 displayable: function displayable() {
14995 return this.rgb().displayable();
14997 hex: color_formatHex,
14998 // Deprecated! Use color.formatHex.
14999 formatHex: color_formatHex,
15000 formatHsl: color_formatHsl,
15001 formatRgb: color_formatRgb,
15002 toString: color_formatRgb
15005 function color_formatHex() {
15006 return this.rgb().formatHex();
15009 function color_formatHsl() {
15010 return hslConvert(this).formatHsl();
15013 function color_formatRgb() {
15014 return this.rgb().formatRgb();
15017 function color(format) {
15019 format = (format + "").trim().toLowerCase();
15020 return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
15021 : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
15022 : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
15023 : 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
15024 : null // invalid hex
15025 ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
15026 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
15027 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
15028 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
15029 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
15030 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
15031 : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
15032 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
15036 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
15039 function rgba(r, g, b, a) {
15040 if (a <= 0) r = g = b = NaN;
15041 return new Rgb(r, g, b, a);
15044 function rgbConvert(o) {
15045 if (!(o instanceof Color)) o = color(o);
15046 if (!o) return new Rgb();
15048 return new Rgb(o.r, o.g, o.b, o.opacity);
15050 function rgb(r, g, b, opacity) {
15051 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
15053 function Rgb(r, g, b, opacity) {
15057 this.opacity = +opacity;
15059 define(Rgb, rgb, extend$3(Color, {
15060 brighter: function brighter(k) {
15061 k = k == null ? _brighter : Math.pow(_brighter, k);
15062 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15064 darker: function darker(k) {
15065 k = k == null ? _darker : Math.pow(_darker, k);
15066 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
15068 rgb: function rgb() {
15071 displayable: function displayable() {
15072 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;
15074 hex: rgb_formatHex,
15075 // Deprecated! Use color.formatHex.
15076 formatHex: rgb_formatHex,
15077 formatRgb: rgb_formatRgb,
15078 toString: rgb_formatRgb
15081 function rgb_formatHex() {
15082 return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b);
15085 function rgb_formatRgb() {
15086 var a = this.opacity;
15087 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15088 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 + ")");
15091 function hex$1(value) {
15092 value = Math.max(0, Math.min(255, Math.round(value) || 0));
15093 return (value < 16 ? "0" : "") + value.toString(16);
15096 function hsla(h, s, l, a) {
15097 if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
15098 return new Hsl(h, s, l, a);
15101 function hslConvert(o) {
15102 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
15103 if (!(o instanceof Color)) o = color(o);
15104 if (!o) return new Hsl();
15105 if (o instanceof Hsl) return o;
15110 min = Math.min(r, g, b),
15111 max = Math.max(r, g, b),
15114 l = (max + min) / 2;
15117 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;
15118 s /= l < 0.5 ? max + min : 2 - max - min;
15121 s = l > 0 && l < 1 ? 0 : h;
15124 return new Hsl(h, s, l, o.opacity);
15126 function hsl(h, s, l, opacity) {
15127 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
15130 function Hsl(h, s, l, opacity) {
15134 this.opacity = +opacity;
15137 define(Hsl, hsl, extend$3(Color, {
15138 brighter: function brighter(k) {
15139 k = k == null ? _brighter : Math.pow(_brighter, k);
15140 return new Hsl(this.h, this.s, this.l * k, this.opacity);
15142 darker: function darker(k) {
15143 k = k == null ? _darker : Math.pow(_darker, k);
15144 return new Hsl(this.h, this.s, this.l * k, this.opacity);
15146 rgb: function rgb() {
15147 var h = this.h % 360 + (this.h < 0) * 360,
15148 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
15150 m2 = l + (l < 0.5 ? l : 1 - l) * s,
15152 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);
15154 displayable: function displayable() {
15155 return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
15157 formatHsl: function formatHsl() {
15158 var a = this.opacity;
15159 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
15160 return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
15163 /* From FvD 13.37, CSS Color Module Level 3 */
15165 function hsl2rgb(h, m1, m2) {
15166 return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
15169 var constant$1 = (function (x) {
15170 return function () {
15175 function linear$2(a, d) {
15176 return function (t) {
15181 function exponential(a, b, y) {
15182 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
15183 return Math.pow(a + t * b, y);
15186 function gamma(y) {
15187 return (y = +y) === 1 ? nogamma : function (a, b) {
15188 return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
15191 function nogamma(a, b) {
15193 return d ? linear$2(a, d) : constant$1(isNaN(a) ? b : a);
15196 var d3_interpolateRgb = (function rgbGamma(y) {
15197 var color = gamma(y);
15199 function rgb$1(start, end) {
15200 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
15201 g = color(start.g, end.g),
15202 b = color(start.b, end.b),
15203 opacity = nogamma(start.opacity, end.opacity);
15204 return function (t) {
15208 start.opacity = opacity(t);
15213 rgb$1.gamma = rgbGamma;
15217 function numberArray (a, b) {
15219 var n = a ? Math.min(b.length, a.length) : 0,
15222 return function (t) {
15223 for (i = 0; i < n; ++i) {
15224 c[i] = a[i] * (1 - t) + b[i] * t;
15230 function isNumberArray(x) {
15231 return ArrayBuffer.isView(x) && !(x instanceof DataView);
15234 function genericArray(a, b) {
15235 var nb = b ? b.length : 0,
15236 na = a ? Math.min(nb, a.length) : 0,
15241 for (i = 0; i < na; ++i) {
15242 x[i] = interpolate$1(a[i], b[i]);
15245 for (; i < nb; ++i) {
15249 return function (t) {
15250 for (i = 0; i < na; ++i) {
15258 function date (a, b) {
15259 var d = new Date();
15260 return a = +a, b = +b, function (t) {
15261 return d.setTime(a * (1 - t) + b * t), d;
15265 function d3_interpolateNumber (a, b) {
15266 return a = +a, b = +b, function (t) {
15267 return a * (1 - t) + b * t;
15271 function object (a, b) {
15275 if (a === null || _typeof(a) !== "object") a = {};
15276 if (b === null || _typeof(b) !== "object") b = {};
15280 i[k] = interpolate$1(a[k], b[k]);
15286 return function (t) {
15295 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
15296 reB = new RegExp(reA.source, "g");
15299 return function () {
15305 return function (t) {
15310 function interpolateString (a, b) {
15311 var bi = reA.lastIndex = reB.lastIndex = 0,
15312 // scan index for next number in b
15314 // current match in a
15316 // current match in b
15318 // string preceding current number in b, if any
15322 // string constants and placeholders
15323 q = []; // number interpolators
15324 // Coerce inputs to strings.
15326 a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15328 while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15329 if ((bs = bm.index) > bi) {
15330 // a string precedes the next number in b
15331 bs = b.slice(bi, bs);
15332 if (s[i]) s[i] += bs; // coalesce with previous string
15336 if ((am = am[0]) === (bm = bm[0])) {
15337 // numbers in a & b match
15338 if (s[i]) s[i] += bm; // coalesce with previous string
15341 // interpolate non-matching numbers
15345 x: d3_interpolateNumber(am, bm)
15349 bi = reB.lastIndex;
15350 } // Add remains of b.
15353 if (bi < b.length) {
15355 if (s[i]) s[i] += bs; // coalesce with previous string
15357 } // Special optimization for only a single match.
15358 // Otherwise, interpolate each of the numbers and rejoin the string.
15361 return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15362 for (var i = 0, o; i < b; ++i) {
15363 s[(o = q[i]).i] = o.x(t);
15370 function interpolate$1 (a, b) {
15371 var t = _typeof(b),
15374 return b == null || t === "boolean" ? constant$1(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);
15377 function interpolateRound (a, b) {
15378 return a = +a, b = +b, function (t) {
15379 return Math.round(a * (1 - t) + b * t);
15383 var degrees = 180 / Math.PI;
15392 function decompose (a, b, c, d, e, f) {
15393 var scaleX, scaleY, skewX;
15394 if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15395 if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15396 if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15397 if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15401 rotate: Math.atan2(b, a) * degrees,
15402 skewX: Math.atan(skewX) * degrees,
15409 /* eslint-disable no-undef */
15411 function parseCss(value) {
15412 var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15413 return m.isIdentity ? identity$3 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15415 function parseSvg(value) {
15416 if (value == null) return identity$3;
15417 if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15418 svgNode.setAttribute("transform", value);
15419 if (!(value = svgNode.transform.baseVal.consolidate())) return identity$3;
15420 value = value.matrix;
15421 return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15424 function interpolateTransform(parse, pxComma, pxParen, degParen) {
15426 return s.length ? s.pop() + " " : "";
15429 function translate(xa, ya, xb, yb, s, q) {
15430 if (xa !== xb || ya !== yb) {
15431 var i = s.push("translate(", null, pxComma, null, pxParen);
15434 x: d3_interpolateNumber(xa, xb)
15437 x: d3_interpolateNumber(ya, yb)
15439 } else if (xb || yb) {
15440 s.push("translate(" + xb + pxComma + yb + pxParen);
15444 function rotate(a, b, s, q) {
15446 if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15449 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15450 x: d3_interpolateNumber(a, b)
15453 s.push(pop(s) + "rotate(" + b + degParen);
15457 function skewX(a, b, s, q) {
15460 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15461 x: d3_interpolateNumber(a, b)
15464 s.push(pop(s) + "skewX(" + b + degParen);
15468 function scale(xa, ya, xb, yb, s, q) {
15469 if (xa !== xb || ya !== yb) {
15470 var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15473 x: d3_interpolateNumber(xa, xb)
15476 x: d3_interpolateNumber(ya, yb)
15478 } else if (xb !== 1 || yb !== 1) {
15479 s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15483 return function (a, b) {
15485 // string constants and placeholders
15486 q = []; // number interpolators
15488 a = parse(a), b = parse(b);
15489 translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15490 rotate(a.rotate, b.rotate, s, q);
15491 skewX(a.skewX, b.skewX, s, q);
15492 scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15493 a = b = null; // gc
15495 return function (t) {
15501 s[(o = q[i]).i] = o.x(t);
15509 var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15510 var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15512 var epsilon2 = 1e-12;
15515 return ((x = Math.exp(x)) + 1 / x) / 2;
15519 return ((x = Math.exp(x)) - 1 / x) / 2;
15523 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15526 var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15527 // p0 = [ux0, uy0, w0]
15528 // p1 = [ux1, uy1, w1]
15529 function zoom(p0, p1) {
15538 d2 = dx * dx + dy * dy,
15540 S; // Special case for u0 ≅ u1.
15542 if (d2 < epsilon2) {
15543 S = Math.log(w1 / w0) / rho;
15545 i = function i(t) {
15546 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15550 var d1 = Math.sqrt(d2),
15551 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15552 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15553 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15554 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15555 S = (r1 - r0) / rho;
15557 i = function i(t) {
15560 u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15561 return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15565 i.duration = S * 1000 * rho / Math.SQRT2;
15569 zoom.rho = function (_) {
15570 var _1 = Math.max(1e-3, +_),
15574 return zoomRho(_1, _2, _4);
15578 })(Math.SQRT2, 2, 4);
15580 function d3_quantize (interpolator, n) {
15581 var samples = new Array(n);
15583 for (var i = 0; i < n; ++i) {
15584 samples[i] = interpolator(i / (n - 1));
15590 // `Function.prototype.bind` method
15591 // https://tc39.es/ecma262/#sec-function.prototype.bind
15592 _export({ target: 'Function', proto: true }, {
15597 // is an animation frame pending?
15599 // is a timeout pending?
15601 // are any timers active?
15603 // how frequently we check for clock skew
15609 clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15610 setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15614 return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15617 function clearNow() {
15622 this._call = this._time = this._next = null;
15624 Timer.prototype = timer.prototype = {
15625 constructor: Timer,
15626 restart: function restart(callback, delay, time) {
15627 if (typeof callback !== "function") throw new TypeError("callback is not a function");
15628 time = (time == null ? now$1() : +time) + (delay == null ? 0 : +delay);
15630 if (!this._next && taskTail !== this) {
15631 if (taskTail) taskTail._next = this;else taskHead = this;
15635 this._call = callback;
15639 stop: function stop() {
15642 this._time = Infinity;
15647 function timer(callback, delay, time) {
15648 var t = new Timer();
15649 t.restart(callback, delay, time);
15652 function timerFlush() {
15653 now$1(); // Get the current time, if not already set.
15655 ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15661 if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15669 clockNow = (clockLast = clock.now()) + clockSkew;
15670 frame = timeout = 0;
15682 var now = clock.now(),
15683 delay = now - clockLast;
15684 if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15695 if (time > t1._time) time = t1._time;
15696 t0 = t1, t1 = t1._next;
15698 t2 = t1._next, t1._next = null;
15699 t1 = t0 ? t0._next = t2 : taskHead = t2;
15707 function sleep(time) {
15708 if (frame) return; // Soonest alarm already set, or will be.
15710 if (timeout) timeout = clearTimeout(timeout);
15711 var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15714 if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15715 if (interval) interval = clearInterval(interval);
15717 if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15718 frame = 1, setFrame(wake);
15722 function d3_timeout (callback, delay, time) {
15723 var t = new Timer();
15724 delay = delay == null ? 0 : +delay;
15725 t.restart(function (elapsed) {
15727 callback(elapsed + delay);
15732 var emptyOn = dispatch$8("start", "end", "cancel", "interrupt");
15733 var emptyTween = [];
15741 function schedule (node, name, id, index, group, timing) {
15742 var schedules = node.__transition;
15743 if (!schedules) node.__transition = {};else if (id in schedules) return;
15744 create$2(node, id, {
15747 // For context during callback.
15749 // For context during callback.
15753 delay: timing.delay,
15754 duration: timing.duration,
15760 function init(node, id) {
15761 var schedule = get$1(node, id);
15762 if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15765 function set(node, id) {
15766 var schedule = get$1(node, id);
15767 if (schedule.state > STARTED) throw new Error("too late; already running");
15770 function get$1(node, id) {
15771 var schedule = node.__transition;
15772 if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15776 function create$2(node, id, self) {
15777 var schedules = node.__transition,
15778 tween; // Initialize the self timer when the transition is created.
15779 // Note the actual delay is not known until the first callback!
15781 schedules[id] = self;
15782 self.timer = timer(schedule, 0, self.time);
15784 function schedule(elapsed) {
15785 self.state = SCHEDULED;
15786 self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15788 if (self.delay <= elapsed) start(elapsed - self.delay);
15791 function start(elapsed) {
15792 var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15794 if (self.state !== SCHEDULED) return stop();
15796 for (i in schedules) {
15798 if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15799 // defer starting an interrupting transition until that transition has a
15800 // chance to tick (and possibly end); see d3/d3-transition#54!
15802 if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15804 if (o.state === RUNNING) {
15807 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15808 delete schedules[i];
15809 } // Cancel any pre-empted transitions.
15810 else if (+i < id) {
15813 o.on.call("cancel", node, node.__data__, o.index, o.group);
15814 delete schedules[i];
15816 } // Defer the first tick to end of the current frame; see d3/d3#1576.
15817 // Note the transition may be canceled after start and before the first tick!
15818 // Note this must be scheduled before the start event; see d3/d3-transition#16!
15819 // Assuming this is successful, subsequent callbacks go straight to tick.
15822 d3_timeout(function () {
15823 if (self.state === STARTED) {
15824 self.state = RUNNING;
15825 self.timer.restart(tick, self.delay, self.time);
15828 }); // Dispatch the start event.
15829 // Note this must be done before the tween are initialized.
15831 self.state = STARTING;
15832 self.on.call("start", node, node.__data__, self.index, self.group);
15833 if (self.state !== STARTING) return; // interrupted
15835 self.state = STARTED; // Initialize the tween, deleting null tween.
15837 tween = new Array(n = self.tween.length);
15839 for (i = 0, j = -1; i < n; ++i) {
15840 if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15845 tween.length = j + 1;
15848 function tick(elapsed) {
15849 var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15854 tween[i].call(node, t);
15855 } // Dispatch the end event.
15858 if (self.state === ENDING) {
15859 self.on.call("end", node, node.__data__, self.index, self.group);
15865 self.state = ENDED;
15867 delete schedules[id];
15869 for (var i in schedules) {
15871 } // eslint-disable-line no-unused-vars
15874 delete node.__transition;
15878 function interrupt (node, name) {
15879 var schedules = node.__transition,
15884 if (!schedules) return;
15885 name = name == null ? null : name + "";
15887 for (i in schedules) {
15888 if ((schedule = schedules[i]).name !== name) {
15893 active = schedule.state > STARTING && schedule.state < ENDING;
15894 schedule.state = ENDED;
15895 schedule.timer.stop();
15896 schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15897 delete schedules[i];
15900 if (empty) delete node.__transition;
15903 function selection_interrupt (name) {
15904 return this.each(function () {
15905 interrupt(this, name);
15909 function tweenRemove(id, name) {
15910 var tween0, tween1;
15911 return function () {
15912 var schedule = set(this, id),
15913 tween = schedule.tween; // If this node shared tween with the previous node,
15914 // just assign the updated shared tween and we’re done!
15915 // Otherwise, copy-on-write.
15917 if (tween !== tween0) {
15918 tween1 = tween0 = tween;
15920 for (var i = 0, n = tween1.length; i < n; ++i) {
15921 if (tween1[i].name === name) {
15922 tween1 = tween1.slice();
15923 tween1.splice(i, 1);
15929 schedule.tween = tween1;
15933 function tweenFunction(id, name, value) {
15934 var tween0, tween1;
15935 if (typeof value !== "function") throw new Error();
15936 return function () {
15937 var schedule = set(this, id),
15938 tween = schedule.tween; // If this node shared tween with the previous node,
15939 // just assign the updated shared tween and we’re done!
15940 // Otherwise, copy-on-write.
15942 if (tween !== tween0) {
15943 tween1 = (tween0 = tween).slice();
15948 }, i = 0, n = tween1.length; i < n; ++i) {
15949 if (tween1[i].name === name) {
15955 if (i === n) tween1.push(t);
15958 schedule.tween = tween1;
15962 function transition_tween (name, value) {
15966 if (arguments.length < 2) {
15967 var tween = get$1(this.node(), id).tween;
15969 for (var i = 0, n = tween.length, t; i < n; ++i) {
15970 if ((t = tween[i]).name === name) {
15978 return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15980 function tweenValue(transition, name, value) {
15981 var id = transition._id;
15982 transition.each(function () {
15983 var schedule = set(this, id);
15984 (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15986 return function (node) {
15987 return get$1(node, id).value[name];
15991 function interpolate (a, b) {
15993 return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15996 function attrRemove(name) {
15997 return function () {
15998 this.removeAttribute(name);
16002 function attrRemoveNS(fullname) {
16003 return function () {
16004 this.removeAttributeNS(fullname.space, fullname.local);
16008 function attrConstant(name, interpolate, value1) {
16010 string1 = value1 + "",
16012 return function () {
16013 var string0 = this.getAttribute(name);
16014 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16018 function attrConstantNS(fullname, interpolate, value1) {
16020 string1 = value1 + "",
16022 return function () {
16023 var string0 = this.getAttributeNS(fullname.space, fullname.local);
16024 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16028 function attrFunction(name, interpolate, value) {
16029 var string00, string10, interpolate0;
16030 return function () {
16032 value1 = value(this),
16034 if (value1 == null) return void this.removeAttribute(name);
16035 string0 = this.getAttribute(name);
16036 string1 = value1 + "";
16037 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16041 function attrFunctionNS(fullname, interpolate, value) {
16042 var string00, string10, interpolate0;
16043 return function () {
16045 value1 = value(this),
16047 if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
16048 string0 = this.getAttributeNS(fullname.space, fullname.local);
16049 string1 = value1 + "";
16050 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16054 function transition_attr (name, value) {
16055 var fullname = namespace(name),
16056 i = fullname === "transform" ? interpolateTransformSvg : interpolate;
16057 return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));
16060 function attrInterpolate(name, i) {
16061 return function (t) {
16062 this.setAttribute(name, i.call(this, t));
16066 function attrInterpolateNS(fullname, i) {
16067 return function (t) {
16068 this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
16072 function attrTweenNS(fullname, value) {
16076 var i = value.apply(this, arguments);
16077 if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
16081 tween._value = value;
16085 function attrTween(name, value) {
16089 var i = value.apply(this, arguments);
16090 if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
16094 tween._value = value;
16098 function transition_attrTween (name, value) {
16099 var key = "attr." + name;
16100 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16101 if (value == null) return this.tween(key, null);
16102 if (typeof value !== "function") throw new Error();
16103 var fullname = namespace(name);
16104 return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
16107 function delayFunction(id, value) {
16108 return function () {
16109 init(this, id).delay = +value.apply(this, arguments);
16113 function delayConstant(id, value) {
16114 return value = +value, function () {
16115 init(this, id).delay = value;
16119 function transition_delay (value) {
16121 return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$1(this.node(), id).delay;
16124 function durationFunction(id, value) {
16125 return function () {
16126 set(this, id).duration = +value.apply(this, arguments);
16130 function durationConstant(id, value) {
16131 return value = +value, function () {
16132 set(this, id).duration = value;
16136 function transition_duration (value) {
16138 return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$1(this.node(), id).duration;
16141 function easeConstant(id, value) {
16142 if (typeof value !== "function") throw new Error();
16143 return function () {
16144 set(this, id).ease = value;
16148 function transition_ease (value) {
16150 return arguments.length ? this.each(easeConstant(id, value)) : get$1(this.node(), id).ease;
16153 function easeVarying(id, value) {
16154 return function () {
16155 var v = value.apply(this, arguments);
16156 if (typeof v !== "function") throw new Error();
16157 set(this, id).ease = v;
16161 function transition_easeVarying (value) {
16162 if (typeof value !== "function") throw new Error();
16163 return this.each(easeVarying(this._id, value));
16166 function transition_filter (match) {
16167 if (typeof match !== "function") match = matcher(match);
16169 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16170 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
16171 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
16172 subgroup.push(node);
16177 return new Transition(subgroups, this._parents, this._name, this._id);
16180 function transition_merge (transition) {
16181 if (transition._id !== this._id) throw new Error();
16183 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) {
16184 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
16185 if (node = group0[i] || group1[i]) {
16191 for (; j < m0; ++j) {
16192 merges[j] = groups0[j];
16195 return new Transition(merges, this._parents, this._name, this._id);
16198 function start(name) {
16199 return (name + "").trim().split(/^|\s+/).every(function (t) {
16200 var i = t.indexOf(".");
16201 if (i >= 0) t = t.slice(0, i);
16202 return !t || t === "start";
16206 function onFunction(id, name, listener) {
16209 sit = start(name) ? init : set;
16210 return function () {
16211 var schedule = sit(this, id),
16212 on = schedule.on; // If this node shared a dispatch with the previous node,
16213 // just assign the updated shared dispatch and we’re done!
16214 // Otherwise, copy-on-write.
16216 if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
16221 function transition_on (name, listener) {
16223 return arguments.length < 2 ? get$1(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
16226 function removeFunction(id) {
16227 return function () {
16228 var parent = this.parentNode;
16230 for (var i in this.__transition) {
16231 if (+i !== id) return;
16234 if (parent) parent.removeChild(this);
16238 function transition_remove () {
16239 return this.on("end.remove", removeFunction(this._id));
16242 function transition_select (select) {
16243 var name = this._name,
16245 if (typeof select !== "function") select = selector(select);
16247 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
16248 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
16249 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
16250 if ("__data__" in node) subnode.__data__ = node.__data__;
16251 subgroup[i] = subnode;
16252 schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
16257 return new Transition(subgroups, this._parents, name, id);
16260 function transition_selectAll (select) {
16261 var name = this._name,
16263 if (typeof select !== "function") select = selectorAll(select);
16265 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
16266 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16267 if (node = group[i]) {
16268 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
16269 if (child = children[k]) {
16270 schedule(child, name, id, k, children, inherit);
16274 subgroups.push(children);
16275 parents.push(node);
16280 return new Transition(subgroups, parents, name, id);
16283 var Selection = selection.prototype.constructor;
16284 function transition_selection () {
16285 return new Selection(this._groups, this._parents);
16288 function styleNull(name, interpolate) {
16289 var string00, string10, interpolate0;
16290 return function () {
16291 var string0 = styleValue(this, name),
16292 string1 = (this.style.removeProperty(name), styleValue(this, name));
16293 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
16297 function styleRemove(name) {
16298 return function () {
16299 this.style.removeProperty(name);
16303 function styleConstant(name, interpolate, value1) {
16305 string1 = value1 + "",
16307 return function () {
16308 var string0 = styleValue(this, name);
16309 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16313 function styleFunction(name, interpolate, value) {
16314 var string00, string10, interpolate0;
16315 return function () {
16316 var string0 = styleValue(this, name),
16317 value1 = value(this),
16318 string1 = value1 + "";
16319 if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16320 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16324 function styleMaybeRemove(id, name) {
16328 key = "style." + name,
16329 event = "end." + key,
16331 return function () {
16332 var schedule = set(this, id),
16334 listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined; // If this node shared a dispatch with the previous node,
16335 // just assign the updated shared dispatch and we’re done!
16336 // Otherwise, copy-on-write.
16338 if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16343 function transition_style (name, value, priority) {
16344 var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
16345 return value == null ? this.styleTween(name, styleNull(name, i)).on("end.style." + name, styleRemove(name)) : typeof value === "function" ? this.styleTween(name, styleFunction(name, i, tweenValue(this, "style." + name, value))).each(styleMaybeRemove(this._id, name)) : this.styleTween(name, styleConstant(name, i, value), priority).on("end.style." + name, null);
16348 function styleInterpolate(name, i, priority) {
16349 return function (t) {
16350 this.style.setProperty(name, i.call(this, t), priority);
16354 function styleTween(name, value, priority) {
16358 var i = value.apply(this, arguments);
16359 if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16363 tween._value = value;
16367 function transition_styleTween (name, value, priority) {
16368 var key = "style." + (name += "");
16369 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16370 if (value == null) return this.tween(key, null);
16371 if (typeof value !== "function") throw new Error();
16372 return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16375 function textConstant(value) {
16376 return function () {
16377 this.textContent = value;
16381 function textFunction(value) {
16382 return function () {
16383 var value1 = value(this);
16384 this.textContent = value1 == null ? "" : value1;
16388 function transition_text (value) {
16389 return this.tween("text", typeof value === "function" ? textFunction(tweenValue(this, "text", value)) : textConstant(value == null ? "" : value + ""));
16392 function textInterpolate(i) {
16393 return function (t) {
16394 this.textContent = i.call(this, t);
16398 function textTween(value) {
16402 var i = value.apply(this, arguments);
16403 if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16407 tween._value = value;
16411 function transition_textTween (value) {
16413 if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16414 if (value == null) return this.tween(key, null);
16415 if (typeof value !== "function") throw new Error();
16416 return this.tween(key, textTween(value));
16419 function transition_transition () {
16420 var name = this._name,
16424 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16425 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16426 if (node = group[i]) {
16427 var inherit = get$1(node, id0);
16428 schedule(node, name, id1, i, group, {
16429 time: inherit.time + inherit.delay + inherit.duration,
16431 duration: inherit.duration,
16438 return new Transition(groups, this._parents, name, id1);
16441 function transition_end () {
16446 size = that.size();
16447 return new Promise(function (resolve, reject) {
16452 value: function value() {
16453 if (--size === 0) resolve();
16456 that.each(function () {
16457 var schedule = set(this, id),
16458 on = schedule.on; // If this node shared a dispatch with the previous node,
16459 // just assign the updated shared dispatch and we’re done!
16460 // Otherwise, copy-on-write.
16463 on1 = (on0 = on).copy();
16465 on1._.cancel.push(cancel);
16467 on1._.interrupt.push(cancel);
16469 on1._.end.push(end);
16473 }); // The selection was empty, resolve end immediately
16475 if (size === 0) resolve();
16480 function Transition(groups, parents, name, id) {
16481 this._groups = groups;
16482 this._parents = parents;
16489 var selection_prototype = selection.prototype;
16490 Transition.prototype = _defineProperty({
16491 constructor: Transition,
16492 select: transition_select,
16493 selectAll: transition_selectAll,
16494 filter: transition_filter,
16495 merge: transition_merge,
16496 selection: transition_selection,
16497 transition: transition_transition,
16498 call: selection_prototype.call,
16499 nodes: selection_prototype.nodes,
16500 node: selection_prototype.node,
16501 size: selection_prototype.size,
16502 empty: selection_prototype.empty,
16503 each: selection_prototype.each,
16505 attr: transition_attr,
16506 attrTween: transition_attrTween,
16507 style: transition_style,
16508 styleTween: transition_styleTween,
16509 text: transition_text,
16510 textTween: transition_textTween,
16511 remove: transition_remove,
16512 tween: transition_tween,
16513 delay: transition_delay,
16514 duration: transition_duration,
16515 ease: transition_ease,
16516 easeVarying: transition_easeVarying,
16517 end: transition_end
16518 }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16520 var linear$1 = function linear(t) {
16524 function cubicInOut(t) {
16525 return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16528 var defaultTiming = {
16536 function inherit(node, id) {
16539 while (!(timing = node.__transition) || !(timing = timing[id])) {
16540 if (!(node = node.parentNode)) {
16541 throw new Error("transition ".concat(id, " not found"));
16548 function selection_transition (name) {
16551 if (name instanceof Transition) {
16552 id = name._id, name = name._name;
16554 id = newId(), (timing = defaultTiming).time = now$1(), name = name == null ? null : name + "";
16557 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16558 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16559 if (node = group[i]) {
16560 schedule(node, name, id, i, group, timing || inherit(node, id));
16565 return new Transition(groups, this._parents, name, id);
16568 selection.prototype.interrupt = selection_interrupt;
16569 selection.prototype.transition = selection_transition;
16571 var constant = (function (x) {
16572 return function () {
16577 function ZoomEvent(type, _ref) {
16578 var sourceEvent = _ref.sourceEvent,
16579 target = _ref.target,
16580 transform = _ref.transform,
16581 dispatch = _ref.dispatch;
16582 Object.defineProperties(this, {
16589 value: sourceEvent,
16609 function Transform(k, x, y) {
16614 Transform.prototype = {
16615 constructor: Transform,
16616 scale: function scale(k) {
16617 return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16619 translate: function translate(x, y) {
16620 return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16622 apply: function apply(point) {
16623 return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16625 applyX: function applyX(x) {
16626 return x * this.k + this.x;
16628 applyY: function applyY(y) {
16629 return y * this.k + this.y;
16631 invert: function invert(location) {
16632 return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16634 invertX: function invertX(x) {
16635 return (x - this.x) / this.k;
16637 invertY: function invertY(y) {
16638 return (y - this.y) / this.k;
16640 rescaleX: function rescaleX(x) {
16641 return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16643 rescaleY: function rescaleY(y) {
16644 return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16646 toString: function toString() {
16647 return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16650 var identity$2 = new Transform(1, 0, 0);
16652 function nopropagation(event) {
16653 event.stopImmediatePropagation();
16655 function noevent (event) {
16656 event.preventDefault();
16657 event.stopImmediatePropagation();
16660 // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16662 function defaultFilter$1(event) {
16663 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16666 function defaultExtent$1() {
16669 if (e instanceof SVGElement) {
16670 e = e.ownerSVGElement || e;
16672 if (e.hasAttribute("viewBox")) {
16673 e = e.viewBox.baseVal;
16674 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16677 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16680 return [[0, 0], [e.clientWidth, e.clientHeight]];
16683 function defaultTransform() {
16684 return this.__zoom || identity$2;
16687 function defaultWheelDelta$1(event) {
16688 return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16691 function defaultTouchable() {
16692 return navigator.maxTouchPoints || "ontouchstart" in this;
16695 function defaultConstrain$1(transform, extent, translateExtent) {
16696 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16697 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16698 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16699 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16700 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));
16703 function d3_zoom () {
16704 var filter = defaultFilter$1,
16705 extent = defaultExtent$1,
16706 constrain = defaultConstrain$1,
16707 wheelDelta = defaultWheelDelta$1,
16708 touchable = defaultTouchable,
16709 scaleExtent = [0, Infinity],
16710 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16712 interpolate = interpolateZoom,
16713 listeners = dispatch$8("start", "zoom", "end"),
16719 clickDistance2 = 0,
16722 function zoom(selection) {
16723 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("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
16726 zoom.transform = function (collection, transform, point, event) {
16727 var selection = collection.selection ? collection.selection() : collection;
16728 selection.property("__zoom", defaultTransform);
16730 if (collection !== selection) {
16731 schedule(collection, transform, point, event);
16733 selection.interrupt().each(function () {
16734 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16739 zoom.scaleBy = function (selection, k, p, event) {
16740 zoom.scaleTo(selection, function () {
16741 var k0 = this.__zoom.k,
16742 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16747 zoom.scaleTo = function (selection, k, p, event) {
16748 zoom.transform(selection, function () {
16749 var e = extent.apply(this, arguments),
16751 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16752 p1 = t0.invert(p0),
16753 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16754 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16758 zoom.translateBy = function (selection, x, y, event) {
16759 zoom.transform(selection, function () {
16760 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);
16764 zoom.translateTo = function (selection, x, y, p, event) {
16765 zoom.transform(selection, function () {
16766 var e = extent.apply(this, arguments),
16768 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16769 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);
16773 function scale(transform, k) {
16774 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16775 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16778 function translate(transform, p0, p1) {
16779 var x = p0[0] - p1[0] * transform.k,
16780 y = p0[1] - p1[1] * transform.k;
16781 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16784 function centroid(extent) {
16785 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16788 function schedule(transition, transform, point, event) {
16789 transition.on("start.zoom", function () {
16790 gesture(this, arguments).event(event).start();
16791 }).on("interrupt.zoom end.zoom", function () {
16792 gesture(this, arguments).event(event).end();
16793 }).tween("zoom", function () {
16796 g = gesture(that, args).event(event),
16797 e = extent.apply(that, args),
16798 p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16799 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16801 b = typeof transform === "function" ? transform.apply(that, args) : transform,
16802 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16803 return function (t) {
16804 if (t === 1) t = b; // Avoid rounding error on end.
16808 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16815 function gesture(that, args, clean) {
16816 return !clean && that.__zooming || new Gesture(that, args);
16819 function Gesture(that, args) {
16823 this.sourceEvent = null;
16824 this.extent = extent.apply(that, args);
16828 Gesture.prototype = {
16829 event: function event(_event) {
16830 if (_event) this.sourceEvent = _event;
16833 start: function start() {
16834 if (++this.active === 1) {
16835 this.that.__zooming = this;
16836 this.emit("start");
16841 zoom: function zoom(key, transform) {
16842 if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16843 if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16844 if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16845 this.that.__zoom = transform;
16849 end: function end() {
16850 if (--this.active === 0) {
16851 delete this.that.__zooming;
16857 emit: function emit(type) {
16858 var d = select(this.that).datum();
16859 listeners.call(type, this.that, new ZoomEvent(type, {
16860 sourceEvent: this.sourceEvent,
16863 transform: this.that.__zoom,
16864 dispatch: listeners
16869 function wheeled(event) {
16870 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16871 args[_key - 1] = arguments[_key];
16874 if (!filter.apply(this, arguments)) return;
16875 var g = gesture(this, args).event(event),
16877 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16878 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16879 // If there were recent wheel events, reset the wheel idle timeout.
16882 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16883 g.mouse[1] = t.invert(g.mouse[0] = p);
16886 clearTimeout(g.wheel);
16887 } // If this wheel event won’t trigger a transform change, ignore it.
16888 else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16890 g.mouse = [p, t.invert(p)];
16896 g.wheel = setTimeout(wheelidled, wheelDelay);
16897 g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16899 function wheelidled() {
16905 function mousedowned(event) {
16906 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16907 args[_key2 - 1] = arguments[_key2];
16910 if (touchending || !filter.apply(this, arguments)) return;
16911 var g = gesture(this, args, true).event(event),
16912 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16913 p = pointer(event, currentTarget),
16914 currentTarget = event.currentTarget,
16915 x0 = event.clientX,
16916 y0 = event.clientY;
16917 dragDisable(event.view);
16918 nopropagation(event);
16919 g.mouse = [p, this.__zoom.invert(p)];
16923 function mousemoved(event) {
16927 var dx = event.clientX - x0,
16928 dy = event.clientY - y0;
16929 g.moved = dx * dx + dy * dy > clickDistance2;
16932 g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16935 function mouseupped(event) {
16936 v.on("mousemove.zoom mouseup.zoom", null);
16937 yesdrag(event.view, g.moved);
16939 g.event(event).end();
16943 function dblclicked(event) {
16944 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16945 args[_key3 - 1] = arguments[_key3];
16948 if (!filter.apply(this, arguments)) return;
16949 var t0 = this.__zoom,
16950 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16951 p1 = t0.invert(p0),
16952 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16953 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16955 if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16958 function touchstarted(event) {
16959 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16960 args[_key4 - 1] = arguments[_key4];
16963 if (!filter.apply(this, arguments)) return;
16964 var touches = event.touches,
16965 n = touches.length,
16966 g = gesture(this, args, event.changedTouches.length === n).event(event),
16971 nopropagation(event);
16973 for (i = 0; i < n; ++i) {
16974 t = touches[i], p = pointer(t, this);
16975 p = [p, this.__zoom.invert(p), t.identifier];
16976 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;
16979 if (touchstarting) touchstarting = clearTimeout(touchstarting);
16982 if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16983 touchstarting = null;
16990 function touchmoved(event) {
16991 if (!this.__zooming) return;
16993 for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16994 args[_key5 - 1] = arguments[_key5];
16997 var g = gesture(this, args).event(event),
16998 touches = event.changedTouches,
16999 n = touches.length,
17006 for (i = 0; i < n; ++i) {
17007 t = touches[i], p = pointer(t, this);
17008 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;
17014 var p0 = g.touch0[0],
17018 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
17019 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
17020 t = scale(t, Math.sqrt(dp / dl));
17021 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
17022 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
17023 } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
17025 g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
17028 function touchended(event) {
17029 for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
17030 args[_key6 - 1] = arguments[_key6];
17033 if (!this.__zooming) return;
17034 var g = gesture(this, args).event(event),
17035 touches = event.changedTouches,
17036 n = touches.length,
17039 nopropagation(event);
17040 if (touchending) clearTimeout(touchending);
17041 touchending = setTimeout(function () {
17042 touchending = null;
17045 for (i = 0; i < n; ++i) {
17047 if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
17050 if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
17051 if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
17052 g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
17054 if (g.taps === 2) {
17055 t = pointer(t, this);
17057 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
17058 var p = select(this).on("dblclick.zoom");
17059 if (p) p.apply(this, arguments);
17065 zoom.wheelDelta = function (_) {
17066 return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant(+_), zoom) : wheelDelta;
17069 zoom.filter = function (_) {
17070 return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), zoom) : filter;
17073 zoom.touchable = function (_) {
17074 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable;
17077 zoom.extent = function (_) {
17078 return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
17081 zoom.scaleExtent = function (_) {
17082 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
17085 zoom.translateExtent = function (_) {
17086 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]]];
17089 zoom.constrain = function (_) {
17090 return arguments.length ? (constrain = _, zoom) : constrain;
17093 zoom.duration = function (_) {
17094 return arguments.length ? (duration = +_, zoom) : duration;
17097 zoom.interpolate = function (_) {
17098 return arguments.length ? (interpolate = _, zoom) : interpolate;
17101 zoom.on = function () {
17102 var value = listeners.on.apply(listeners, arguments);
17103 return value === listeners ? zoom : value;
17106 zoom.clickDistance = function (_) {
17107 return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
17110 zoom.tapDistance = function (_) {
17111 return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
17118 Bypasses features of D3's default projection stream pipeline that are unnecessary:
17119 * Antimeridian clipping
17120 * Spherical rotation
17124 function geoRawMercator() {
17125 var project = mercatorRaw;
17126 var k = 512 / Math.PI; // scale
17129 var y = 0; // translate
17131 var clipExtent = [[0, 0], [0, 0]];
17133 function projection(point) {
17134 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
17135 return [point[0] * k + x, y - point[1] * k];
17138 projection.invert = function (point) {
17139 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
17140 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
17143 projection.scale = function (_) {
17144 if (!arguments.length) return k;
17149 projection.translate = function (_) {
17150 if (!arguments.length) return [x, y];
17156 projection.clipExtent = function (_) {
17157 if (!arguments.length) return clipExtent;
17162 projection.transform = function (obj) {
17163 if (!arguments.length) return identity$2.translate(x, y).scale(k);
17170 projection.stream = d3_geoTransform({
17171 point: function point(x, y) {
17172 var vec = projection([x, y]);
17173 this.stream.point(vec[0], vec[1]);
17179 function geoOrthoNormalizedDotProduct(a, b, origin) {
17180 if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
17181 return 1; // coincident points, treat as straight and try to remove
17184 return geoVecNormalizedDot(a, b, origin);
17187 function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
17188 var val = Math.abs(dotp);
17190 if (val < epsilon) {
17191 return 0; // already orthogonal
17192 } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
17193 return 0; // straight angle, which is okay in this case
17194 } else if (val < lowerThreshold || val > upperThreshold) {
17195 return dotp; // can be adjusted
17197 return null; // ignore vertex
17201 function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
17203 var first = isClosed ? 0 : 1;
17204 var last = isClosed ? points.length : points.length - 1;
17205 var coords = points.map(function (p) {
17208 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17209 var upperThreshold = Math.cos(threshold * Math.PI / 180);
17211 for (var i = first; i < last; i++) {
17212 var a = coords[(i - 1 + coords.length) % coords.length];
17213 var origin = coords[i];
17214 var b = coords[(i + 1) % coords.length];
17215 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
17216 if (dotp === null) continue; // ignore vertex
17218 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
17222 } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
17224 function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
17225 var max = -Infinity;
17226 var first = isClosed ? 0 : 1;
17227 var last = isClosed ? coords.length : coords.length - 1;
17229 for (var i = first; i < last; i++) {
17230 var a = coords[(i - 1 + coords.length) % coords.length];
17231 var origin = coords[i];
17232 var b = coords[(i + 1) % coords.length];
17233 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
17234 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
17235 if (angle > 45) angle = 90 - angle;
17236 if (angle >= lessThan) continue;
17237 if (angle > max) max = angle;
17240 if (max === -Infinity) return null;
17242 } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
17244 function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
17246 var first = isClosed ? 0 : 1;
17247 var last = isClosed ? coords.length : coords.length - 1;
17248 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
17249 var upperThreshold = Math.cos(threshold * Math.PI / 180);
17251 for (var i = first; i < last; i++) {
17252 var a = coords[(i - 1 + coords.length) % coords.length];
17253 var origin = coords[i];
17254 var b = coords[(i + 1) % coords.length];
17255 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
17256 if (dotp === null) continue; // ignore vertex
17258 if (Math.abs(dotp) > 0) return 1; // something to do
17260 score = 0; // already square
17266 var onFreeze = internalMetadata.onFreeze;
17268 // eslint-disable-next-line es/no-object-freeze -- safe
17269 var $freeze = Object.freeze;
17270 var FAILS_ON_PRIMITIVES = fails(function () { $freeze(1); });
17272 // `Object.freeze` method
17273 // https://tc39.es/ecma262/#sec-object.freeze
17274 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES, sham: !freezing }, {
17275 freeze: function freeze(it) {
17276 return $freeze && isObject$4(it) ? $freeze(onFreeze(it)) : it;
17280 // Returns true if a and b have the same elements at the same indices.
17281 function utilArrayIdentical(a, b) {
17282 // an array is always identical to itself
17283 if (a === b) return true;
17285 if (i !== b.length) return false;
17288 if (a[i] !== b[i]) return false;
17292 } // http://2ality.com/2015/01/es6-set-operations.html
17293 // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
17294 // This operation is also sometimes called minus (-).
17295 // var a = [1,2,3];
17296 // var b = [4,3,2];
17297 // utilArrayDifference(a, b)
17299 // utilArrayDifference(b, a)
17302 function utilArrayDifference(a, b) {
17303 var other = new Set(b);
17304 return Array.from(new Set(a)).filter(function (v) {
17305 return !other.has(v);
17307 } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
17308 // var a = [1,2,3];
17309 // var b = [4,3,2];
17310 // utilArrayIntersection(a, b)
17313 function utilArrayIntersection(a, b) {
17314 var other = new Set(b);
17315 return Array.from(new Set(a)).filter(function (v) {
17316 return other.has(v);
17318 } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17319 // var a = [1,2,3];
17320 // var b = [4,3,2];
17321 // utilArrayUnion(a, b)
17324 function utilArrayUnion(a, b) {
17325 var result = new Set(a);
17326 b.forEach(function (v) {
17329 return Array.from(result);
17330 } // Returns an Array with all the duplicates removed
17331 // var a = [1,1,2,3,3];
17332 // utilArrayUniq(a)
17335 function utilArrayUniq(a) {
17336 return Array.from(new Set(a));
17337 } // Splits array into chunks of given chunk size
17338 // var a = [1,2,3,4,5,6,7];
17339 // utilArrayChunk(a, 3);
17340 // [[1,2,3],[4,5,6],[7]];
17342 function utilArrayChunk(a, chunkSize) {
17343 if (!chunkSize || chunkSize < 0) return [a.slice()];
17344 var result = new Array(Math.ceil(a.length / chunkSize));
17345 return Array.from(result, function (item, i) {
17346 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17348 } // Flattens two level array into a single level
17349 // var a = [[1,2,3],[4,5,6],[7]];
17350 // utilArrayFlatten(a);
17351 // [1,2,3,4,5,6,7];
17353 function utilArrayFlatten(a) {
17354 return a.reduce(function (acc, val) {
17355 return acc.concat(val);
17357 } // Groups the items of the Array according to the given key
17358 // `key` can be passed as a property or as a key function
17361 // { type: 'Dog', name: 'Spot' },
17362 // { type: 'Cat', name: 'Tiger' },
17363 // { type: 'Dog', name: 'Rover' },
17364 // { type: 'Cat', name: 'Leo' }
17367 // utilArrayGroupBy(pets, 'type')
17369 // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17370 // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17373 // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17375 // 3: [{type: 'Cat', name: 'Leo'}],
17376 // 4: [{type: 'Dog', name: 'Spot'}],
17377 // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17380 function utilArrayGroupBy(a, key) {
17381 return a.reduce(function (acc, item) {
17382 var group = typeof key === 'function' ? key(item) : item[key];
17383 (acc[group] = acc[group] || []).push(item);
17386 } // Returns an Array with all the duplicates removed
17387 // where uniqueness determined by the given key
17388 // `key` can be passed as a property or as a key function
17391 // { type: 'Dog', name: 'Spot' },
17392 // { type: 'Cat', name: 'Tiger' },
17393 // { type: 'Dog', name: 'Rover' },
17394 // { type: 'Cat', name: 'Leo' }
17397 // utilArrayUniqBy(pets, 'type')
17399 // { type: 'Dog', name: 'Spot' },
17400 // { type: 'Cat', name: 'Tiger' }
17403 // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17405 // { type: 'Dog', name: 'Spot' },
17406 // { type: 'Cat', name: 'Tiger' },
17407 // { type: 'Cat', name: 'Leo' }
17410 function utilArrayUniqBy(a, key) {
17411 var seen = new Set();
17412 return a.reduce(function (acc, item) {
17413 var val = typeof key === 'function' ? key(item) : item[key];
17415 if (val && !seen.has(val)) {
17425 fixRegexpWellKnownSymbolLogic('match', function (MATCH, nativeMatch, maybeCallNative) {
17427 // `String.prototype.match` method
17428 // https://tc39.es/ecma262/#sec-string.prototype.match
17429 function match(regexp) {
17430 var O = requireObjectCoercible(this);
17431 var matcher = regexp == undefined ? undefined : regexp[MATCH];
17432 return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17434 // `RegExp.prototype[@@match]` method
17435 // https://tc39.es/ecma262/#sec-regexp.prototype-@@match
17436 function (string) {
17437 var res = maybeCallNative(nativeMatch, this, string);
17438 if (res.done) return res.value;
17440 var rx = anObject(this);
17441 var S = String(string);
17443 if (!rx.global) return regexpExecAbstract(rx, S);
17445 var fullUnicode = rx.unicode;
17450 while ((result = regexpExecAbstract(rx, S)) !== null) {
17451 var matchStr = String(result[0]);
17453 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17456 return n === 0 ? null : A;
17461 var remove$6 = removeDiacritics;
17462 var replacementList = [{
17470 chars: "\u24B6\uFF21\xC0\xC1\xC2\u1EA6\u1EA4\u1EAA\u1EA8\xC3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\xC4\u01DE\u1EA2\xC5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F"
17476 chars: "\xC6\u01FC\u01E2"
17485 chars: "\uA738\uA73A"
17491 chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17494 chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17497 chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17503 chars: "\u01F1\u01C4"
17506 chars: "\u01F2\u01C5"
17509 chars: "\u025B\u24BA\uFF25\xC8\xC9\xCA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\xCB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07"
17512 chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17515 chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17518 chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17521 chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17524 chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17527 chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17530 chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17539 chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17542 chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17551 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"
17566 chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17569 chars: "\u24C6\uFF31\uA756\uA758\u024A"
17572 chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17575 chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17578 chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17587 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"
17590 chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17596 chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17599 chars: "\u24CD\uFF38\u1E8A\u1E8C"
17602 chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17605 chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17608 chars: "\u24D0\uFF41\u1E9A\xE0\xE1\xE2\u1EA7\u1EA5\u1EAB\u1EA9\xE3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\xE4\u01DF\u1EA3\xE5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251"
17614 chars: "\xE6\u01FD\u01E3"
17623 chars: "\uA739\uA73B"
17629 chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17632 chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17635 chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17641 chars: "\u01F3\u01C6"
17644 chars: "\u24D4\uFF45\xE8\xE9\xEA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\xEB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD"
17647 chars: "\u24D5\uFF46\u1E1F\u0192"
17665 chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17668 chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17674 chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17677 chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17680 chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17683 chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17689 chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17692 chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17698 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"
17713 chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17716 chars: "\u24E0\uFF51\u024B\uA757\uA759"
17719 chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17722 chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17728 chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17737 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"
17740 chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17746 chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17749 chars: "\u24E7\uFF58\u1E8B\u1E8D"
17752 chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17755 chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17757 var diacriticsMap = {};
17759 for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) {
17760 var chars = replacementList[i$1].chars;
17762 for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17763 diacriticsMap[chars[j$1]] = replacementList[i$1].base;
17767 function removeDiacritics(str) {
17768 return str.replace(/[^\u0000-\u007e]/g, function (c) {
17769 return diacriticsMap[c] || c;
17773 var replacementList_1 = replacementList;
17774 var diacriticsMap_1 = diacriticsMap;
17777 replacementList: replacementList_1,
17778 diacriticsMap: diacriticsMap_1
17781 var 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
17784 function isArabic(_char) {
17785 if (_char.length > 1) {
17786 // allow the newer chars?
17787 throw new Error('isArabic works on only one-character strings');
17790 var code = _char.charCodeAt(0);
17792 for (var i = 0; i < arabicBlocks.length; i++) {
17793 var block = arabicBlocks[i];
17795 if (code >= block[0] && code <= block[1]) {
17803 var isArabic_2 = isArabic;
17805 function isMath(_char2) {
17806 if (_char2.length > 2) {
17807 // allow the newer chars?
17808 throw new Error('isMath works on only one-character strings');
17811 var code = _char2.charCodeAt(0);
17813 return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17816 var isMath_1 = isMath;
17817 var isArabic_1 = /*#__PURE__*/Object.defineProperty({
17818 isArabic: isArabic_2,
17824 var arabicReference = {
17826 "normal": ["\u0627"],
17828 "normal": ["\u0627\u0653", "\u0622"],
17829 "isolated": "\uFE81",
17833 "normal": ["\u0627\u0654", "\u0623"],
17834 "isolated": "\uFE83",
17838 "normal": ["\u0627\u0655", "\u0625"],
17839 "isolated": "\uFE87",
17843 "normal": "\u0671",
17844 "isolated": "\uFB50",
17847 "wavy_hamza_above": ["\u0672"],
17848 "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17849 "high_hamza": ["\u0675", "\u0627\u0674"],
17850 "indic_two_above": ["\u0773"],
17851 "indic_three_above": ["\u0774"],
17853 "normal": ["\u0627\u064B"],
17855 "isolated": "\uFD3D"
17857 "isolated": "\uFE8D",
17861 "normal": ["\u0628"],
17862 "dotless": ["\u066E"],
17863 "three_dots_horizontally_below": ["\u0750"],
17864 "dot_below_three_dots_above": ["\u0751"],
17865 "three_dots_pointing_upwards_below": ["\u0752"],
17866 "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17867 "two_dots_below_dot_above": ["\u0754"],
17868 "inverted_small_v_below": ["\u0755"],
17869 "small_v": ["\u0756"],
17870 "small_v_below": ["\u08A0"],
17871 "hamza_above": ["\u08A1"],
17872 "small_meem_above": ["\u08B6"],
17873 "isolated": "\uFE8F",
17875 "initial": "\uFE91",
17879 "normal": ["\u0629"],
17880 "isolated": "\uFE93",
17884 "normal": ["\u062A"],
17885 "ring": ["\u067C"],
17886 "three_dots_above_downwards": ["\u067D"],
17887 "small_teh_above": ["\u08B8"],
17888 "isolated": "\uFE95",
17890 "initial": "\uFE97",
17894 "normal": ["\u062B"],
17895 "isolated": "\uFE99",
17897 "initial": "\uFE9B",
17901 "normal": ["\u062C"],
17902 "two_dots_above": ["\u08A2"],
17903 "isolated": "\uFE9D",
17905 "initial": "\uFE9F",
17909 "normal": ["\u062D"],
17910 "hamza_above": ["\u0681"],
17911 "two_dots_vertical_above": ["\u0682"],
17912 "three_dots_above": ["\u0685"],
17913 "two_dots_above": ["\u0757"],
17914 "three_dots_pointing_upwards_below": ["\u0758"],
17915 "small_tah_below": ["\u076E"],
17916 "small_tah_two_dots": ["\u076F"],
17917 "small_tah_above": ["\u0772"],
17918 "indic_four_below": ["\u077C"],
17919 "isolated": "\uFEA1",
17921 "initial": "\uFEA3",
17925 "normal": ["\u062E"],
17926 "isolated": "\uFEA5",
17928 "initial": "\uFEA7",
17932 "normal": ["\u062F"],
17933 "ring": ["\u0689"],
17934 "dot_below": ["\u068A"],
17935 "dot_below_small_tah": ["\u068B"],
17936 "three_dots_above_downwards": ["\u068F"],
17937 "four_dots_above": ["\u0690"],
17938 "inverted_v": ["\u06EE"],
17939 "two_dots_vertically_below_small_tah": ["\u0759"],
17940 "inverted_small_v_below": ["\u075A"],
17941 "three_dots_below": ["\u08AE"],
17942 "isolated": "\uFEA9",
17946 "normal": ["\u0630"],
17947 "isolated": "\uFEAB",
17951 "normal": ["\u0631"],
17952 "small_v": ["\u0692"],
17953 "ring": ["\u0693"],
17954 "dot_below": ["\u0694"],
17955 "small_v_below": ["\u0695"],
17956 "dot_below_dot_above": ["\u0696"],
17957 "two_dots_above": ["\u0697"],
17958 "four_dots_above": ["\u0699"],
17959 "inverted_v": ["\u06EF"],
17960 "stroke": ["\u075B"],
17961 "two_dots_vertically_above": ["\u076B"],
17962 "hamza_above": ["\u076C"],
17963 "small_tah_two_dots": ["\u0771"],
17964 "loop": ["\u08AA"],
17965 "small_noon_above": ["\u08B9"],
17966 "isolated": "\uFEAD",
17970 "normal": ["\u0632"],
17971 "inverted_v_above": ["\u08B2"],
17972 "isolated": "\uFEAF",
17976 "normal": ["\u0633"],
17977 "dot_below_dot_above": ["\u069A"],
17978 "three_dots_below": ["\u069B"],
17979 "three_dots_below_three_dots_above": ["\u069C"],
17980 "four_dots_above": ["\u075C"],
17981 "two_dots_vertically_above": ["\u076D"],
17982 "small_tah_two_dots": ["\u0770"],
17983 "indic_four_above": ["\u077D"],
17984 "inverted_v": ["\u077E"],
17985 "isolated": "\uFEB1",
17987 "initial": "\uFEB3",
17991 "normal": ["\u0634"],
17992 "dot_below": ["\u06FA"],
17993 "isolated": "\uFEB5",
17995 "initial": "\uFEB7",
17999 "normal": ["\u0635"],
18000 "two_dots_below": ["\u069D"],
18001 "three_dots_above": ["\u069E"],
18002 "three_dots_below": ["\u08AF"],
18003 "isolated": "\uFEB9",
18005 "initial": "\uFEBB",
18009 "normal": ["\u0636"],
18010 "dot_below": ["\u06FB"],
18011 "isolated": "\uFEBD",
18013 "initial": "\uFEBF",
18017 "normal": ["\u0637"],
18018 "three_dots_above": ["\u069F"],
18019 "two_dots_above": ["\u08A3"],
18020 "isolated": "\uFEC1",
18022 "initial": "\uFEC3",
18026 "normal": ["\u0638"],
18027 "isolated": "\uFEC5",
18029 "initial": "\uFEC7",
18033 "normal": ["\u0639"],
18034 "three_dots_above": ["\u06A0"],
18035 "two_dots_above": ["\u075D"],
18036 "three_dots_pointing_downwards_above": ["\u075E"],
18037 "two_dots_vertically_above": ["\u075F"],
18038 "three_dots_below": ["\u08B3"],
18039 "isolated": "\uFEC9",
18041 "initial": "\uFECB",
18045 "normal": ["\u063A"],
18046 "dot_below": ["\u06FC"],
18047 "isolated": "\uFECD",
18049 "initial": "\uFECF",
18053 "normal": ["\u0641"],
18054 "dotless": ["\u06A1"],
18055 "dot_moved_below": ["\u06A2"],
18056 "dot_below": ["\u06A3"],
18057 "three_dots_below": ["\u06A5"],
18058 "two_dots_below": ["\u0760"],
18059 "three_dots_pointing_upwards_below": ["\u0761"],
18060 "dot_below_three_dots_above": ["\u08A4"],
18061 "isolated": "\uFED1",
18063 "initial": "\uFED3",
18067 "normal": ["\u0642"],
18068 "dotless": ["\u066F"],
18069 "dot_above": ["\u06A7"],
18070 "three_dots_above": ["\u06A8"],
18071 "dot_below": ["\u08A5"],
18072 "isolated": "\uFED5",
18074 "initial": "\uFED7",
18078 "normal": ["\u0643"],
18079 "swash": ["\u06AA"],
18080 "ring": ["\u06AB"],
18081 "dot_above": ["\u06AC"],
18082 "three_dots_below": ["\u06AE"],
18083 "two_dots_above": ["\u077F"],
18084 "dot_below": ["\u08B4"],
18085 "isolated": "\uFED9",
18087 "initial": "\uFEDB",
18091 "normal": ["\u0644"],
18092 "small_v": ["\u06B5"],
18093 "dot_above": ["\u06B6"],
18094 "three_dots_above": ["\u06B7"],
18095 "three_dots_below": ["\u06B8"],
18097 "double_bar": ["\u08A6"],
18098 "isolated": "\uFEDD",
18100 "initial": "\uFEDF",
18104 "normal": ["\u0645"],
18105 "dot_above": ["\u0765"],
18106 "dot_below": ["\u0766"],
18107 "three_dots_above": ["\u08A7"],
18108 "isolated": "\uFEE1",
18110 "initial": "\uFEE3",
18114 "normal": ["\u0646"],
18115 "dot_below": ["\u06B9"],
18116 "ring": ["\u06BC"],
18117 "three_dots_above": ["\u06BD"],
18118 "two_dots_below": ["\u0767"],
18119 "small_tah": ["\u0768"],
18120 "small_v": ["\u0769"],
18121 "isolated": "\uFEE5",
18123 "initial": "\uFEE7",
18127 "normal": ["\u0647"],
18128 "isolated": "\uFEE9",
18130 "initial": "\uFEEB",
18134 "normal": ["\u0648"],
18136 "normal": ["\u0624", "\u0648\u0654"],
18137 "isolated": "\uFE85",
18140 "high_hamza": ["\u0676", "\u0648\u0674"],
18141 "ring": ["\u06C4"],
18142 "two_dots_above": ["\u06CA"],
18143 "dot_above": ["\u06CF"],
18144 "indic_two_above": ["\u0778"],
18145 "indic_three_above": ["\u0779"],
18146 "dot_within": ["\u08AB"],
18147 "isolated": "\uFEED",
18151 "normal": ["\u0649"],
18152 "hamza_above": ["\u0626", "\u064A\u0654"],
18153 "initial": "\uFBE8",
18154 "medial": "\uFBE9",
18155 "isolated": "\uFEEF",
18159 "normal": ["\u064A"],
18161 "normal": ["\u0626", "\u0649\u0654"],
18162 "isolated": "\uFE89",
18164 "initial": "\uFE8B",
18167 "two_dots_below_hamza_above": ["\u08A8"],
18168 "high_hamza": ["\u0678", "\u064A\u0674"],
18169 "tail": ["\u06CD"],
18170 "small_v": ["\u06CE"],
18171 "three_dots_below": ["\u06D1"],
18172 "two_dots_below_dot_above": ["\u08A9"],
18173 "two_dots_below_small_noon_above": ["\u08BA"],
18174 "isolated": "\uFEF1",
18176 "initial": "\uFEF3",
18180 "normal": ["\u0679"],
18181 "isolated": "\uFB66",
18183 "initial": "\uFB68",
18187 "normal": ["\u067A"],
18188 "isolated": "\uFB5E",
18190 "initial": "\uFB60",
18194 "normal": ["\u067B"],
18195 "isolated": "\uFB52",
18197 "initial": "\uFB54",
18201 "normal": ["\u067E"],
18202 "small_meem_above": ["\u08B7"],
18203 "isolated": "\uFB56",
18205 "initial": "\uFB58",
18209 "normal": ["\u067F"],
18210 "isolated": "\uFB62",
18212 "initial": "\uFB64",
18216 "normal": ["\u0680"],
18217 "isolated": "\uFB5A",
18219 "initial": "\uFB5C",
18223 "normal": ["\u0683"],
18224 "isolated": "\uFB76",
18226 "initial": "\uFB78",
18230 "normal": ["\u0684"],
18231 "isolated": "\uFB72",
18233 "initial": "\uFB74",
18237 "normal": ["\u0686"],
18238 "dot_above": ["\u06BF"],
18239 "isolated": "\uFB7A",
18241 "initial": "\uFB7C",
18245 "normal": ["\u0687"],
18246 "isolated": "\uFB7E",
18248 "initial": "\uFB80",
18252 "normal": ["\u0688"],
18253 "isolated": "\uFB88",
18257 "normal": ["\u068C"],
18258 "isolated": "\uFB84",
18262 "normal": ["\u068D"],
18263 "isolated": "\uFB82",
18267 "normal": ["\u068F", "\u068E"],
18268 "isolated": "\uFB86",
18272 "normal": ["\u0691"],
18273 "isolated": "\uFB8C",
18277 "normal": ["\u0698"],
18278 "isolated": "\uFB8A",
18282 "normal": ["\u06A4"],
18283 "isolated": "\uFB6A",
18285 "initial": "\uFB6C",
18289 "normal": ["\u06A6"],
18290 "isolated": "\uFB6E",
18292 "initial": "\uFB70",
18296 "normal": ["\u06A9"],
18297 "dot_above": ["\u0762"],
18298 "three_dots_above": ["\u0763"],
18299 "three_dots_pointing_upwards_below": ["\u0764"],
18300 "isolated": "\uFB8E",
18302 "initial": "\uFB90",
18306 "normal": ["\u06AD"],
18307 "isolated": "\uFBD3",
18309 "initial": "\uFBD5",
18313 "normal": ["\u06AF"],
18314 "ring": ["\u06B0"],
18315 "two_dots_below": ["\u06B2"],
18316 "three_dots_above": ["\u06B4"],
18317 "inverted_stroke": ["\u08B0"],
18318 "isolated": "\uFB92",
18320 "initial": "\uFB94",
18324 "normal": ["\u06B1"],
18325 "isolated": "\uFB9A",
18327 "initial": "\uFB9C",
18331 "normal": ["\u06B3"],
18332 "isolated": "\uFB96",
18334 "initial": "\uFB98",
18338 "normal": ["\u06BA"],
18339 "isolated": "\uFB9E",
18343 "normal": ["\u06BB"],
18344 "isolated": "\uFBA0",
18346 "initial": "\uFBA2",
18349 "heh doachashmee": {
18350 "normal": ["\u06BE"],
18351 "isolated": "\uFBAA",
18353 "initial": "\uFBAC",
18357 "normal": ["\u06C1"],
18358 "hamza_above": ["\u06C1\u0654", "\u06C2"],
18359 "isolated": "\uFBA6",
18361 "initial": "\uFBA8",
18364 "teh marbuta goal": {
18365 "normal": ["\u06C3"]
18368 "normal": ["\u06C5"],
18369 "isolated": "\uFBE0",
18373 "normal": ["\u06C6"],
18374 "isolated": "\uFBD9",
18378 "normal": ["\u06C7"],
18380 "normal": ["\u0677", "\u06C7\u0674"],
18381 "isolated": "\uFBDD"
18383 "isolated": "\uFBD7",
18387 "normal": ["\u06C8"],
18388 "isolated": "\uFBDB",
18392 "normal": ["\u06C9"],
18393 "isolated": "\uFBE2",
18397 "normal": ["\u06CB"],
18398 "isolated": "\uFBDE",
18402 "normal": ["\u06CC"],
18403 "indic_two_above": ["\u0775"],
18404 "indic_three_above": ["\u0776"],
18405 "indic_four_above": ["\u0777"],
18406 "isolated": "\uFBFC",
18408 "initial": "\uFBFE",
18412 "normal": ["\u06D0"],
18413 "isolated": "\uFBE4",
18415 "initial": "\uFBE6",
18419 "normal": ["\u06D2"],
18421 "normal": ["\u06D2\u0654", "\u06D3"],
18422 "isolated": "\uFBB0",
18425 "indic_two_above": ["\u077A"],
18426 "indic_three_above": ["\u077B"],
18427 "isolated": "\uFBAE",
18431 "normal": ["\u06D5"],
18432 "isolated": "\u06D5",
18435 "normal": ["\u06C0", "\u06D5\u0654"],
18436 "isolated": "\uFBA4",
18441 "normal": ["\u08AC"]
18444 "normal": ["\u08AD"]
18447 "normal": ["\u08B1"]
18450 "normal": ["\u08BB"]
18453 "normal": ["\u08BC"]
18456 "normal": ["\u08BD"]
18459 var _default$3 = arabicReference;
18460 var unicodeArabic = /*#__PURE__*/Object.defineProperty({
18461 "default": _default$3
18466 var ligatureReference = {
18468 "isolated": "\uFBEA",
18472 "isolated": "\uFBEC",
18476 "isolated": "\uFBEE",
18480 "isolated": "\uFBF0",
18484 "isolated": "\uFBF2",
18488 "isolated": "\uFBF4",
18492 "isolated": "\uFBF6",
18494 "initial": "\uFBF8"
18497 "uighur_kirghiz": {
18498 "isolated": "\uFBF9",
18500 "initial": "\uFBFB"
18502 "isolated": "\uFC03",
18506 "isolated": "\uFC00",
18507 "initial": "\uFC97"
18510 "isolated": "\uFC01",
18511 "initial": "\uFC98"
18514 "isolated": "\uFC02",
18516 "initial": "\uFC9A",
18520 "isolated": "\uFC04",
18524 "isolated": "\uFC05",
18525 "initial": "\uFC9C"
18528 "isolated": "\uFC06",
18529 "initial": "\uFC9D"
18532 "isolated": "\uFC07",
18533 "initial": "\uFC9E"
18536 "isolated": "\uFC08",
18538 "initial": "\uFC9F",
18542 "isolated": "\uFC09",
18546 "isolated": "\uFC0A",
18550 "isolated": "\uFC0B",
18551 "initial": "\uFCA1"
18554 "isolated": "\uFC0C",
18555 "initial": "\uFCA2"
18558 "isolated": "\uFC0D",
18559 "initial": "\uFCA3"
18562 "isolated": "\uFC0E",
18564 "initial": "\uFCA4",
18568 "isolated": "\uFC0F",
18572 "isolated": "\uFC10",
18576 "isolated": "\uFC11"
18579 "isolated": "\uFC12",
18581 "initial": "\uFCA6",
18585 "isolated": "\uFC13",
18589 "isolated": "\uFC14"
18592 "isolated": "\uFC15",
18593 "initial": "\uFCA7"
18596 "isolated": "\uFC16",
18597 "initial": "\uFCA8"
18600 "isolated": "\uFC17",
18601 "initial": "\uFCA9"
18604 "isolated": "\uFC18",
18605 "initial": "\uFCAA"
18608 "isolated": "\uFC19",
18609 "initial": "\uFCAB"
18612 "isolated": "\uFC1A"
18615 "isolated": "\uFC1B",
18616 "initial": "\uFCAC"
18619 "isolated": "\uFC1C",
18620 "initial": "\uFCAD",
18624 "isolated": "\uFC1D",
18625 "initial": "\uFCAE",
18629 "isolated": "\uFC1E",
18630 "initial": "\uFCAF",
18634 "isolated": "\uFC1F",
18635 "initial": "\uFCB0",
18639 "isolated": "\uFC20",
18640 "initial": "\uFCB1"
18643 "isolated": "\uFC21",
18644 "initial": "\uFCB3"
18647 "isolated": "\uFC22",
18648 "initial": "\uFCB4"
18651 "isolated": "\uFC23",
18652 "initial": "\uFCB5"
18655 "isolated": "\uFC24",
18656 "initial": "\uFCB6"
18659 "isolated": "\uFC25",
18660 "initial": "\uFCB7"
18663 "isolated": "\uFC26",
18664 "initial": "\uFCB8"
18667 "isolated": "\uFC27",
18668 "initial": "\uFD33",
18672 "isolated": "\uFC28",
18673 "initial": "\uFCB9",
18677 "isolated": "\uFC29",
18678 "initial": "\uFCBA"
18681 "isolated": "\uFC2A",
18682 "initial": "\uFCBB"
18685 "isolated": "\uFC2B",
18686 "initial": "\uFCBC"
18689 "isolated": "\uFC2C",
18690 "initial": "\uFCBD"
18693 "isolated": "\uFC2D",
18694 "initial": "\uFCBE"
18697 "isolated": "\uFC2E",
18698 "initial": "\uFCBF"
18701 "isolated": "\uFC2F",
18702 "initial": "\uFCC0"
18705 "isolated": "\uFC30",
18706 "initial": "\uFCC1"
18709 "isolated": "\uFC31",
18713 "isolated": "\uFC32",
18717 "isolated": "\uFC33",
18718 "initial": "\uFCC2"
18721 "isolated": "\uFC34",
18722 "initial": "\uFCC3"
18725 "isolated": "\uFC35",
18729 "isolated": "\uFC36",
18733 "isolated": "\uFC37",
18737 "isolated": "\uFC38",
18738 "initial": "\uFCC4"
18741 "isolated": "\uFC39",
18742 "initial": "\uFCC5"
18745 "isolated": "\uFC3A",
18746 "initial": "\uFCC6"
18749 "isolated": "\uFC3B",
18751 "initial": "\uFCC7",
18755 "isolated": "\uFC3C",
18757 "initial": "\uFCC8",
18761 "isolated": "\uFC3D",
18765 "isolated": "\uFC3E",
18769 "isolated": "\uFC3F",
18770 "initial": "\uFCC9"
18773 "isolated": "\uFC40",
18774 "initial": "\uFCCA"
18777 "isolated": "\uFC41",
18778 "initial": "\uFCCB"
18781 "isolated": "\uFC42",
18783 "initial": "\uFCCC",
18787 "isolated": "\uFC43",
18791 "isolated": "\uFC44",
18795 "isolated": "\uFC45",
18796 "initial": "\uFCCE"
18799 "isolated": "\uFC46",
18800 "initial": "\uFCCF"
18803 "isolated": "\uFC47",
18804 "initial": "\uFCD0"
18807 "isolated": "\uFC48",
18809 "initial": "\uFCD1"
18812 "isolated": "\uFC49"
18815 "isolated": "\uFC4A"
18818 "isolated": "\uFC4B",
18819 "initial": "\uFCD2"
18822 "isolated": "\uFC4C",
18823 "initial": "\uFCD3"
18826 "isolated": "\uFC4D",
18827 "initial": "\uFCD4"
18830 "isolated": "\uFC4E",
18832 "initial": "\uFCD5",
18836 "isolated": "\uFC4F",
18840 "isolated": "\uFC50",
18844 "isolated": "\uFC51",
18845 "initial": "\uFCD7"
18848 "isolated": "\uFC52",
18849 "initial": "\uFCD8"
18852 "isolated": "\uFC53"
18855 "isolated": "\uFC54"
18858 "isolated": "\uFC55",
18859 "initial": "\uFCDA"
18862 "isolated": "\uFC56",
18863 "initial": "\uFCDB"
18866 "isolated": "\uFC57",
18867 "initial": "\uFCDC"
18870 "isolated": "\uFC58",
18872 "initial": "\uFCDD",
18876 "isolated": "\uFC59",
18880 "isolated": "\uFC5A",
18884 "isolated": "\uFC5B"
18887 "isolated": "\uFC5C"
18890 "isolated": "\uFC5D",
18894 "isolated": "\uFC5E"
18897 "isolated": "\uFC5F"
18900 "isolated": "\uFC60"
18903 "isolated": "\uFC61"
18906 "isolated": "\uFC62"
18909 "isolated": "\uFC63"
18972 "initial": "\uFC99"
18975 "initial": "\uFC9B",
18979 "initial": "\uFCA0",
18983 "initial": "\uFCA5",
18987 "initial": "\uFCB2"
18990 "initial": "\uFCCD"
18993 "initial": "\uFCD6",
18997 "initial": "\uFCD9"
19000 "initial": "\uFCDE",
19007 "medial": "\uFCE8",
19008 "initial": "\uFD31"
19011 "medial": "\uFCE9",
19012 "isolated": "\uFD0C",
19014 "initial": "\uFD30"
19017 "medial": "\uFCEA",
19018 "initial": "\uFD32"
19020 "\u0640\u064E\u0651": {
19023 "\u0640\u064F\u0651": {
19026 "\u0640\u0650\u0651": {
19030 "isolated": "\uFCF5",
19034 "isolated": "\uFCF6",
19038 "isolated": "\uFCF7",
19042 "isolated": "\uFCF8",
19046 "isolated": "\uFCF9",
19050 "isolated": "\uFCFA",
19054 "isolated": "\uFCFB"
19057 "isolated": "\uFCFC",
19061 "isolated": "\uFCFD",
19065 "isolated": "\uFCFE",
19069 "isolated": "\uFCFF",
19073 "isolated": "\uFD00",
19077 "isolated": "\uFD01",
19081 "isolated": "\uFD02",
19085 "isolated": "\uFD03",
19089 "isolated": "\uFD04",
19093 "isolated": "\uFD05",
19097 "isolated": "\uFD06",
19101 "isolated": "\uFD07",
19105 "isolated": "\uFD08",
19109 "isolated": "\uFD09",
19111 "initial": "\uFD2D",
19115 "isolated": "\uFD0A",
19117 "initial": "\uFD2E",
19121 "isolated": "\uFD0B",
19123 "initial": "\uFD2F",
19127 "isolated": "\uFD0D",
19131 "isolated": "\uFD0E",
19135 "isolated": "\uFD0F",
19139 "isolated": "\uFD10",
19145 "\u062A\u062C\u0645": {
19146 "initial": "\uFD50"
19148 "\u062A\u062D\u062C": {
19150 "initial": "\uFD52"
19152 "\u062A\u062D\u0645": {
19153 "initial": "\uFD53"
19155 "\u062A\u062E\u0645": {
19156 "initial": "\uFD54"
19158 "\u062A\u0645\u062C": {
19159 "initial": "\uFD55"
19161 "\u062A\u0645\u062D": {
19162 "initial": "\uFD56"
19164 "\u062A\u0645\u062E": {
19165 "initial": "\uFD57"
19167 "\u062C\u0645\u062D": {
19169 "initial": "\uFD59"
19171 "\u062D\u0645\u064A": {
19174 "\u062D\u0645\u0649": {
19177 "\u0633\u062D\u062C": {
19178 "initial": "\uFD5C"
19180 "\u0633\u062C\u062D": {
19181 "initial": "\uFD5D"
19183 "\u0633\u062C\u0649": {
19186 "\u0633\u0645\u062D": {
19188 "initial": "\uFD60"
19190 "\u0633\u0645\u062C": {
19191 "initial": "\uFD61"
19193 "\u0633\u0645\u0645": {
19195 "initial": "\uFD63"
19197 "\u0635\u062D\u062D": {
19199 "initial": "\uFD65"
19201 "\u0635\u0645\u0645": {
19203 "initial": "\uFDC5"
19205 "\u0634\u062D\u0645": {
19207 "initial": "\uFD68"
19209 "\u0634\u062C\u064A": {
19212 "\u0634\u0645\u062E": {
19214 "initial": "\uFD6B"
19216 "\u0634\u0645\u0645": {
19218 "initial": "\uFD6D"
19220 "\u0636\u062D\u0649": {
19223 "\u0636\u062E\u0645": {
19225 "initial": "\uFD70"
19227 "\u0636\u0645\u062D": {
19230 "\u0637\u0645\u062D": {
19231 "initial": "\uFD72"
19233 "\u0637\u0645\u0645": {
19234 "initial": "\uFD73"
19236 "\u0637\u0645\u064A": {
19239 "\u0639\u062C\u0645": {
19241 "initial": "\uFDC4"
19243 "\u0639\u0645\u0645": {
19245 "initial": "\uFD77"
19247 "\u0639\u0645\u0649": {
19250 "\u063A\u0645\u0645": {
19253 "\u063A\u0645\u064A": {
19256 "\u063A\u0645\u0649": {
19259 "\u0641\u062E\u0645": {
19261 "initial": "\uFD7D"
19263 "\u0642\u0645\u062D": {
19265 "initial": "\uFDB4"
19267 "\u0642\u0645\u0645": {
19270 "\u0644\u062D\u0645": {
19272 "initial": "\uFDB5"
19274 "\u0644\u062D\u064A": {
19277 "\u0644\u062D\u0649": {
19280 "\u0644\u062C\u062C": {
19281 "initial": "\uFD83",
19284 "\u0644\u062E\u0645": {
19286 "initial": "\uFD86"
19288 "\u0644\u0645\u062D": {
19290 "initial": "\uFD88"
19292 "\u0645\u062D\u062C": {
19293 "initial": "\uFD89"
19295 "\u0645\u062D\u0645": {
19296 "initial": "\uFD8A"
19298 "\u0645\u062D\u064A": {
19301 "\u0645\u062C\u062D": {
19302 "initial": "\uFD8C"
19304 "\u0645\u062C\u0645": {
19305 "initial": "\uFD8D"
19307 "\u0645\u062E\u062C": {
19308 "initial": "\uFD8E"
19310 "\u0645\u062E\u0645": {
19311 "initial": "\uFD8F"
19313 "\u0645\u062C\u062E": {
19314 "initial": "\uFD92"
19316 "\u0647\u0645\u062C": {
19317 "initial": "\uFD93"
19319 "\u0647\u0645\u0645": {
19320 "initial": "\uFD94"
19322 "\u0646\u062D\u0645": {
19323 "initial": "\uFD95"
19325 "\u0646\u062D\u0649": {
19328 "\u0646\u062C\u0645": {
19330 "initial": "\uFD98"
19332 "\u0646\u062C\u0649": {
19335 "\u0646\u0645\u064A": {
19338 "\u0646\u0645\u0649": {
19341 "\u064A\u0645\u0645": {
19343 "initial": "\uFD9D"
19345 "\u0628\u062E\u064A": {
19348 "\u062A\u062C\u064A": {
19351 "\u062A\u062C\u0649": {
19354 "\u062A\u062E\u064A": {
19357 "\u062A\u062E\u0649": {
19360 "\u062A\u0645\u064A": {
19363 "\u062A\u0645\u0649": {
19366 "\u062C\u0645\u064A": {
19369 "\u062C\u062D\u0649": {
19372 "\u062C\u0645\u0649": {
19375 "\u0633\u062E\u0649": {
19378 "\u0635\u062D\u064A": {
19381 "\u0634\u062D\u064A": {
19384 "\u0636\u062D\u064A": {
19387 "\u0644\u062C\u064A": {
19390 "\u0644\u0645\u064A": {
19393 "\u064A\u062D\u064A": {
19396 "\u064A\u062C\u064A": {
19399 "\u064A\u0645\u064A": {
19402 "\u0645\u0645\u064A": {
19405 "\u0642\u0645\u064A": {
19408 "\u0646\u062D\u064A": {
19411 "\u0639\u0645\u064A": {
19414 "\u0643\u0645\u064A": {
19417 "\u0646\u062C\u062D": {
19418 "initial": "\uFDB8",
19421 "\u0645\u062E\u064A": {
19424 "\u0644\u062C\u0645": {
19425 "initial": "\uFDBA",
19428 "\u0643\u0645\u0645": {
19430 "initial": "\uFDC3"
19432 "\u062C\u062D\u064A": {
19435 "\u062D\u062C\u064A": {
19438 "\u0645\u062C\u064A": {
19441 "\u0641\u0645\u064A": {
19444 "\u0628\u062D\u064A": {
19447 "\u0633\u062E\u064A": {
19450 "\u0646\u062C\u064A": {
19454 "isolated": "\uFEF5",
19458 "isolated": "\uFEF7",
19462 "isolated": "\uFEF9",
19466 "isolated": "\uFEFB",
19470 "\u0635\u0644\u06D2": "\uFDF0",
19471 "\u0642\u0644\u06D2": "\uFDF1",
19472 "\u0627\u0644\u0644\u0647": "\uFDF2",
19473 "\u0627\u0643\u0628\u0631": "\uFDF3",
19474 "\u0645\u062D\u0645\u062F": "\uFDF4",
19475 "\u0635\u0644\u0639\u0645": "\uFDF5",
19476 "\u0631\u0633\u0648\u0644": "\uFDF6",
19477 "\u0639\u0644\u064A\u0647": "\uFDF7",
19478 "\u0648\u0633\u0644\u0645": "\uFDF8",
19479 "\u0635\u0644\u0649": "\uFDF9",
19480 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19481 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19482 "\u0631\u06CC\u0627\u0644": "\uFDFC"
19485 var _default$2 = ligatureReference;
19486 var unicodeLigatures = /*#__PURE__*/Object.defineProperty({
19487 "default": _default$2
19492 var reference = createCommonjsModule(function (module, exports) {
19494 Object.defineProperty(exports, "__esModule", {
19497 var letterList = Object.keys(unicodeArabic["default"]);
19498 exports.letterList = letterList;
19499 var ligatureList = Object.keys(unicodeLigatures["default"]);
19500 exports.ligatureList = ligatureList;
19501 var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19502 exports.ligatureWordList = ligatureWordList;
19503 var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19504 exports.lams = lams;
19505 var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19506 exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19507 // console.log('-');
19508 // for (var a = 0; a < alefs.length; a++) {
19509 // console.log(a + ': ' + lams[l] + alefs[a]);
19513 var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19514 exports.tashkeel = tashkeel;
19516 function addToTashkeel(start, finish) {
19517 for (var i = start; i <= finish; i++) {
19518 exports.tashkeel = tashkeel += String.fromCharCode(i);
19522 addToTashkeel(0x0610, 0x061A);
19523 addToTashkeel(0x064B, 0x065F);
19524 addToTashkeel(0x06D6, 0x06DC);
19525 addToTashkeel(0x06E0, 0x06E4);
19526 addToTashkeel(0x06EA, 0x06ED);
19527 addToTashkeel(0x08D3, 0x08E1);
19528 addToTashkeel(0x08E3, 0x08FF);
19529 addToTashkeel(0xFE70, 0xFE7F);
19530 var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19531 exports.lineBreakers = lineBreakers;
19533 function addToLineBreakers(start, finish) {
19534 for (var i = start; i <= finish; i++) {
19535 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19539 addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19541 addToLineBreakers(0x0621, 0x0625);
19542 addToLineBreakers(0x062F, 0x0632);
19543 addToLineBreakers(0x0660, 0x066D); // numerals, math
19545 addToLineBreakers(0x0671, 0x0677);
19546 addToLineBreakers(0x0688, 0x0699);
19547 addToLineBreakers(0x06C3, 0x06CB);
19548 addToLineBreakers(0x06D2, 0x06F9);
19549 addToLineBreakers(0x0759, 0x075B);
19550 addToLineBreakers(0x08AA, 0x08AE);
19551 addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19552 // Presentation Forms A includes diacritics but they are meant to stand alone
19554 addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19557 addToLineBreakers(0x10E60, 0x10E7F);
19558 addToLineBreakers(0x1EC70, 0x1ECBF);
19559 addToLineBreakers(0x1EE00, 0x1EEFF);
19562 function GlyphSplitter(word) {
19564 var lastLetter = '';
19565 word.split('').forEach(function (letter) {
19566 if (isArabic_1.isArabic(letter)) {
19567 if (reference.tashkeel.indexOf(letter) > -1) {
19568 letters[letters.length - 1] += letter;
19569 } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19571 letters[letters.length - 1] += letter;
19573 letters.push(letter);
19576 letters.push(letter);
19579 if (reference.tashkeel.indexOf(letter) === -1) {
19580 lastLetter = letter;
19586 var GlyphSplitter_2 = GlyphSplitter;
19587 var GlyphSplitter_1 = /*#__PURE__*/Object.defineProperty({
19588 GlyphSplitter: GlyphSplitter_2
19593 function BaselineSplitter(word) {
19595 var lastLetter = '';
19596 word.split('').forEach(function (letter) {
19597 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19598 if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19599 letters[letters.length - 1] += letter;
19600 } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19601 letters.push(letter);
19603 letters[letters.length - 1] += letter;
19606 letters.push(letter);
19609 if (reference.tashkeel.indexOf(letter) === -1) {
19610 // don't allow tashkeel to hide line break
19611 lastLetter = letter;
19617 var BaselineSplitter_2 = BaselineSplitter;
19618 var BaselineSplitter_1 = /*#__PURE__*/Object.defineProperty({
19619 BaselineSplitter: BaselineSplitter_2
19624 function Normal(word, breakPresentationForm) {
19625 // default is to turn initial/isolated/medial/final presentation form to generic
19626 if (typeof breakPresentationForm === 'undefined') {
19627 breakPresentationForm = true;
19630 var returnable = '';
19631 word.split('').forEach(function (letter) {
19632 if (!isArabic_1.isArabic(letter)) {
19633 returnable += letter;
19637 for (var w = 0; w < reference.letterList.length; w++) {
19638 // ok so we are checking this potential lettertron
19639 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19640 var versions = Object.keys(letterForms);
19642 for (var v = 0; v < versions.length; v++) {
19643 var localVersion = letterForms[versions[v]];
19645 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19646 // look at this embedded object
19647 var embeddedForms = Object.keys(localVersion);
19649 for (var ef = 0; ef < embeddedForms.length; ef++) {
19650 var form = localVersion[embeddedForms[ef]];
19652 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19654 // console.log('embedded match');
19655 if (form === letter) {
19657 if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19658 // replace presentation form
19659 // console.log('keeping normal form of the letter');
19660 if (_typeof(localVersion['normal']) === 'object') {
19661 returnable += localVersion['normal'][0];
19663 returnable += localVersion['normal'];
19667 } // console.log('keeping this letter');
19670 returnable += letter;
19672 } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19674 returnable += form[0]; // console.log('added the first letter from the same array');
19680 } else if (localVersion === letter) {
19682 if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19683 // replace presentation form
19684 // console.log('keeping normal form of the letter');
19685 if (_typeof(letterForms['normal']) === 'object') {
19686 returnable += letterForms['normal'][0];
19688 returnable += letterForms['normal'];
19692 } // console.log('keeping this letter');
19695 returnable += letter;
19697 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19699 returnable += localVersion[0]; // console.log('added the first letter from the same array');
19707 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19708 var normalForm = reference.ligatureList[v2];
19710 if (normalForm !== 'words') {
19711 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19713 for (var f = 0; f < ligForms.length; f++) {
19714 if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19715 returnable += normalForm;
19720 } // try words ligatures
19723 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19724 var _normalForm = reference.ligatureWordList[v3];
19726 if (unicodeLigatures["default"].words[_normalForm] === letter) {
19727 returnable += _normalForm;
19732 returnable += letter; // console.log('kept the letter')
19737 var Normal_1 = Normal;
19738 var Normalization = /*#__PURE__*/Object.defineProperty({
19744 function CharShaper(letter, form) {
19745 if (!isArabic_1.isArabic(letter)) {
19747 throw new Error('Not Arabic');
19750 if (letter === "\u0621") {
19755 for (var w = 0; w < reference.letterList.length; w++) {
19756 // ok so we are checking this potential lettertron
19757 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19758 var versions = Object.keys(letterForms);
19760 for (var v = 0; v < versions.length; v++) {
19761 var localVersion = letterForms[versions[v]];
19763 if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19764 if (versions.indexOf(form) > -1) {
19765 return letterForms[form];
19767 } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19769 var embeddedVersions = Object.keys(localVersion);
19771 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19772 if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19773 if (embeddedVersions.indexOf(form) > -1) {
19774 return localVersion[form];
19783 var CharShaper_2 = CharShaper;
19784 var CharShaper_1 = /*#__PURE__*/Object.defineProperty({
19785 CharShaper: CharShaper_2
19790 function WordShaper$1(word) {
19791 var state = 'initial';
19794 for (var w = 0; w < word.length; w++) {
19795 var nextLetter = ' ';
19797 for (var nxw = w + 1; nxw < word.length; nxw++) {
19798 if (!isArabic_1.isArabic(word[nxw])) {
19802 if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19803 nextLetter = word[nxw];
19808 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19809 // space or other non-Arabic
19812 } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19813 // tashkeel - add without changing state
19815 } else if (nextLetter === ' ' || // last Arabic letter in this word
19816 reference.lineBreakers.indexOf(word[w]) > -1) {
19817 // the current letter is known to break lines
19818 output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19820 } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19821 // LA letters - advance an additional letter after this
19822 output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19824 while (word[w] !== nextLetter) {
19830 output += CharShaper_1.CharShaper(word[w], state);
19838 var WordShaper_2 = WordShaper$1;
19839 var WordShaper_1 = /*#__PURE__*/Object.defineProperty({
19840 WordShaper: WordShaper_2
19845 function ParentLetter(letter) {
19846 if (!isArabic_1.isArabic(letter)) {
19847 throw new Error('Not an Arabic letter');
19850 for (var w = 0; w < reference.letterList.length; w++) {
19851 // ok so we are checking this potential lettertron
19852 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19853 var versions = Object.keys(letterForms);
19855 for (var v = 0; v < versions.length; v++) {
19856 var localVersion = letterForms[versions[v]];
19858 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19859 // look at this embedded object
19860 var embeddedForms = Object.keys(localVersion);
19862 for (var ef = 0; ef < embeddedForms.length; ef++) {
19863 var form = localVersion[embeddedForms[ef]];
19865 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19867 return localVersion;
19870 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19872 return letterForms;
19880 var ParentLetter_2 = ParentLetter;
19882 function GrandparentLetter(letter) {
19883 if (!isArabic_1.isArabic(letter)) {
19884 throw new Error('Not an Arabic letter');
19887 for (var w = 0; w < reference.letterList.length; w++) {
19888 // ok so we are checking this potential lettertron
19889 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19890 var versions = Object.keys(letterForms);
19892 for (var v = 0; v < versions.length; v++) {
19893 var localVersion = letterForms[versions[v]];
19895 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19896 // look at this embedded object
19897 var embeddedForms = Object.keys(localVersion);
19899 for (var ef = 0; ef < embeddedForms.length; ef++) {
19900 var form = localVersion[embeddedForms[ef]];
19902 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19904 return letterForms;
19907 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19909 return letterForms;
19917 var GrandparentLetter_1 = GrandparentLetter;
19918 var ParentLetter_1 = /*#__PURE__*/Object.defineProperty({
19919 ParentLetter: ParentLetter_2,
19920 GrandparentLetter: GrandparentLetter_1
19925 isArabic_1.isArabic;
19926 GlyphSplitter_1.GlyphSplitter;
19927 BaselineSplitter_1.BaselineSplitter;
19928 Normalization.Normal;
19929 CharShaper_1.CharShaper;
19930 var WordShaper = WordShaper_1.WordShaper;
19931 ParentLetter_1.ParentLetter;
19932 ParentLetter_1.GrandparentLetter;
19934 var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19935 function fixRTLTextForSvg(inputText) {
19938 var arabicRegex = /[\u0600-\u06FF]/g;
19939 var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19940 var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19941 var thaanaVowel = /[\u07A6-\u07B0]/;
19942 var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19944 if (arabicRegex.test(inputText)) {
19945 inputText = WordShaper(inputText);
19948 for (var n = 0; n < inputText.length; n++) {
19949 var c = inputText[n];
19951 if (arabicMath.test(c)) {
19952 // Arabic numbers go LTR
19953 ret += rtlBuffer.reverse().join('');
19956 if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19957 ret += rtlBuffer.reverse().join('');
19961 if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19962 rtlBuffer[rtlBuffer.length - 1] += c;
19963 } else if (rtlRegex.test(c) // include Arabic presentation forms
19964 || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19966 } else if (c === ' ' && rtlBuffer.length) {
19967 // whitespace within RTL text
19968 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19970 // non-RTL character
19971 ret += rtlBuffer.reverse().join('') + c;
19977 ret += rtlBuffer.reverse().join('');
19981 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19983 // `Object.{ entries, values }` methods implementation
19984 var createMethod$1 = function (TO_ENTRIES) {
19985 return function (it) {
19986 var O = toIndexedObject(it);
19987 var keys = objectKeys(O);
19988 var length = keys.length;
19992 while (length > i) {
19994 if (!descriptors || propertyIsEnumerable.call(O, key)) {
19995 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
20002 var objectToArray = {
20003 // `Object.entries` method
20004 // https://tc39.es/ecma262/#sec-object.entries
20005 entries: createMethod$1(true),
20006 // `Object.values` method
20007 // https://tc39.es/ecma262/#sec-object.values
20008 values: createMethod$1(false)
20011 var $values = objectToArray.values;
20013 // `Object.values` method
20014 // https://tc39.es/ecma262/#sec-object.values
20015 _export({ target: 'Object', stat: true }, {
20016 values: function values(O) {
20021 // https://github.com/openstreetmap/iD/issues/772
20022 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
20026 _storage = localStorage;
20027 } catch (e) {} // eslint-disable-line no-empty
20030 _storage = _storage || function () {
20033 getItem: function getItem(k) {
20036 setItem: function setItem(k, v) {
20039 removeItem: function removeItem(k) {
20040 return delete s[k];
20044 // corePreferences is an interface for persisting basic key-value strings
20045 // within and between iD sessions on the same site.
20049 function corePreferences(k, v) {
20051 if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
20053 /* eslint-disable no-console */
20054 if (typeof console !== 'undefined') {
20055 console.error('localStorage quota exceeded');
20057 /* eslint-enable no-console */
20062 var vparse = createCommonjsModule(function (module) {
20063 (function (window) {
20065 function parseVersion(v) {
20066 var m = v.replace(/[^0-9.]/g, '').match(/[0-9]*\.|[0-9]+/g) || [];
20073 v.isEmpty = !v.major && !v.minor && !v.patch && !v.build;
20074 v.parsed = [v.major, v.minor, v.patch, v.build];
20075 v.text = v.parsed.join('.');
20076 v.compare = compare;
20080 function compare(v) {
20081 if (typeof v === 'string') {
20082 v = parseVersion(v);
20085 for (var i = 0; i < 4; i++) {
20086 if (this.parsed[i] !== v.parsed[i]) {
20087 return this.parsed[i] > v.parsed[i] ? 1 : -1;
20093 /* istanbul ignore next */
20096 if (module && 'object' === 'object') {
20097 module.exports = parseVersion;
20099 window.parseVersion = parseVersion;
20101 })(commonjsGlobal);
20105 var version = "2.20.1";
20106 var description = "A friendly editor for OpenStreetMap";
20107 var main = "dist/iD.min.js";
20108 var repository = "github:openstreetmap/iD";
20109 var homepage = "https://github.com/openstreetmap/iD";
20110 var bugs = "https://github.com/openstreetmap/iD/issues";
20111 var keywords = ["editor","openstreetmap"];
20112 var license = "ISC";
20113 var scripts = {all:"npm-run-all -s clean build build:legacy dist",build:"npm-run-all -s build:css build:data build:dev","build:css":"node scripts/build_css.js","build:data":"shx mkdir -p dist/data && node scripts/build_data.js","build:dev":"rollup --config config/rollup.config.dev.js","build:legacy":"rollup --config config/rollup.config.legacy.js","build:stats":"rollup --config config/rollup.config.stats.js",clean:"shx rm -f dist/*.js dist/*.map dist/*.css dist/img/*.svg",dist:"npm-run-all -p dist:**","dist:mapillary":"shx mkdir -p dist/mapillary-js && shx cp -R node_modules/mapillary-js/dist/* dist/mapillary-js/","dist:pannellum":"shx mkdir -p dist/pannellum-streetside && shx cp -R node_modules/pannellum/build/* dist/pannellum-streetside/","dist:min:iD":"uglifyjs dist/iD.legacy.js --compress --mangle --output dist/iD.min.js","dist:svg:iD":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"iD-%s\" --symbol-sprite dist/img/iD-sprite.svg \"svg/iD-sprite/**/*.svg\"","dist:svg:community":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"community-%s\" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg","dist:svg:fa":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg","dist:svg:maki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"maki-%s\" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg","dist:svg:mapillary:signs":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg","dist:svg:mapillary:objects":"svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-object-sprite.svg node_modules/mapillary_sprite_source/package_objects/*.svg","dist:svg:temaki":"svg-sprite --symbol --symbol-dest . --shape-id-generator \"temaki-%s\" --symbol-sprite dist/img/temaki-sprite.svg node_modules/@ideditor/temaki/icons/*.svg",imagery:"node scripts/update_imagery.js",lint:"eslint scripts test/spec modules","lint:fix":"eslint scripts test/spec modules --fix",start:"npm-run-all -s build start:server",quickstart:"npm-run-all -s build:dev start:server","start:server":"node scripts/server.js",test:"npm-run-all -s lint build:css build:data build:legacy test:spec","test:spec":"phantomjs --web-security=no node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html spec",translations:"node scripts/update_locales.js"};
20114 var dependencies = {"@ideditor/country-coder":"~5.0.3","@ideditor/location-conflation":"~1.0.2","@mapbox/geojson-area":"^0.2.2","@mapbox/sexagesimal":"1.2.0","@mapbox/vector-tile":"^1.3.1","@tmcw/togeojson":"^4.5.0","@turf/bbox-clip":"^6.0.0","abortcontroller-polyfill":"^1.4.0","aes-js":"^3.1.2","alif-toolkit":"^1.2.9","core-js":"^3.6.5",diacritics:"1.3.0","fast-deep-equal":"~3.1.1","fast-json-stable-stringify":"2.1.0","lodash-es":"~4.17.15",marked:"~2.0.0","node-diff3":"2.1.0","osm-auth":"1.1.0",pannellum:"2.5.6",pbf:"^3.2.1","polygon-clipping":"~0.15.1",rbush:"3.0.1","whatwg-fetch":"^3.4.1","which-polygon":"2.2.0"};
20115 var devDependencies = {"@babel/core":"^7.11.6","@babel/preset-env":"^7.11.5","@fortawesome/fontawesome-svg-core":"^1.2.32","@fortawesome/free-brands-svg-icons":"~5.15.1","@fortawesome/free-regular-svg-icons":"~5.15.1","@fortawesome/free-solid-svg-icons":"~5.15.1","@ideditor/temaki":"~4.4.0","@mapbox/maki":"^6.0.0","@rollup/plugin-babel":"^5.2.1","@rollup/plugin-commonjs":"^17.0.0","@rollup/plugin-json":"^4.0.1","@rollup/plugin-node-resolve":"~11.2.0",autoprefixer:"^10.0.1",btoa:"^1.2.1",chai:"^4.1.0","cldr-core":"37.0.0","cldr-localenames-full":"37.0.0",colors:"^1.1.2","concat-files":"^0.1.1",d3:"~6.6.0","editor-layer-index":"github:osmlab/editor-layer-index#gh-pages",eslint:"^7.1.0",gaze:"^1.1.3",glob:"^7.1.0",happen:"^0.3.1","js-yaml":"^4.0.0","json-stringify-pretty-compact":"^3.0.0",mapillary_sprite_source:"^1.8.0","mapillary-js":"4.0.0",minimist:"^1.2.3",mocha:"^7.0.1","mocha-phantomjs-core":"^2.1.0","name-suggestion-index":"~6.0","node-fetch":"^2.6.1","npm-run-all":"^4.0.0","object-inspect":"1.10.3","osm-community-index":"~5.1.0","phantomjs-prebuilt":"~2.1.16",postcss:"^8.1.1","postcss-selector-prepend":"^0.5.0",rollup:"~2.52.8","rollup-plugin-includepaths":"~0.2.3","rollup-plugin-progress":"^1.1.1","rollup-plugin-visualizer":"~4.2.0",shelljs:"^0.8.0",shx:"^0.3.0",sinon:"7.5.0","sinon-chai":"^3.3.0",smash:"0.0","static-server":"^2.2.1","svg-sprite":"1.5.1","uglify-js":"~3.13.0",vparse:"~1.1.0"};
20116 var engines = {node:">=10"};
20117 var browserslist = ["> 0.2%, last 6 major versions, Firefox ESR, IE 11, maintained node versions"];
20118 var packageJSON = {
20121 description: description,
20123 repository: repository,
20124 homepage: homepage,
20126 keywords: keywords,
20129 dependencies: dependencies,
20130 devDependencies: devDependencies,
20132 browserslist: browserslist
20135 var _mainFileFetcher = coreFileFetcher(); // singleton
20136 // coreFileFetcher asynchronously fetches data from JSON files
20139 function coreFileFetcher() {
20140 var ociVersion = packageJSON.devDependencies['osm-community-index'];
20141 var v = vparse(ociVersion);
20142 var vMinor = "".concat(v.major, ".").concat(v.minor);
20144 var _inflight = {};
20146 'address_formats': 'data/address_formats.min.json',
20147 'deprecated': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/deprecated.min.json',
20148 'discarded': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/discarded.min.json',
20149 'imagery': 'data/imagery.min.json',
20150 'intro_graph': 'data/intro_graph.min.json',
20151 'keepRight': 'data/keepRight.min.json',
20152 'languages': 'data/languages.min.json',
20153 'locales': 'locales/index.min.json',
20154 'oci_defaults': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/defaults.min.json"),
20155 'oci_features': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/featureCollection.min.json"),
20156 'oci_resources': "https://cdn.jsdelivr.net/npm/osm-community-index@".concat(vMinor, "/dist/resources.min.json"),
20157 'preset_categories': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_categories.min.json',
20158 'preset_defaults': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/preset_defaults.min.json',
20159 'preset_fields': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/fields.min.json',
20160 'preset_presets': 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/presets.min.json',
20161 'phone_formats': 'data/phone_formats.min.json',
20162 'qa_data': 'data/qa_data.min.json',
20163 'shortcuts': 'data/shortcuts.min.json',
20164 'territory_languages': 'data/territory_languages.min.json',
20165 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
20167 var _cachedData = {}; // expose the cache; useful for tests
20169 _this.cache = function () {
20170 return _cachedData;
20171 }; // Returns a Promise to fetch data
20172 // (resolved with the data if we have it already)
20175 _this.get = function (which) {
20176 if (_cachedData[which]) {
20177 return Promise.resolve(_cachedData[which]);
20180 var file = _fileMap[which];
20182 var url = file && _this.asset(file);
20185 return Promise.reject("Unknown data file for \"".concat(which, "\""));
20188 var prom = _inflight[url];
20191 _inflight[url] = prom = utilFetchJson(url).then(function (result) {
20192 delete _inflight[url];
20195 throw new Error("No data loaded for \"".concat(which, "\""));
20198 _cachedData[which] = result;
20200 })["catch"](function (err) {
20201 delete _inflight[url];
20207 }; // Accessor for the file map
20210 _this.fileMap = function (val) {
20211 if (!arguments.length) return _fileMap;
20216 var _assetPath = '';
20218 _this.assetPath = function (val) {
20219 if (!arguments.length) return _assetPath;
20224 var _assetMap = {};
20226 _this.assetMap = function (val) {
20227 if (!arguments.length) return _assetMap;
20232 _this.asset = function (val) {
20233 if (/^http(s)?:\/\//i.test(val)) return val;
20234 var filename = _assetPath + val;
20235 return _assetMap[filename] || filename;
20241 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
20242 var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
20243 var defineProperty = objectDefineProperty.f;
20244 var trim$2 = stringTrim.trim;
20246 var NUMBER = 'Number';
20247 var NativeNumber = global$2[NUMBER];
20248 var NumberPrototype = NativeNumber.prototype;
20250 // Opera ~12 has broken Object#toString
20251 var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20253 // `ToNumber` abstract operation
20254 // https://tc39.es/ecma262/#sec-tonumber
20255 var toNumber$1 = function (argument) {
20256 var it = toPrimitive(argument, false);
20257 var first, third, radix, maxCode, digits, length, index, code;
20258 if (typeof it == 'string' && it.length > 2) {
20260 first = it.charCodeAt(0);
20261 if (first === 43 || first === 45) {
20262 third = it.charCodeAt(2);
20263 if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20264 } else if (first === 48) {
20265 switch (it.charCodeAt(1)) {
20266 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20267 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20268 default: return +it;
20270 digits = it.slice(2);
20271 length = digits.length;
20272 for (index = 0; index < length; index++) {
20273 code = digits.charCodeAt(index);
20274 // parseInt parses a string to a first unavailable symbol
20275 // but ToNumber should return NaN if a string contains unavailable symbols
20276 if (code < 48 || code > maxCode) return NaN;
20277 } return parseInt(digits, radix);
20282 // `Number` constructor
20283 // https://tc39.es/ecma262/#sec-number-constructor
20284 if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20285 var NumberWrapper = function Number(value) {
20286 var it = arguments.length < 1 ? 0 : value;
20288 return dummy instanceof NumberWrapper
20289 // check on 1..constructor(foo) case
20290 && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20291 ? inheritIfRequired(new NativeNumber(toNumber$1(it)), dummy, NumberWrapper) : toNumber$1(it);
20293 for (var keys = descriptors ? getOwnPropertyNames(NativeNumber) : (
20295 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20296 // ES2015 (in case, if modules with ES2015 Number statics required before):
20297 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20298 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,' +
20301 ).split(','), j = 0, key; keys.length > j; j++) {
20302 if (has$1(NativeNumber, key = keys[j]) && !has$1(NumberWrapper, key)) {
20303 defineProperty(NumberWrapper, key, getOwnPropertyDescriptor$2(NativeNumber, key));
20306 NumberWrapper.prototype = NumberPrototype;
20307 NumberPrototype.constructor = NumberWrapper;
20308 redefine(global$2, NUMBER, NumberWrapper);
20311 // `thisNumberValue` abstract operation
20312 // https://tc39.es/ecma262/#sec-thisnumbervalue
20313 var thisNumberValue = function (value) {
20314 if (typeof value != 'number' && classofRaw(value) != 'Number') {
20315 throw TypeError('Incorrect invocation');
20320 // `String.prototype.repeat` method implementation
20321 // https://tc39.es/ecma262/#sec-string.prototype.repeat
20322 var stringRepeat = function repeat(count) {
20323 var str = String(requireObjectCoercible(this));
20325 var n = toInteger(count);
20326 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
20327 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
20331 var nativeToFixed = 1.0.toFixed;
20332 var floor = Math.floor;
20334 var pow = function (x, n, acc) {
20335 return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc);
20338 var log = function (x) {
20341 while (x2 >= 4096) {
20351 var multiply = function (data, n, c) {
20354 while (++index < 6) {
20355 c2 += n * data[index];
20356 data[index] = c2 % 1e7;
20357 c2 = floor(c2 / 1e7);
20361 var divide = function (data, n) {
20364 while (--index >= 0) {
20366 data[index] = floor(c / n);
20371 var dataToString = function (data) {
20374 while (--index >= 0) {
20375 if (s !== '' || index === 0 || data[index] !== 0) {
20376 var t = String(data[index]);
20377 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
20382 var FORCED$4 = nativeToFixed && (
20383 0.00008.toFixed(3) !== '0.000' ||
20384 0.9.toFixed(0) !== '1' ||
20385 1.255.toFixed(2) !== '1.25' ||
20386 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
20387 ) || !fails(function () {
20388 // V8 ~ Android 4.3-
20389 nativeToFixed.call({});
20392 // `Number.prototype.toFixed` method
20393 // https://tc39.es/ecma262/#sec-number.prototype.tofixed
20394 _export({ target: 'Number', proto: true, forced: FORCED$4 }, {
20395 toFixed: function toFixed(fractionDigits) {
20396 var number = thisNumberValue(this);
20397 var fractDigits = toInteger(fractionDigits);
20398 var data = [0, 0, 0, 0, 0, 0];
20403 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
20404 // eslint-disable-next-line no-self-compare -- NaN check
20405 if (number != number) return 'NaN';
20406 if (number <= -1e21 || number >= 1e21) return String(number);
20411 if (number > 1e-21) {
20412 e = log(number * pow(2, 69, 1)) - 69;
20413 z = e < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1);
20414 z *= 0x10000000000000;
20417 multiply(data, 0, z);
20420 multiply(data, 1e7, 0);
20423 multiply(data, pow(10, j, 1), 0);
20426 divide(data, 1 << 23);
20429 divide(data, 1 << j);
20430 multiply(data, 1, 1);
20432 result = dataToString(data);
20434 multiply(data, 0, z);
20435 multiply(data, 1 << -e, 0);
20436 result = dataToString(data) + stringRepeat.call('0', fractDigits);
20439 if (fractDigits > 0) {
20441 result = sign + (k <= fractDigits
20442 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
20443 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
20445 result = sign + result;
20450 var globalIsFinite = global$2.isFinite;
20452 // `Number.isFinite` method
20453 // https://tc39.es/ecma262/#sec-number.isfinite
20454 // eslint-disable-next-line es/no-number-isfinite -- safe
20455 var numberIsFinite = Number.isFinite || function isFinite(it) {
20456 return typeof it == 'number' && globalIsFinite(it);
20459 // `Number.isFinite` method
20460 // https://tc39.es/ecma262/#sec-number.isfinite
20461 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
20463 var fromCharCode = String.fromCharCode;
20464 // eslint-disable-next-line es/no-string-fromcodepoint -- required for testing
20465 var $fromCodePoint = String.fromCodePoint;
20467 // length should be 1, old FF problem
20468 var INCORRECT_LENGTH = !!$fromCodePoint && $fromCodePoint.length != 1;
20470 // `String.fromCodePoint` method
20471 // https://tc39.es/ecma262/#sec-string.fromcodepoint
20472 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
20473 // eslint-disable-next-line no-unused-vars -- required for `.length`
20474 fromCodePoint: function fromCodePoint(x) {
20476 var length = arguments.length;
20479 while (length > i) {
20480 code = +arguments[i++];
20481 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
20482 elements.push(code < 0x10000
20483 ? fromCharCode(code)
20484 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
20486 } return elements.join('');
20491 fixRegexpWellKnownSymbolLogic('search', function (SEARCH, nativeSearch, maybeCallNative) {
20493 // `String.prototype.search` method
20494 // https://tc39.es/ecma262/#sec-string.prototype.search
20495 function search(regexp) {
20496 var O = requireObjectCoercible(this);
20497 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
20498 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
20500 // `RegExp.prototype[@@search]` method
20501 // https://tc39.es/ecma262/#sec-regexp.prototype-@@search
20502 function (string) {
20503 var res = maybeCallNative(nativeSearch, this, string);
20504 if (res.done) return res.value;
20506 var rx = anObject(this);
20507 var S = String(string);
20509 var previousLastIndex = rx.lastIndex;
20510 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
20511 var result = regexpExecAbstract(rx, S);
20512 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
20513 return result === null ? -1 : result.index;
20518 var quickselect$1 = createCommonjsModule(function (module, exports) {
20519 (function (global, factory) {
20520 module.exports = factory() ;
20521 })(commonjsGlobal, function () {
20523 function quickselect(arr, k, left, right, compare) {
20524 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
20527 function quickselectStep(arr, k, left, right, compare) {
20528 while (right > left) {
20529 if (right - left > 600) {
20530 var n = right - left + 1;
20531 var m = k - left + 1;
20532 var z = Math.log(n);
20533 var s = 0.5 * Math.exp(2 * z / 3);
20534 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
20535 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
20536 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
20537 quickselectStep(arr, k, newLeft, newRight, compare);
20543 swap(arr, left, k);
20544 if (compare(arr[right], t) > 0) swap(arr, left, right);
20551 while (compare(arr[i], t) < 0) {
20555 while (compare(arr[j], t) > 0) {
20560 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
20562 swap(arr, j, right);
20564 if (j <= k) left = j + 1;
20565 if (k <= j) right = j - 1;
20569 function swap(arr, i, j) {
20575 function defaultCompare(a, b) {
20576 return a < b ? -1 : a > b ? 1 : 0;
20579 return quickselect;
20583 var rbush_1 = rbush;
20584 var _default$1 = rbush;
20586 function rbush(maxEntries, format) {
20587 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
20589 this._maxEntries = Math.max(4, maxEntries || 9);
20590 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
20593 this._initFormat(format);
20599 rbush.prototype = {
20600 all: function all() {
20601 return this._all(this.data, []);
20603 search: function search(bbox) {
20604 var node = this.data,
20606 toBBox = this.toBBox;
20607 if (!intersects$1(bbox, node)) return result;
20608 var nodesToSearch = [],
20615 for (i = 0, len = node.children.length; i < len; i++) {
20616 child = node.children[i];
20617 childBBox = node.leaf ? toBBox(child) : child;
20619 if (intersects$1(bbox, childBBox)) {
20620 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
20624 node = nodesToSearch.pop();
20629 collides: function collides(bbox) {
20630 var node = this.data,
20631 toBBox = this.toBBox;
20632 if (!intersects$1(bbox, node)) return false;
20633 var nodesToSearch = [],
20640 for (i = 0, len = node.children.length; i < len; i++) {
20641 child = node.children[i];
20642 childBBox = node.leaf ? toBBox(child) : child;
20644 if (intersects$1(bbox, childBBox)) {
20645 if (node.leaf || contains$1(bbox, childBBox)) return true;
20646 nodesToSearch.push(child);
20650 node = nodesToSearch.pop();
20655 load: function load(data) {
20656 if (!(data && data.length)) return this;
20658 if (data.length < this._minEntries) {
20659 for (var i = 0, len = data.length; i < len; i++) {
20660 this.insert(data[i]);
20664 } // recursively build the tree with the given data from scratch using OMT algorithm
20667 var node = this._build(data.slice(), 0, data.length - 1, 0);
20669 if (!this.data.children.length) {
20670 // save as is if tree is empty
20672 } else if (this.data.height === node.height) {
20673 // split root if trees have the same height
20674 this._splitRoot(this.data, node);
20676 if (this.data.height < node.height) {
20677 // swap trees if inserted one is bigger
20678 var tmpNode = this.data;
20681 } // insert the small tree into the large tree at appropriate level
20684 this._insert(node, this.data.height - node.height - 1, true);
20689 insert: function insert(item) {
20690 if (item) this._insert(item, this.data.height - 1);
20693 clear: function clear() {
20694 this.data = createNode$1([]);
20697 remove: function remove(item, equalsFn) {
20698 if (!item) return this;
20699 var node = this.data,
20700 bbox = this.toBBox(item),
20706 goingUp; // depth-first iterative tree traversal
20708 while (node || path.length) {
20712 parent = path[path.length - 1];
20718 // check current node
20719 index = findItem$1(item, node.children, equalsFn);
20721 if (index !== -1) {
20722 // item found, remove the item and condense tree upwards
20723 node.children.splice(index, 1);
20726 this._condense(path);
20732 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
20738 node = node.children[0];
20739 } else if (parent) {
20742 node = parent.children[i];
20744 } else node = null; // nothing found
20750 toBBox: function toBBox(item) {
20753 compareMinX: compareNodeMinX$1,
20754 compareMinY: compareNodeMinY$1,
20755 toJSON: function toJSON() {
20758 fromJSON: function fromJSON(data) {
20762 _all: function _all(node, result) {
20763 var nodesToSearch = [];
20766 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
20767 node = nodesToSearch.pop();
20772 _build: function _build(items, left, right, height) {
20773 var N = right - left + 1,
20774 M = this._maxEntries,
20778 // reached leaf level; return leaf
20779 node = createNode$1(items.slice(left, right + 1));
20780 calcBBox$1(node, this.toBBox);
20785 // target height of the bulk-loaded tree
20786 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
20788 M = Math.ceil(N / Math.pow(M, height - 1));
20791 node = createNode$1([]);
20793 node.height = height; // split the items into M mostly square tiles
20795 var N2 = Math.ceil(N / M),
20796 N1 = N2 * Math.ceil(Math.sqrt(M)),
20801 multiSelect$1(items, left, right, N1, this.compareMinX);
20803 for (i = left; i <= right; i += N1) {
20804 right2 = Math.min(i + N1 - 1, right);
20805 multiSelect$1(items, i, right2, N2, this.compareMinY);
20807 for (j = i; j <= right2; j += N2) {
20808 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
20810 node.children.push(this._build(items, j, right3, height - 1));
20814 calcBBox$1(node, this.toBBox);
20817 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
20818 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
20822 if (node.leaf || path.length - 1 === level) break;
20823 minArea = minEnlargement = Infinity;
20825 for (i = 0, len = node.children.length; i < len; i++) {
20826 child = node.children[i];
20827 area = bboxArea$1(child);
20828 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
20830 if (enlargement < minEnlargement) {
20831 minEnlargement = enlargement;
20832 minArea = area < minArea ? area : minArea;
20833 targetNode = child;
20834 } else if (enlargement === minEnlargement) {
20835 // otherwise choose one with the smallest area
20836 if (area < minArea) {
20838 targetNode = child;
20843 node = targetNode || node.children[0];
20848 _insert: function _insert(item, level, isNode) {
20849 var toBBox = this.toBBox,
20850 bbox = isNode ? item : toBBox(item),
20851 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
20853 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
20856 node.children.push(item);
20857 extend$2(node, bbox); // split on node overflow; propagate upwards if necessary
20859 while (level >= 0) {
20860 if (insertPath[level].children.length > this._maxEntries) {
20861 this._split(insertPath, level);
20865 } // adjust bboxes along the insertion path
20868 this._adjustParentBBoxes(bbox, insertPath, level);
20870 // split overflowed node into two
20871 _split: function _split(insertPath, level) {
20872 var node = insertPath[level],
20873 M = node.children.length,
20874 m = this._minEntries;
20876 this._chooseSplitAxis(node, m, M);
20878 var splitIndex = this._chooseSplitIndex(node, m, M);
20880 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
20881 newNode.height = node.height;
20882 newNode.leaf = node.leaf;
20883 calcBBox$1(node, this.toBBox);
20884 calcBBox$1(newNode, this.toBBox);
20885 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
20887 _splitRoot: function _splitRoot(node, newNode) {
20889 this.data = createNode$1([node, newNode]);
20890 this.data.height = node.height + 1;
20891 this.data.leaf = false;
20892 calcBBox$1(this.data, this.toBBox);
20894 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
20895 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
20896 minOverlap = minArea = Infinity;
20898 for (i = m; i <= M - m; i++) {
20899 bbox1 = distBBox$1(node, 0, i, this.toBBox);
20900 bbox2 = distBBox$1(node, i, M, this.toBBox);
20901 overlap = intersectionArea$1(bbox1, bbox2);
20902 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
20904 if (overlap < minOverlap) {
20905 minOverlap = overlap;
20907 minArea = area < minArea ? area : minArea;
20908 } else if (overlap === minOverlap) {
20909 // otherwise choose distribution with minimum area
20910 if (area < minArea) {
20919 // sorts node children by the best axis for split
20920 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
20921 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
20922 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
20923 xMargin = this._allDistMargin(node, m, M, compareMinX),
20924 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
20925 // otherwise it's already sorted by minY
20928 if (xMargin < yMargin) node.children.sort(compareMinX);
20930 // total margin of all possible split distributions where each node is at least m full
20931 _allDistMargin: function _allDistMargin(node, m, M, compare) {
20932 node.children.sort(compare);
20933 var toBBox = this.toBBox,
20934 leftBBox = distBBox$1(node, 0, m, toBBox),
20935 rightBBox = distBBox$1(node, M - m, M, toBBox),
20936 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
20940 for (i = m; i < M - m; i++) {
20941 child = node.children[i];
20942 extend$2(leftBBox, node.leaf ? toBBox(child) : child);
20943 margin += bboxMargin$1(leftBBox);
20946 for (i = M - m - 1; i >= m; i--) {
20947 child = node.children[i];
20948 extend$2(rightBBox, node.leaf ? toBBox(child) : child);
20949 margin += bboxMargin$1(rightBBox);
20954 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
20955 // adjust bboxes along the given tree path
20956 for (var i = level; i >= 0; i--) {
20957 extend$2(path[i], bbox);
20960 _condense: function _condense(path) {
20961 // go through the path, removing empty nodes and updating bboxes
20962 for (var i = path.length - 1, siblings; i >= 0; i--) {
20963 if (path[i].children.length === 0) {
20965 siblings = path[i - 1].children;
20966 siblings.splice(siblings.indexOf(path[i]), 1);
20967 } else this.clear();
20968 } else calcBBox$1(path[i], this.toBBox);
20971 _initFormat: function _initFormat(format) {
20972 // data format (minX, minY, maxX, maxY accessors)
20973 // uses eval-type function compilation instead of just accepting a toBBox function
20974 // because the algorithms are very sensitive to sorting functions performance,
20975 // so they should be dead simple and without inner calls
20976 var compareArr = ['return a', ' - b', ';'];
20977 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
20978 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
20979 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
20983 function findItem$1(item, items, equalsFn) {
20984 if (!equalsFn) return items.indexOf(item);
20986 for (var i = 0; i < items.length; i++) {
20987 if (equalsFn(item, items[i])) return i;
20991 } // calculate node's bbox from bboxes of its children
20994 function calcBBox$1(node, toBBox) {
20995 distBBox$1(node, 0, node.children.length, toBBox, node);
20996 } // min bounding rectangle of node children from k to p-1
20999 function distBBox$1(node, k, p, toBBox, destNode) {
21000 if (!destNode) destNode = createNode$1(null);
21001 destNode.minX = Infinity;
21002 destNode.minY = Infinity;
21003 destNode.maxX = -Infinity;
21004 destNode.maxY = -Infinity;
21006 for (var i = k, child; i < p; i++) {
21007 child = node.children[i];
21008 extend$2(destNode, node.leaf ? toBBox(child) : child);
21014 function extend$2(a, b) {
21015 a.minX = Math.min(a.minX, b.minX);
21016 a.minY = Math.min(a.minY, b.minY);
21017 a.maxX = Math.max(a.maxX, b.maxX);
21018 a.maxY = Math.max(a.maxY, b.maxY);
21022 function compareNodeMinX$1(a, b) {
21023 return a.minX - b.minX;
21026 function compareNodeMinY$1(a, b) {
21027 return a.minY - b.minY;
21030 function bboxArea$1(a) {
21031 return (a.maxX - a.minX) * (a.maxY - a.minY);
21034 function bboxMargin$1(a) {
21035 return a.maxX - a.minX + (a.maxY - a.minY);
21038 function enlargedArea$1(a, b) {
21039 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));
21042 function intersectionArea$1(a, b) {
21043 var minX = Math.max(a.minX, b.minX),
21044 minY = Math.max(a.minY, b.minY),
21045 maxX = Math.min(a.maxX, b.maxX),
21046 maxY = Math.min(a.maxY, b.maxY);
21047 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
21050 function contains$1(a, b) {
21051 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
21054 function intersects$1(a, b) {
21055 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
21058 function createNode$1(children) {
21060 children: children,
21068 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
21069 // combines selection algorithm with binary divide & conquer approach
21072 function multiSelect$1(arr, left, right, n, compare) {
21073 var stack = [left, right],
21076 while (stack.length) {
21077 right = stack.pop();
21078 left = stack.pop();
21079 if (right - left <= n) continue;
21080 mid = left + Math.ceil((right - left) / n / 2) * n;
21081 quickselect$1(arr, mid, left, right, compare);
21082 stack.push(left, mid, mid, right);
21085 rbush_1["default"] = _default$1;
21087 var lineclip_1 = lineclip$1;
21088 lineclip$1.polyline = lineclip$1;
21089 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
21090 // handle polylines rather than just segments
21092 function lineclip$1(points, bbox, result) {
21093 var len = points.length,
21094 codeA = bitCode$1(points[0], bbox),
21101 if (!result) result = [];
21103 for (i = 1; i < len; i++) {
21106 codeB = lastCode = bitCode$1(b, bbox);
21109 if (!(codeA | codeB)) {
21113 if (codeB !== lastCode) {
21114 // segment went outside
21118 // start a new line
21122 } else if (i === len - 1) {
21127 } else if (codeA & codeB) {
21130 } else if (codeA) {
21131 // a outside, intersect with clip edge
21132 a = intersect$1(a, b, codeA, bbox);
21133 codeA = bitCode$1(a, bbox);
21136 b = intersect$1(a, b, codeB, bbox);
21137 codeB = bitCode$1(b, bbox);
21144 if (part.length) result.push(part);
21146 } // Sutherland-Hodgeman polygon clipping algorithm
21149 function polygonclip$1(points, bbox) {
21150 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
21152 for (edge = 1; edge <= 8; edge *= 2) {
21154 prev = points[points.length - 1];
21155 prevInside = !(bitCode$1(prev, bbox) & edge);
21157 for (i = 0; i < points.length; i++) {
21159 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
21161 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
21162 if (inside) result.push(p); // add a point if it's inside
21165 prevInside = inside;
21169 if (!points.length) break;
21173 } // intersect a segment against one of the 4 lines that make up the bbox
21176 function intersect$1(a, b, edge, bbox) {
21177 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
21178 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
21179 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
21180 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
21182 } // bit code reflects the point position relative to the bbox:
21184 // top 1001 1000 1010
21185 // mid 0001 0000 0010
21186 // bottom 0101 0100 0110
21189 function bitCode$1(p, bbox) {
21191 if (p[0] < bbox[0]) code |= 1; // left
21192 else if (p[0] > bbox[2]) code |= 2; // right
21194 if (p[1] < bbox[1]) code |= 4; // bottom
21195 else if (p[1] > bbox[3]) code |= 8; // top
21200 var whichPolygon_1 = whichPolygon;
21202 function whichPolygon(data) {
21205 for (var i = 0; i < data.features.length; i++) {
21206 var feature = data.features[i];
21207 var coords = feature.geometry.coordinates;
21209 if (feature.geometry.type === 'Polygon') {
21210 bboxes.push(treeItem(coords, feature.properties));
21211 } else if (feature.geometry.type === 'MultiPolygon') {
21212 for (var j = 0; j < coords.length; j++) {
21213 bboxes.push(treeItem(coords[j], feature.properties));
21218 var tree = rbush_1().load(bboxes);
21220 function query(p, multi) {
21222 result = tree.search({
21229 for (var i = 0; i < result.length; i++) {
21230 if (insidePolygon(result[i].coords, p)) {
21231 if (multi) output.push(result[i].props);else return result[i].props;
21235 return multi && output.length ? output : null;
21240 query.bbox = function queryBBox(bbox) {
21242 var result = tree.search({
21249 for (var i = 0; i < result.length; i++) {
21250 if (polygonIntersectsBBox(result[i].coords, bbox)) {
21251 output.push(result[i].props);
21261 function polygonIntersectsBBox(polygon, bbox) {
21262 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
21263 if (insidePolygon(polygon, bboxCenter)) return true;
21265 for (var i = 0; i < polygon.length; i++) {
21266 if (lineclip_1(polygon[i], bbox).length > 0) return true;
21270 } // ray casting algorithm for detecting if point is in polygon
21273 function insidePolygon(rings, p) {
21274 var inside = false;
21276 for (var i = 0, len = rings.length; i < len; i++) {
21277 var ring = rings[i];
21279 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
21280 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
21287 function rayIntersect(p, p1, p2) {
21288 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];
21291 function treeItem(coords, props) {
21301 for (var i = 0; i < coords[0].length; i++) {
21302 var p = coords[0][i];
21303 item.minX = Math.min(item.minX, p[0]);
21304 item.minY = Math.min(item.minY, p[1]);
21305 item.maxX = Math.max(item.maxX, p[0]);
21306 item.maxY = Math.max(item.maxY, p[1]);
21312 var type = "FeatureCollection";
21318 aliases: ["GB-ENG"],
21320 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21322 roadSpeedUnit: "mph",
21323 roadHeightUnit: "ft",
21324 callingCodes: ["44"]
21327 type: "MultiPolygon",
21328 coordinates: [[[[-6.03913, 51.13217], [-7.74976, 48.64773], [1.17405, 50.74239], [2.18458, 51.52087], [2.56575, 51.85301], [0.792, 57.56437], [-2.30613, 55.62698], [-2.17058, 55.45916], [-2.6095, 55.28488], [-2.63532, 55.19452], [-3.02906, 55.04606], [-3.09361, 54.94924], [-3.38407, 54.94278], [-4.1819, 54.57861], [-3.5082, 53.54318], [-3.08228, 53.25526], [-3.03675, 53.25092], [-2.92329, 53.19383], [-2.92022, 53.17685], [-2.98598, 53.15589], [-2.90649, 53.10964], [-2.87469, 53.12337], [-2.89131, 53.09374], [-2.83133, 52.99184], [-2.7251, 52.98389], [-2.72221, 52.92969], [-2.80549, 52.89428], [-2.85897, 52.94487], [-2.92401, 52.93836], [-2.97243, 52.9651], [-3.13576, 52.895], [-3.15744, 52.84947], [-3.16105, 52.79599], [-3.08734, 52.77504], [-3.01001, 52.76636], [-2.95581, 52.71794], [-3.01724, 52.72083], [-3.04398, 52.65435], [-3.13648, 52.58208], [-3.12926, 52.5286], [-3.09746, 52.53077], [-3.08662, 52.54811], [-3.00929, 52.57774], [-2.99701, 52.551], [-3.03603, 52.49969], [-3.13359, 52.49174], [-3.22971, 52.45344], [-3.22754, 52.42526], [-3.04687, 52.34504], [-2.95364, 52.3501], [-2.99701, 52.323], [-3.00785, 52.2753], [-3.09289, 52.20546], [-3.12638, 52.08114], [-2.97111, 51.90456], [-2.8818, 51.93196], [-2.78742, 51.88833], [-2.74277, 51.84367], [-2.66234, 51.83555], [-2.66336, 51.59504], [-3.20563, 51.31615], [-6.03913, 51.13217]]]]
21334 nameEn: "Scotland",
21335 aliases: ["GB-SCT"],
21337 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21339 roadSpeedUnit: "mph",
21340 roadHeightUnit: "ft",
21341 callingCodes: ["44"]
21344 type: "MultiPolygon",
21345 coordinates: [[[[0.792, 57.56437], [-0.3751, 61.32236], [-14.78497, 57.60709], [-6.82333, 55.83103], [-4.69044, 54.3629], [-3.38407, 54.94278], [-3.09361, 54.94924], [-3.02906, 55.04606], [-2.63532, 55.19452], [-2.6095, 55.28488], [-2.17058, 55.45916], [-2.30613, 55.62698], [0.792, 57.56437]]]]
21352 aliases: ["GB-WLS"],
21354 groups: ["Q23666", "Q3336843", "154", "150", "UN"],
21356 roadSpeedUnit: "mph",
21357 roadHeightUnit: "ft",
21358 callingCodes: ["44"]
21361 type: "MultiPolygon",
21362 coordinates: [[[[-3.5082, 53.54318], [-5.37267, 53.63269], [-6.03913, 51.13217], [-3.20563, 51.31615], [-2.66336, 51.59504], [-2.66234, 51.83555], [-2.74277, 51.84367], [-2.78742, 51.88833], [-2.8818, 51.93196], [-2.97111, 51.90456], [-3.12638, 52.08114], [-3.09289, 52.20546], [-3.00785, 52.2753], [-2.99701, 52.323], [-2.95364, 52.3501], [-3.04687, 52.34504], [-3.22754, 52.42526], [-3.22971, 52.45344], [-3.13359, 52.49174], [-3.03603, 52.49969], [-2.99701, 52.551], [-3.00929, 52.57774], [-3.08662, 52.54811], [-3.09746, 52.53077], [-3.12926, 52.5286], [-3.13648, 52.58208], [-3.04398, 52.65435], [-3.01724, 52.72083], [-2.95581, 52.71794], [-3.01001, 52.76636], [-3.08734, 52.77504], [-3.16105, 52.79599], [-3.15744, 52.84947], [-3.13576, 52.895], [-2.97243, 52.9651], [-2.92401, 52.93836], [-2.85897, 52.94487], [-2.80549, 52.89428], [-2.72221, 52.92969], [-2.7251, 52.98389], [-2.83133, 52.99184], [-2.89131, 53.09374], [-2.87469, 53.12337], [-2.90649, 53.10964], [-2.98598, 53.15589], [-2.92022, 53.17685], [-2.92329, 53.19383], [-3.03675, 53.25092], [-3.08228, 53.25526], [-3.5082, 53.54318]]]]
21368 nameEn: "Northern Ireland",
21369 aliases: ["GB-NIR"],
21371 groups: ["Q22890", "Q3336843", "154", "150", "UN"],
21373 roadSpeedUnit: "mph",
21374 roadHeightUnit: "ft",
21375 callingCodes: ["44"]
21378 type: "MultiPolygon",
21379 coordinates: [[[[-6.34755, 55.49206], [-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], [-4.69044, 54.3629], [-6.34755, 55.49206]]]]
21387 groups: ["EU", "154", "150", "UN"],
21388 callingCodes: ["45"]
21391 type: "MultiPolygon",
21392 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]]]]
21398 nameEn: "Netherlands",
21400 groups: ["EU", "155", "150", "UN"],
21401 callingCodes: ["31"]
21404 type: "MultiPolygon",
21405 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]]]]
21412 aliases: ["US-HI"],
21414 groups: ["Q35657", "061", "009", "UN"],
21415 roadSpeedUnit: "mph",
21416 roadHeightUnit: "ft",
21417 callingCodes: ["1"]
21420 type: "MultiPolygon",
21421 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]]]]
21428 aliases: ["US-AK"],
21430 groups: ["Q35657", "021", "003", "019", "UN"],
21431 roadSpeedUnit: "mph",
21432 roadHeightUnit: "ft",
21433 callingCodes: ["1"]
21436 type: "MultiPolygon",
21437 coordinates: [[[[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]]]]
21444 aliases: ["ID-SM"],
21446 groups: ["035", "142", "UN"],
21448 callingCodes: ["62"]
21451 type: "MultiPolygon",
21452 coordinates: [[[[109.82788, 2.86812], [110.90339, 7.52694], [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], [102.92489, -8.17146], [106.32259, -5.50116], [106.38511, -5.16715], [109.17017, -4.07401], [109.3962, -2.07276], [108.50935, -2.01066], [107.94791, 1.06924], [109.82788, 2.86812]]]]
21459 aliases: ["ID-JW"],
21461 groups: ["035", "142", "UN"],
21463 callingCodes: ["62"]
21466 type: "MultiPolygon",
21467 coordinates: [[[[109.17017, -4.07401], [106.38511, -5.16715], [106.32259, -5.50116], [102.92489, -8.17146], [116.22542, -10.49172], [114.39575, -8.2889], [114.42235, -8.09762], [114.92859, -7.49253], [116.33992, -7.56171], [116.58433, -5.30385], [109.17017, -4.07401]]]]
21473 nameEn: "Kalimantan",
21474 aliases: ["ID-KA"],
21476 groups: ["Q36117", "035", "142", "UN"],
21478 callingCodes: ["62"]
21481 type: "MultiPolygon",
21482 coordinates: [[[[120.02464, 2.83703], [118.06469, 4.16638], [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.82788, 2.86812], [107.94791, 1.06924], [108.50935, -2.01066], [109.3962, -2.07276], [109.17017, -4.07401], [116.58433, -5.30385], [120.02464, 2.83703]]]]
21488 nameEn: "Lesser Sunda Islands",
21489 aliases: ["ID-NU"],
21491 groups: ["035", "142", "UN"],
21493 callingCodes: ["62"]
21496 type: "MultiPolygon",
21497 coordinates: [[[[116.96967, -8.01483], [114.92859, -7.49253], [114.42235, -8.09762], [114.39575, -8.2889], [116.22542, -10.49172], [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.87688, -7.49892], [116.96967, -8.01483]]]]
21503 nameEn: "Sulawesi",
21504 aliases: ["ID-SL"],
21506 groups: ["035", "142", "UN"],
21508 callingCodes: ["62"]
21511 type: "MultiPolygon",
21512 coordinates: [[[[128.34321, 3.90322], [126.69413, 6.02692], [119.56457, 0.90759], [116.58433, -5.30385], [116.33992, -7.56171], [116.96967, -8.01483], [125.87688, -7.49892], [123.78965, -0.86805], [128.34321, 3.90322]]]]
21518 nameEn: "Maluku Islands",
21519 aliases: ["ID-ML"],
21521 groups: ["035", "142", "UN"],
21523 callingCodes: ["62"]
21526 type: "MultiPolygon",
21527 coordinates: [[[[129.63187, 2.21409], [128.34321, 3.90322], [123.78965, -0.86805], [125.87688, -7.49892], [125.58506, -7.95311], [125.87691, -8.31789], [127.42116, -8.22471], [127.55165, -9.05052], [135.49042, -9.2276], [135.35517, -5.01442], [132.8312, -4.70282], [130.8468, -2.61103], [128.40647, -2.30349], [129.71519, -0.24692], [129.63187, 2.21409]]]]
21533 nameEn: "Western New Guinea",
21534 aliases: ["ID-PP"],
21536 groups: ["035", "142", "UN"],
21538 callingCodes: ["62"]
21541 type: "MultiPolygon",
21542 coordinates: [[[[135.49042, -9.2276], [141.01842, -9.35091], [141.01763, -6.90181], [140.90448, -6.85033], [140.85295, -6.72996], [140.99813, -6.3233], [141.02352, 0.08993], [129.63187, 2.21409], [129.71519, -0.24692], [128.40647, -2.30349], [130.8468, -2.61103], [132.8312, -4.70282], [135.35517, -5.01442], [135.49042, -9.2276]]]]
21548 nameEn: "Balearic Islands",
21549 aliases: ["ES-IB"],
21551 groups: ["EU", "039", "150", "UN"],
21552 callingCodes: ["34 971"]
21555 type: "MultiPolygon",
21556 coordinates: [[[[-2.27707, 35.35051], [5.10072, 39.89531], [3.75438, 42.33445], [-2.27707, 35.35051]]]]
21563 aliases: ["ES-CE"],
21565 groups: ["EA", "EU", "015", "002", "UN"],
21566 level: "subterritory",
21567 callingCodes: ["34"]
21570 type: "MultiPolygon",
21571 coordinates: [[[[-5.38491, 35.92591], [-5.37338, 35.88417], [-5.35844, 35.87375], [-5.34379, 35.8711], [-5.21179, 35.90091], [-5.38491, 35.92591]]]]
21578 aliases: ["ES-ML"],
21580 groups: ["EA", "EU", "015", "002", "UN"],
21581 level: "subterritory",
21582 callingCodes: ["34"]
21585 type: "MultiPolygon",
21586 coordinates: [[[[-2.91909, 35.33927], [-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.92272, 35.27509], [-2.91909, 35.33927]]]]
21594 groups: ["151", "150", "UN"],
21595 level: "subterritory",
21596 callingCodes: ["7"]
21599 type: "MultiPolygon",
21600 coordinates: [[[[33.5, 44], [36.4883, 45.0488], [36.475, 45.2411], [36.5049, 45.3136], [36.6545, 45.3417], [36.6645, 45.4514], [35.0498, 45.7683], [34.9601, 45.7563], [34.7991, 45.8101], [34.8015, 45.9005], [34.7548, 45.907], [34.6668, 45.9714], [34.6086, 45.9935], [34.5589, 45.9935], [34.5201, 45.951], [34.4873, 45.9427], [34.4415, 45.9599], [34.4122, 46.0025], [34.3391, 46.0611], [34.2511, 46.0532], [34.181, 46.068], [34.1293, 46.1049], [34.0731, 46.1177], [34.0527, 46.1084], [33.9155, 46.1594], [33.8523, 46.1986], [33.7972, 46.2048], [33.7405, 46.1855], [33.646, 46.2303], [33.6152, 46.2261], [33.6385, 46.1415], [33.6147, 46.1356], [33.5732, 46.1032], [33.5909, 46.0601], [33.5597, 46.0307], [31.5, 45.5], [33.5, 44]]]]
21605 wikidata: "Q12837",
21607 level: "sharedLandform"
21613 wikidata: "Q14056",
21614 nameEn: "Jan Mayen",
21615 aliases: ["NO-22"],
21617 groups: ["SJ", "154", "150", "UN"],
21618 level: "subterritory"
21621 type: "MultiPolygon",
21622 coordinates: [[[[-9.18243, 72.23144], [-10.71459, 70.09565], [-5.93364, 70.76368], [-9.18243, 72.23144]]]]
21627 wikidata: "Q19188",
21628 nameEn: "Mainland China",
21630 groups: ["030", "142", "UN"],
21631 callingCodes: ["86"]
21634 type: "MultiPolygon",
21635 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], [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.21879, 47.88505], [116.4465, 47.83662], [116.67405, 47.89039], [116.9723, 47.87285], [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.32827, 46.61433], [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], [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.60329, 45.44717], [114.94546, 45.37377], [114.74612, 45.43585], [114.54801, 45.38337], [114.5166, 45.27189], [113.70918, 44.72891], [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], [107.57258, 42.40898], [107.49681, 42.46221], [107.29755, 42.41395], [107.24774, 42.36107], [106.76517, 42.28741], [105.0123, 41.63188], [104.51667, 41.66113], [104.52258, 41.8706], [103.92804, 41.78246], [102.72403, 42.14675], [102.07645, 42.22519], [101.80515, 42.50074], [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], [78.89344, 31.30481], [79.01931, 31.42817], [79.14016, 31.43403], [79.30694, 31.17357], [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.5459, 30.37688], [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]]]]
21640 wikidata: "Q22890",
21642 level: "sharedLandform"
21648 wikidata: "Q23666",
21649 nameEn: "Great Britain",
21651 level: "sharedLandform"
21657 wikidata: "Q23681",
21658 nameEn: "Northern Cyprus",
21659 groups: ["Q644636", "145", "142"],
21661 callingCodes: ["90 392"]
21664 type: "MultiPolygon",
21665 coordinates: [[[[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], [32.46489, 35.48584], [32.60361, 35.16647], [32.64864, 35.19967], [32.70947, 35.18328], [32.70779, 35.14127], [32.85733, 35.07742], [32.86406, 35.1043], [32.94471, 35.09422], [33.01192, 35.15639], [33.08249, 35.17319], [33.11105, 35.15639], [33.15138, 35.19504], [33.27068, 35.16815], [33.3072, 35.16816], [33.31955, 35.18096], [33.35056, 35.18328], [33.34964, 35.17803], [33.35596, 35.17942], [33.35612, 35.17402], [33.36569, 35.17479], [33.3717, 35.1788], [33.37248, 35.18698], [33.38575, 35.2018], [33.4076, 35.20062], [33.41675, 35.16325], [33.46813, 35.10564], [33.48136, 35.0636], [33.47825, 35.04103], [33.45178, 35.02078], [33.45256, 35.00288], [33.47666, 35.00701], [33.48915, 35.06594], [33.53975, 35.08151], [33.57478, 35.06049], [33.567, 35.04803], [33.59658, 35.03635], [33.61215, 35.0527], [33.63765, 35.03869], [33.67678, 35.03866]]]]
21670 wikidata: "Q25231",
21671 nameEn: "Svalbard",
21672 aliases: ["NO-21"],
21674 groups: ["SJ", "154", "150", "UN"],
21675 level: "subterritory",
21676 callingCodes: ["47 79"]
21679 type: "MultiPolygon",
21680 coordinates: [[[[-7.49892, 77.24208], [32.07813, 72.01005], [36.85549, 84.09565], [-7.49892, 77.24208]]]]
21685 wikidata: "Q25263",
21687 aliases: ["PT-20"],
21689 groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21690 callingCodes: ["351"]
21693 type: "MultiPolygon",
21694 coordinates: [[[[-23.12984, 40.26428], [-36.43765, 41.39418], [-22.54767, 33.34416], [-23.12984, 40.26428]]]]
21699 wikidata: "Q25359",
21700 nameEn: "Navassa Island",
21701 aliases: ["UM-76"],
21703 groups: ["UM", "Q1352230", "029", "003", "419", "019", "UN"],
21704 level: "subterritory",
21705 roadSpeedUnit: "mph",
21706 roadHeightUnit: "ft"
21709 type: "MultiPolygon",
21710 coordinates: [[[[-74.7289, 18.71009], [-75.71816, 18.46438], [-74.76465, 18.06252], [-74.7289, 18.71009]]]]
21715 wikidata: "Q25396",
21717 aliases: ["BQ-BO", "NL-BQ1"],
21719 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21720 level: "subterritory",
21721 callingCodes: ["599 7"]
21724 type: "MultiPolygon",
21725 coordinates: [[[[-67.89186, 12.4116], [-68.90012, 12.62309], [-68.33524, 11.78151], [-68.01417, 11.77722], [-67.89186, 12.4116]]]]
21730 wikidata: "Q25528",
21732 aliases: ["BQ-SA", "NL-BQ2"],
21734 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21735 level: "subterritory",
21736 callingCodes: ["599 4"]
21739 type: "MultiPolygon",
21740 coordinates: [[[[-63.07669, 17.79659], [-63.81314, 17.95045], [-63.22932, 17.32592], [-63.07669, 17.79659]]]]
21745 wikidata: "Q26180",
21746 nameEn: "Sint Eustatius",
21747 aliases: ["BQ-SE", "NL-BQ3"],
21749 groups: ["Q1451600", "BQ", "029", "003", "419", "019", "UN"],
21750 level: "subterritory",
21751 callingCodes: ["599 3"]
21754 type: "MultiPolygon",
21755 coordinates: [[[[-63.07669, 17.79659], [-63.34999, 16.94218], [-62.76692, 17.64353], [-63.07669, 17.79659]]]]
21760 wikidata: "Q26253",
21762 aliases: ["PT-30"],
21764 groups: ["Q3320166", "Q2914565", "Q105472", "EU", "039", "150", "UN"],
21765 callingCodes: ["351"]
21768 type: "MultiPolygon",
21769 coordinates: [[[[-19.30302, 33.65304], [-16.04789, 29.65076], [-11.68307, 33.12333], [-19.30302, 33.65304]]]]
21774 wikidata: "Q26927",
21775 nameEn: "Lakshadweep",
21776 aliases: ["IN-LD"],
21778 groups: ["034", "142", "UN"],
21780 callingCodes: ["91"]
21783 type: "MultiPolygon",
21784 coordinates: [[[[67.64074, 11.57295], [76.59015, 5.591], [72.67494, 13.58102], [67.64074, 11.57295]]]]
21789 wikidata: "Q27329",
21790 nameEn: "Asian Russia",
21792 groups: ["142", "UN"],
21793 callingCodes: ["7"]
21796 type: "MultiPolygon",
21797 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]]], [[[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.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], [76.13964, 83.37843], [64.18965, 69.94255], [66.1708, 67.61252], [61.98014, 65.72191], [60.74386, 64.95767], [59.63945, 64.78384], [59.80579, 64.13948], [59.24834, 63.01859], [59.61398, 62.44915], [59.36223, 61.3882], [59.50685, 60.91162], [58.3853, 59.487], [59.15636, 59.14682], [59.40376, 58.45822], [58.71104, 58.07475], [58.81412, 57.71602], [58.13789, 57.68097], [58.07604, 57.08308], [57.28024, 56.87898], [57.51527, 56.08729], [59.28419, 56.15739], [59.49035, 55.60486], [58.81825, 55.03378], [57.25137, 55.26262], [57.14829, 54.84204], [57.95234, 54.39672], [59.95217, 54.85853], [59.70487, 54.14846], [58.94336, 53.953], [58.79644, 52.43392], [59.22409, 52.28437], [59.25033, 52.46803], [60.17516, 52.39457], [60.17253, 52.25814], [59.91279, 52.06924], [59.99809, 51.98263]]]]
21802 wikidata: "Q34366",
21803 nameEn: "Tasmania",
21804 aliases: ["AU-TAS"],
21806 groups: ["053", "009", "UN"],
21808 callingCodes: ["61"]
21811 type: "MultiPolygon",
21812 coordinates: [[[[123.64533, -39.13605], [159.69067, -56.28945], [159.74028, -39.1978], [123.64533, -39.13605]]]]
21817 wikidata: "Q34497",
21818 nameEn: "Saint Helena",
21819 aliases: ["SH-HL"],
21821 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
21822 level: "subterritory",
21824 roadSpeedUnit: "mph",
21825 roadHeightUnit: "ft",
21826 callingCodes: ["290"]
21829 type: "MultiPolygon",
21830 coordinates: [[[[-8.3824, -13.9131], [-6.17428, -19.07236], [-3.29308, -15.22647], [-8.3824, -13.9131]]]]
21835 wikidata: "Q35657",
21836 nameEn: "US States",
21838 level: "subcountryGroup"
21844 wikidata: "Q36117",
21846 level: "sharedLandform"
21852 wikidata: "Q36678",
21853 nameEn: "West Bank",
21855 groups: ["145", "142"],
21856 callingCodes: ["970"]
21859 type: "MultiPolygon",
21860 coordinates: [[[[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]]]]
21865 wikidata: "Q37362",
21866 nameEn: "Akrotiri and Dhekelia",
21874 wikidata: "Q38095",
21875 nameEn: "Gal\xE1pagos Islands",
21878 groups: ["005", "419", "019", "UN"],
21879 callingCodes: ["593"]
21882 type: "MultiPolygon",
21883 coordinates: [[[[-93.12365, 2.64343], [-92.46744, -2.52874], [-87.07749, -0.8849], [-93.12365, 2.64343]]]]
21888 wikidata: "Q39760",
21889 nameEn: "Gaza Strip",
21891 groups: ["145", "142"],
21892 callingCodes: ["970"]
21895 type: "MultiPolygon",
21896 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]]]]
21901 wikidata: "Q40888",
21902 nameEn: "Andaman and Nicobar Islands",
21903 aliases: ["IN-AN"],
21905 groups: ["034", "142", "UN"],
21907 callingCodes: ["91"]
21910 type: "MultiPolygon",
21911 coordinates: [[[[94.42132, 5.96581], [94.6371, 13.81803], [86.7822, 13.41052], [94.42132, 5.96581]]]]
21916 wikidata: "Q41684",
21917 nameEn: "Stewart Island",
21919 groups: ["053", "009", "UN"],
21921 callingCodes: ["64"]
21924 type: "MultiPolygon",
21925 coordinates: [[[[166.59185, -47.61313], [169.70504, -47.56021], [167.52103, -46.41337], [166.59185, -47.61313]]]]
21930 wikidata: "Q43296",
21931 nameEn: "Wake Island",
21932 aliases: ["WK", "WAK", "WKUM", "872", "UM-79"],
21934 groups: ["UM", "Q1352230", "057", "009", "UN"],
21935 level: "subterritory",
21936 roadSpeedUnit: "mph",
21937 roadHeightUnit: "ft",
21938 callingCodes: ["1"]
21941 type: "MultiPolygon",
21942 coordinates: [[[[167.34779, 18.97692], [166.67967, 20.14834], [165.82549, 18.97692], [167.34779, 18.97692]]]]
21947 wikidata: "Q46275",
21948 nameEn: "New Zealand Subantarctic Islands",
21950 groups: ["Q851132", "053", "009", "UN"],
21954 type: "MultiPolygon",
21955 coordinates: [[[[164.30551, -47.88072], [161.96603, -56.07661], [179.49541, -50.04657], [179.49541, -47.2902], [169.91032, -47.66283], [164.30551, -47.88072]]]]
21960 wikidata: "Q46395",
21961 nameEn: "British Overseas Territories",
21962 aliases: ["BOTS", "UKOTS"],
21964 level: "subcountryGroup"
21970 wikidata: "Q46772",
21971 nameEn: "Kerguelen Islands",
21973 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
21974 level: "subterritory"
21977 type: "MultiPolygon",
21978 coordinates: [[[[61.9216, -49.39746], [70.67507, -51.14192], [74.25129, -45.45074], [61.9216, -49.39746]]]]
21983 wikidata: "Q46879",
21984 nameEn: "Baker Island",
21985 aliases: ["UM-81"],
21987 groups: ["UM", "Q1352230", "061", "009", "UN"],
21988 level: "subterritory",
21989 roadSpeedUnit: "mph",
21990 roadHeightUnit: "ft"
21993 type: "MultiPolygon",
21994 coordinates: [[[[-175.33482, -1.40631], [-175.31323, 0.5442], [-177.91421, 0.39582], [-175.33482, -1.40631]]]]
21999 wikidata: "Q47863",
22000 nameEn: "Midway Atoll",
22001 aliases: ["MI", "MID", "MIUM", "488", "UM-71"],
22003 groups: ["UM", "Q1352230", "061", "009", "UN"],
22004 level: "subterritory",
22005 roadSpeedUnit: "mph",
22006 roadHeightUnit: "ft",
22007 callingCodes: ["1"]
22010 type: "MultiPolygon",
22011 coordinates: [[[[-176.29741, 29.09786], [-177.77531, 29.29793], [-177.5224, 27.7635], [-176.29741, 29.09786]]]]
22016 wikidata: "Q62218",
22017 nameEn: "Jarvis Island",
22018 aliases: ["UM-86"],
22020 groups: ["UM", "Q1352230", "061", "009", "UN"],
22021 level: "subterritory",
22022 roadSpeedUnit: "mph",
22023 roadHeightUnit: "ft"
22026 type: "MultiPolygon",
22027 coordinates: [[[[-160.42921, -1.4364], [-159.12443, 0.19975], [-160.38779, 0.30331], [-160.42921, -1.4364]]]]
22032 wikidata: "Q105472",
22033 nameEn: "Macaronesia",
22034 level: "sharedLandform"
22040 wikidata: "Q114935",
22041 nameEn: "Kermadec Islands",
22043 groups: ["Q851132", "053", "009", "UN"],
22045 callingCodes: ["64"]
22048 type: "MultiPolygon",
22049 coordinates: [[[[-174.40891, -29.09438], [-180, -24.21376], [-179.96512, -35.00791], [-174.40891, -29.09438]]]]
22054 wikidata: "Q115459",
22055 nameEn: "Chatham Islands",
22056 aliases: ["NZ-CIT"],
22058 groups: ["Q851132", "053", "009", "UN"],
22060 callingCodes: ["64"]
22063 type: "MultiPolygon",
22064 coordinates: [[[[-179.93224, -45.18423], [-172.47015, -45.17912], [-176.30998, -41.38382], [-179.93224, -45.18423]]]]
22069 wikidata: "Q118863",
22070 nameEn: "North Island",
22072 groups: ["053", "009", "UN"],
22074 callingCodes: ["64"]
22077 type: "MultiPolygon",
22078 coordinates: [[[[179.49541, -47.2902], [179.49541, -36.79303], [174.17679, -32.62487], [170.27492, -36.38133], [174.58663, -40.80446], [174.46634, -41.55028], [179.49541, -47.2902]]]]
22083 wikidata: "Q120755",
22084 nameEn: "South Island",
22086 groups: ["053", "009", "UN"],
22088 callingCodes: ["64"]
22091 type: "MultiPolygon",
22092 coordinates: [[[[169.70504, -47.56021], [179.49541, -47.2902], [174.46634, -41.55028], [174.58663, -40.80446], [170.27492, -36.38133], [166.56976, -39.94841], [164.8365, -46.0205], [167.52103, -46.41337], [169.70504, -47.56021]]]]
22097 wikidata: "Q123076",
22098 nameEn: "Palmyra Atoll",
22099 aliases: ["UM-95"],
22101 groups: ["UM", "Q1352230", "061", "009", "UN"],
22102 level: "subterritory",
22103 roadSpeedUnit: "mph",
22104 roadHeightUnit: "ft",
22105 callingCodes: ["1"]
22108 type: "MultiPolygon",
22109 coordinates: [[[[-161.06795, 5.2462], [-161.0731, 7.1291], [-163.24478, 5.24198], [-161.06795, 5.2462]]]]
22114 wikidata: "Q130574",
22115 nameEn: "Chafarinas Islands",
22117 groups: ["EU", "Q191011", "015", "002", "UN"],
22118 level: "subterritory"
22121 type: "MultiPolygon",
22122 coordinates: [[[[-2.40316, 35.16893], [-2.43262, 35.20652], [-2.45965, 35.16527], [-2.40316, 35.16893]]]]
22127 wikidata: "Q130895",
22128 nameEn: "Kingman Reef",
22129 aliases: ["UM-89"],
22131 groups: ["UM", "Q1352230", "061", "009", "UN"],
22132 level: "subterritory",
22133 roadSpeedUnit: "mph",
22134 roadHeightUnit: "ft"
22137 type: "MultiPolygon",
22138 coordinates: [[[[-161.0731, 7.1291], [-163.16627, 7.15036], [-163.24478, 5.24198], [-161.0731, 7.1291]]]]
22143 wikidata: "Q131008",
22144 nameEn: "Johnston Atoll",
22145 aliases: ["JT", "JTN", "JTUM", "396", "UM-67"],
22147 groups: ["UM", "Q1352230", "061", "009", "UN"],
22148 level: "subterritory",
22149 roadSpeedUnit: "mph",
22150 roadHeightUnit: "ft",
22151 callingCodes: ["1"]
22154 type: "MultiPolygon",
22155 coordinates: [[[[-170.65691, 16.57199], [-168.87689, 16.01159], [-169.2329, 17.4933], [-170.65691, 16.57199]]]]
22160 wikidata: "Q131305",
22161 nameEn: "Howland Island",
22162 aliases: ["UM-84"],
22164 groups: ["UM", "Q1352230", "061", "009", "UN"],
22165 level: "subterritory",
22166 roadSpeedUnit: "mph",
22167 roadHeightUnit: "ft"
22170 type: "MultiPolygon",
22171 coordinates: [[[[-177.91421, 0.39582], [-175.31323, 0.5442], [-176.74464, 2.28109], [-177.91421, 0.39582]]]]
22176 wikidata: "Q133888",
22177 nameEn: "Ashmore and Cartier Islands",
22179 groups: ["053", "009", "UN"],
22181 callingCodes: ["61"]
22184 type: "MultiPolygon",
22185 coordinates: [[[[123.7463, -11.1783], [120.6877, -13.59408], [125.29076, -12.33139], [123.7463, -11.1783]]]]
22190 wikidata: "Q153732",
22191 nameEn: "Mariana Islands",
22192 level: "sharedLandform"
22198 wikidata: "Q172216",
22199 nameEn: "Coral Sea Islands",
22201 groups: ["053", "009", "UN"],
22203 callingCodes: ["61"]
22206 type: "MultiPolygon",
22207 coordinates: [[[[159.77159, -28.41151], [156.73836, -14.50464], [145.2855, -9.62524], [147.69992, -17.5933], [152.93188, -20.92631], [154.02855, -24.43238], [159.77159, -28.41151]]]]
22212 wikidata: "Q179313",
22213 nameEn: "Alderney",
22215 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22216 level: "subterritory",
22218 roadSpeedUnit: "mph",
22219 roadHeightUnit: "ft",
22220 callingCodes: ["44 01481"]
22223 type: "MultiPolygon",
22224 coordinates: [[[[-2.36485, 49.48223], [-2.09454, 49.46288], [-2.02963, 49.91866], [-2.49556, 49.79012], [-2.36485, 49.48223]]]]
22229 wikidata: "Q185086",
22230 nameEn: "Crown Dependencies",
22232 level: "subcountryGroup"
22238 wikidata: "Q190571",
22239 nameEn: "Scattered Islands",
22241 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22242 level: "subterritory"
22245 type: "MultiPolygon",
22246 coordinates: [[[[53.53458, -16.36909], [54.96649, -16.28353], [54.61476, -15.02273], [53.53458, -16.36909]]], [[[38.55969, -20.75596], [40.68027, -23.38889], [43.52893, -15.62903], [38.55969, -20.75596]]], [[[47.03092, -11.05648], [47.11593, -12.08552], [47.96702, -11.46447], [47.03092, -11.05648]]]]
22251 wikidata: "Q191011",
22252 nameEn: "Plazas de soberan\xEDa",
22259 wikidata: "Q191146",
22260 nameEn: "Pe\xF1\xF3n de V\xE9lez de la Gomera",
22262 groups: ["EU", "Q191011", "015", "002", "UN"],
22263 level: "subterritory"
22266 type: "MultiPolygon",
22267 coordinates: [[[[-4.30191, 35.17419], [-4.30112, 35.17058], [-4.29436, 35.17149], [-4.30191, 35.17419]]]]
22272 wikidata: "Q201698",
22273 nameEn: "Crozet Islands",
22275 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22276 level: "subterritory"
22279 type: "MultiPolygon",
22280 coordinates: [[[[55.03425, -43.65017], [46.31615, -46.28749], [54.5587, -47.93013], [55.03425, -43.65017]]]]
22285 wikidata: "Q578170",
22286 nameEn: "Contiguous United States",
22287 aliases: ["CONUS"],
22289 groups: ["Q35657", "021", "003", "019", "UN"],
22290 roadSpeedUnit: "mph",
22291 roadHeightUnit: "ft",
22292 callingCodes: ["1"]
22295 type: "MultiPolygon",
22296 coordinates: [[[[-97.13927, 25.96583], [-96.92418, 25.97377], [-80.57035, 24.0565], [-78.91214, 27.76553], [-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], [-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], [-111.07523, 31.33232], [-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]]]]
22301 wikidata: "Q620634",
22302 nameEn: "Bir Tawil",
22303 groups: ["015", "002"],
22307 type: "MultiPolygon",
22308 coordinates: [[[[33.17563, 22.00405], [33.57251, 21.72406], [33.99686, 21.76784], [34.0765, 22.00501], [33.17563, 22.00405]]]]
22313 wikidata: "Q639185",
22314 nameEn: "Peros Banhos",
22316 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22317 level: "subterritory"
22320 type: "MultiPolygon",
22321 coordinates: [[[[72.12587, -4.02588], [70.1848, -6.37445], [72.09518, -5.61768], [72.12587, -4.02588]]]]
22326 wikidata: "Q644636",
22328 level: "sharedLandform"
22334 wikidata: "Q851132",
22335 nameEn: "New Zealand Outlying Islands",
22337 level: "subcountryGroup"
22343 wikidata: "Q875134",
22344 nameEn: "European Russia",
22346 groups: ["151", "150", "UN"],
22347 callingCodes: ["7"]
22350 type: "MultiPolygon",
22351 coordinates: [[[[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], [21.38446, 55.29348], [21.35465, 55.28427], [21.26425, 55.24456], [20.95181, 55.27994], [20.60454, 55.40986], [18.57853, 55.25302]]], [[[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], [35.04991, 45.76827], [36.6645, 45.4514], [36.6545, 45.3417], [36.5049, 45.3136], [36.475, 45.2411], [36.4883, 45.0488], [33.5943, 44.03313], [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], [47.00857, 49.04921], [47.04658, 49.19834], [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], [59.91279, 52.06924], [60.17253, 52.25814], [60.17516, 52.39457], [59.25033, 52.46803], [59.22409, 52.28437], [58.79644, 52.43392], [58.94336, 53.953], [59.70487, 54.14846], [59.95217, 54.85853], [57.95234, 54.39672], [57.14829, 54.84204], [57.25137, 55.26262], [58.81825, 55.03378], [59.49035, 55.60486], [59.28419, 56.15739], [57.51527, 56.08729], [57.28024, 56.87898], [58.07604, 57.08308], [58.13789, 57.68097], [58.81412, 57.71602], [58.71104, 58.07475], [59.40376, 58.45822], [59.15636, 59.14682], [58.3853, 59.487], [59.50685, 60.91162], [59.36223, 61.3882], [59.61398, 62.44915], [59.24834, 63.01859], [59.80579, 64.13948], [59.63945, 64.78384], [60.74386, 64.95767], [61.98014, 65.72191], [66.1708, 67.61252], [64.18965, 69.94255], [76.13964, 83.37843], [36.85549, 84.09565], [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]]]]
22356 wikidata: "Q1083368",
22357 nameEn: "Mainland Finland",
22359 groups: ["EU", "154", "150", "UN"],
22360 callingCodes: ["358"]
22363 type: "MultiPolygon",
22364 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]]]]
22369 wikidata: "Q1184963",
22370 nameEn: "Alhucemas Islands",
22372 groups: ["EU", "Q191011", "015", "002", "UN"],
22373 level: "subterritory"
22376 type: "MultiPolygon",
22377 coordinates: [[[[-3.90602, 35.21494], [-3.88372, 35.20767], [-3.89343, 35.22728], [-3.90602, 35.21494]]]]
22382 wikidata: "Q1298289",
22383 nameEn: "Egmont Islands",
22385 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22386 level: "subterritory"
22389 type: "MultiPolygon",
22390 coordinates: [[[[70.1848, -6.37445], [70.67958, -8.2663], [72.17991, -6.68509], [70.1848, -6.37445]]]]
22395 wikidata: "Q1352230",
22396 nameEn: "US Territories",
22398 level: "subcountryGroup"
22404 wikidata: "Q1451600",
22405 nameEn: "Overseas Countries and Territories of the EU",
22413 wikidata: "Q1544253",
22414 nameEn: "Great Chagos Bank",
22416 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22417 level: "subterritory"
22420 type: "MultiPolygon",
22421 coordinates: [[[[70.1848, -6.37445], [72.17991, -6.68509], [73.20573, -5.20727], [70.1848, -6.37445]]]]
22426 wikidata: "Q1585511",
22427 nameEn: "Salomon Atoll",
22429 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
22430 level: "subterritory"
22433 type: "MultiPolygon",
22434 coordinates: [[[[72.09518, -5.61768], [73.20573, -5.20727], [72.12587, -4.02588], [72.09518, -5.61768]]]]
22439 wikidata: "Q1681727",
22440 nameEn: "Saint-Paul and Amsterdam",
22442 groups: ["TF", "Q1451600", "014", "202", "002", "UN"],
22443 level: "subterritory"
22446 type: "MultiPolygon",
22447 coordinates: [[[[76.31747, -42.16264], [80.15867, -36.04977], [71.22311, -38.75287], [76.31747, -42.16264]]]]
22452 wikidata: "Q1901211",
22453 nameEn: "East Malaysia",
22455 groups: ["Q36117", "035", "142", "UN"],
22457 callingCodes: ["60"]
22460 type: "MultiPolygon",
22461 coordinates: [[[[110.90339, 7.52694], [109.82788, 2.86812], [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], [118.06469, 4.16638], [118.93936, 4.09009], [119.52945, 5.35672], [117.98544, 6.27477], [117.93857, 6.89845], [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.10166, 4.76112], [110.90339, 7.52694]]]]
22466 wikidata: "Q1973345",
22467 nameEn: "Peninsular Malaysia",
22469 groups: ["035", "142", "UN"],
22471 callingCodes: ["60"]
22474 type: "MultiPolygon",
22475 coordinates: [[[[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], [102.46318, 7.22462]]]]
22480 wikidata: "Q2093907",
22481 nameEn: "Three Kings Islands",
22483 groups: ["Q851132", "053", "009", "UN"],
22487 type: "MultiPolygon",
22488 coordinates: [[[[174.17679, -32.62487], [170.93268, -32.97889], [171.97383, -34.64644], [174.17679, -32.62487]]]]
22493 wikidata: "Q2298216",
22494 nameEn: "Solander Islands",
22496 groups: ["Q851132", "053", "009", "UN"],
22500 type: "MultiPolygon",
22501 coordinates: [[[[167.39068, -46.49187], [166.5534, -46.39484], [166.84561, -46.84889], [167.39068, -46.49187]]]]
22506 wikidata: "Q2872203",
22507 nameEn: "Mainland Australia",
22509 groups: ["053", "009", "UN"],
22510 level: "subcountryGroup",
22512 callingCodes: ["61"]
22515 type: "MultiPolygon",
22516 coordinates: [[[[88.16419, -23.49578], [123.64533, -39.13605], [159.74028, -39.1978], [159.76765, -29.76946], [154.02855, -24.43238], [152.93188, -20.92631], [147.69992, -17.5933], [145.2855, -9.62524], [143.87386, -9.02382], [143.29772, -9.33993], [142.48658, -9.36754], [142.19246, -9.15378], [141.88934, -9.36111], [141.01842, -9.35091], [135.49042, -9.2276], [127.55165, -9.05052], [125.29076, -12.33139], [88.16419, -23.49578]]]]
22521 wikidata: "Q2914565",
22522 nameEn: "Autonomous Regions of Portugal",
22524 level: "subcountryGroup"
22530 wikidata: "Q2915956",
22531 nameEn: "Mainland Portugal",
22533 groups: ["Q12837", "EU", "039", "150", "UN"],
22534 level: "subcountryGroup",
22535 callingCodes: ["351"]
22538 type: "MultiPolygon",
22539 coordinates: [[[[-10.39881, 36.12218], [-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], [-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], [-11.19304, 41.83075], [-10.39881, 36.12218]]]]
22544 wikidata: "Q3311985",
22545 nameEn: "Guernsey",
22547 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22548 level: "subterritory",
22550 roadSpeedUnit: "mph",
22551 roadHeightUnit: "ft",
22552 callingCodes: ["44 01481"]
22555 type: "MultiPolygon",
22556 coordinates: [[[[-2.49556, 49.79012], [-3.28154, 49.57329], [-2.65349, 49.15373], [-2.36485, 49.48223], [-2.49556, 49.79012]]]]
22561 wikidata: "Q3320166",
22562 nameEn: "Outermost Regions of the EU",
22570 wikidata: "Q3336843",
22571 nameEn: "Countries of the United Kingdom",
22573 level: "subcountryGroup"
22579 wikidata: "Q6736667",
22580 nameEn: "Mainland India",
22582 groups: ["034", "142", "UN"],
22584 callingCodes: ["91"]
22587 type: "MultiPolygon",
22588 coordinates: [[[[89.08044, 21.41871], [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.03471, 28.40054], [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.57179, 29.91422], [80.60226, 29.95732], [80.67076, 29.95732], [80.8778, 30.13384], [80.86673, 30.17321], [80.91143, 30.22173], [80.92547, 30.17193], [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.30694, 31.17357], [79.14016, 31.43403], [79.01931, 31.42817], [78.89344, 31.30481], [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], [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], [76.59015, 5.591], [79.50447, 8.91876], [79.42124, 9.80115], [80.48418, 10.20786], [89.08044, 21.41871]]]]
22593 wikidata: "Q9143535",
22594 nameEn: "Akrotiri",
22596 groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22597 level: "subterritory",
22599 callingCodes: ["357"]
22602 type: "MultiPolygon",
22603 coordinates: [[[[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]]]]
22608 wikidata: "Q9206745",
22609 nameEn: "Dhekelia",
22611 groups: ["Q644636", "Q37362", "BOTS", "145", "142", "UN"],
22612 level: "subterritory",
22614 callingCodes: ["357"]
22617 type: "MultiPolygon",
22618 coordinates: [[[[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]]]]
22623 wikidata: "Q16390686",
22624 nameEn: "Peninsular Spain",
22626 groups: ["Q12837", "EU", "039", "150", "UN"],
22627 callingCodes: ["34"]
22630 type: "MultiPolygon",
22631 coordinates: [[[[3.75438, 42.33445], [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], [-11.19304, 41.83075], [-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.2725, 35.73269], [-5.10878, 36.05227], [-2.27707, 35.35051], [3.75438, 42.33445]], [[-5.27801, 36.14942], [-5.34064, 36.03744], [-5.40526, 36.15488], [-5.34536, 36.15501], [-5.33822, 36.15272], [-5.27801, 36.14942]]], [[[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]]]]
22636 wikidata: "Q98059339",
22637 nameEn: "Mainland Norway",
22639 groups: ["154", "150", "UN"],
22640 callingCodes: ["47"]
22643 type: "MultiPolygon",
22644 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], [-11.60274, 67.73467], [7.28637, 57.35913], [10.40861, 58.38489]]]]
22649 wikidata: "Q98543636",
22650 nameEn: "Mainland Ecuador",
22652 groups: ["005", "419", "019", "UN"],
22653 callingCodes: ["593"]
22656 type: "MultiPolygon",
22657 coordinates: [[[[-84.52388, -3.36941], [-80.30602, -3.39149], [-80.20647, -3.431], [-80.24123, -3.46124], [-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], [-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], [-82.12561, 4.00341], [-84.52388, -3.36941]]]]
22665 aliases: ["Earth", "Planet"],
22683 nameEn: "North America",
22692 nameEn: "South America",
22693 level: "intermediateRegion"
22710 nameEn: "Western Africa",
22711 level: "intermediateRegion"
22718 wikidata: "Q27611",
22719 nameEn: "Central America",
22720 level: "intermediateRegion"
22727 wikidata: "Q27407",
22728 nameEn: "Eastern Africa",
22729 level: "intermediateRegion"
22736 wikidata: "Q27381",
22737 nameEn: "Northern Africa",
22745 wikidata: "Q27433",
22746 nameEn: "Middle Africa",
22747 level: "intermediateRegion"
22754 wikidata: "Q27394",
22755 nameEn: "Southern Africa",
22756 level: "intermediateRegion"
22764 nameEn: "Americas",
22772 wikidata: "Q2017699",
22773 nameEn: "Northern America",
22781 wikidata: "Q664609",
22782 nameEn: "Caribbean",
22783 level: "intermediateRegion"
22790 wikidata: "Q27231",
22791 nameEn: "Eastern Asia",
22799 wikidata: "Q771405",
22800 nameEn: "Southern Asia",
22808 wikidata: "Q11708",
22809 nameEn: "South-eastern Asia",
22817 wikidata: "Q27449",
22818 nameEn: "Southern Europe",
22826 wikidata: "Q45256",
22827 nameEn: "Australia and New Zealand",
22828 aliases: ["Australasia"],
22836 wikidata: "Q37394",
22837 nameEn: "Melanesia",
22845 wikidata: "Q3359409",
22846 nameEn: "Micronesia",
22854 wikidata: "Q35942",
22855 nameEn: "Polynesia",
22872 wikidata: "Q27275",
22873 nameEn: "Central Asia",
22881 wikidata: "Q27293",
22882 nameEn: "Western Asia",
22899 wikidata: "Q27468",
22900 nameEn: "Eastern Europe",
22908 wikidata: "Q27479",
22909 nameEn: "Northern Europe",
22917 wikidata: "Q27496",
22918 nameEn: "Western Europe",
22926 wikidata: "Q132959",
22927 nameEn: "Sub-Saharan Africa",
22935 wikidata: "Q72829598",
22936 nameEn: "Latin America and the Caribbean",
22944 wikidata: "Q3405693",
22947 groups: ["GG", "830", "Q185086", "154", "150", "UN"],
22948 level: "subterritory",
22950 roadSpeedUnit: "mph",
22951 roadHeightUnit: "ft",
22952 callingCodes: ["44 01481"]
22955 type: "MultiPolygon",
22956 coordinates: [[[[-2.36485, 49.48223], [-2.65349, 49.15373], [-2.09454, 49.46288], [-2.36485, 49.48223]]]]
22962 wikidata: "Q42314",
22963 nameEn: "Channel Islands",
22964 level: "intermediateRegion"
22972 wikidata: "Q46197",
22973 nameEn: "Ascension Island",
22974 aliases: ["SH-AC"],
22976 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
22977 isoStatus: "excRes",
22979 roadSpeedUnit: "mph",
22980 roadHeightUnit: "ft",
22981 callingCodes: ["247"]
22984 type: "MultiPolygon",
22985 coordinates: [[[[-14.82771, -8.70814], [-13.33271, -8.07391], [-14.91926, -6.63386], [-14.82771, -8.70814]]]]
22995 groups: ["Q12837", "039", "150", "UN"],
22996 callingCodes: ["376"]
22999 type: "MultiPolygon",
23000 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]]]]
23009 nameEn: "United Arab Emirates",
23010 groups: ["145", "142", "UN"],
23011 callingCodes: ["971"]
23014 type: "MultiPolygon",
23015 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]]]]
23024 nameEn: "Afghanistan",
23025 groups: ["034", "142", "UN"],
23026 callingCodes: ["93"]
23029 type: "MultiPolygon",
23030 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]]]]
23039 nameEn: "Antigua and Barbuda",
23040 groups: ["029", "003", "419", "019", "UN"],
23042 roadSpeedUnit: "mph",
23043 callingCodes: ["1 268"]
23046 type: "MultiPolygon",
23047 coordinates: [[[[-61.66959, 18.6782], [-62.58307, 16.68909], [-62.1023, 16.97277], [-61.23098, 16.62484], [-61.66959, 18.6782]]]]
23055 wikidata: "Q25228",
23056 nameEn: "Anguilla",
23058 groups: ["BOTS", "029", "003", "419", "019", "UN"],
23060 roadSpeedUnit: "mph",
23061 callingCodes: ["1 264"]
23064 type: "MultiPolygon",
23065 coordinates: [[[[-63.79029, 19.11219], [-63.35989, 18.06012], [-62.62718, 18.26185], [-63.79029, 19.11219]]]]
23075 groups: ["039", "150", "UN"],
23076 callingCodes: ["355"]
23079 type: "MultiPolygon",
23080 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]]]]
23090 groups: ["145", "142", "UN"],
23091 callingCodes: ["374"]
23094 type: "MultiPolygon",
23095 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]]]]
23105 groups: ["017", "202", "002", "UN"],
23106 callingCodes: ["244"]
23109 type: "MultiPolygon",
23110 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]]]]
23119 nameEn: "Antarctica",
23121 callingCodes: ["672"]
23124 type: "MultiPolygon",
23125 coordinates: [[[[180, -60], [-180, -60], [-180, -90], [180, -90], [180, -60]]]]
23134 nameEn: "Argentina",
23136 groups: ["005", "419", "019", "UN"],
23137 callingCodes: ["54"]
23140 type: "MultiPolygon",
23141 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]]]]
23149 wikidata: "Q16641",
23150 nameEn: "American Samoa",
23151 aliases: ["US-AS"],
23153 groups: ["Q1352230", "061", "009", "UN"],
23154 roadSpeedUnit: "mph",
23155 roadHeightUnit: "ft",
23156 callingCodes: ["1 684"]
23159 type: "MultiPolygon",
23160 coordinates: [[[[-171.39864, -10.21587], [-170.99605, -15.1275], [-166.32598, -15.26169], [-171.39864, -10.21587]]]]
23170 groups: ["EU", "155", "150", "UN"],
23171 callingCodes: ["43"]
23174 type: "MultiPolygon",
23175 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]]]]
23184 nameEn: "Australia"
23193 wikidata: "Q21203",
23195 aliases: ["NL-AW"],
23197 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23198 callingCodes: ["297"]
23201 type: "MultiPolygon",
23202 coordinates: [[[[-70.00823, 12.98375], [-70.35625, 12.58277], [-69.60231, 12.17], [-70.00823, 12.98375]]]]
23211 nameEn: "\xC5land Islands",
23213 groups: ["EU", "154", "150", "UN"],
23214 callingCodes: ["358 18", "358 457"]
23217 type: "MultiPolygon",
23218 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]]]]
23227 nameEn: "Azerbaijan",
23228 groups: ["145", "142", "UN"],
23229 callingCodes: ["994"]
23232 type: "MultiPolygon",
23233 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]]]]
23242 nameEn: "Bosnia and Herzegovina",
23243 groups: ["039", "150", "UN"],
23244 callingCodes: ["387"]
23247 type: "MultiPolygon",
23248 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]]]]
23257 nameEn: "Barbados",
23258 groups: ["029", "003", "419", "019", "UN"],
23260 callingCodes: ["1 246"]
23263 type: "MultiPolygon",
23264 coordinates: [[[[-58.56442, 13.24471], [-59.80731, 13.87556], [-59.82929, 12.70644], [-58.56442, 13.24471]]]]
23273 nameEn: "Bangladesh",
23274 groups: ["034", "142", "UN"],
23276 callingCodes: ["880"]
23279 type: "MultiPolygon",
23280 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.08044, 21.41871], [92.47409, 20.38654], [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]]]]
23290 groups: ["EU", "155", "150", "UN"],
23291 callingCodes: ["32"]
23294 type: "MultiPolygon",
23295 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]]]]
23304 nameEn: "Burkina Faso",
23305 groups: ["011", "202", "002", "UN"],
23306 callingCodes: ["226"]
23309 type: "MultiPolygon",
23310 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]]]]
23319 nameEn: "Bulgaria",
23320 groups: ["EU", "151", "150", "UN"],
23321 callingCodes: ["359"]
23324 type: "MultiPolygon",
23325 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]]]]
23335 groups: ["145", "142", "UN"],
23336 callingCodes: ["973"]
23339 type: "MultiPolygon",
23340 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]]]]
23350 groups: ["014", "202", "002", "UN"],
23351 callingCodes: ["257"]
23354 type: "MultiPolygon",
23355 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]]]]
23366 groups: ["011", "202", "002", "UN"],
23367 callingCodes: ["229"]
23370 type: "MultiPolygon",
23371 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]]]]
23379 wikidata: "Q25362",
23380 nameEn: "Saint-Barth\xE9lemy",
23382 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23383 callingCodes: ["590"]
23386 type: "MultiPolygon",
23387 coordinates: [[[[-62.62718, 18.26185], [-63.1055, 17.86651], [-62.34423, 17.49165], [-62.62718, 18.26185]]]]
23395 wikidata: "Q23635",
23398 groups: ["BOTS", "021", "003", "019", "UN"],
23400 callingCodes: ["1 441"]
23403 type: "MultiPolygon",
23404 coordinates: [[[[-63.20987, 32.6953], [-65.31453, 32.68437], [-65.63955, 31.43417], [-63.20987, 32.6953]]]]
23414 groups: ["Q36117", "035", "142", "UN"],
23416 callingCodes: ["673"]
23419 type: "MultiPolygon",
23420 coordinates: [[[[115.16236, 5.01011], [115.02521, 5.35005], [114.10166, 4.76112], [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]]]]
23430 groups: ["005", "419", "019", "UN"],
23431 callingCodes: ["591"]
23434 type: "MultiPolygon",
23435 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]]]]
23443 wikidata: "Q27561",
23444 nameEn: "Caribbean Netherlands",
23456 groups: ["005", "419", "019", "UN"],
23457 callingCodes: ["55"]
23460 type: "MultiPolygon",
23461 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]]]]
23470 nameEn: "The Bahamas",
23471 groups: ["029", "003", "419", "019", "UN"],
23473 roadSpeedUnit: "mph",
23474 callingCodes: ["1 242"]
23477 type: "MultiPolygon",
23478 coordinates: [[[[-72.98446, 20.4801], [-71.70065, 25.7637], [-78.91214, 27.76553], [-80.65727, 23.71953], [-72.98446, 20.4801]]]]
23488 groups: ["034", "142", "UN"],
23490 callingCodes: ["975"]
23493 type: "MultiPolygon",
23494 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]]]]
23502 wikidata: "Q23408",
23503 nameEn: "Bouvet Island",
23505 groups: ["005", "419", "019", "UN"]
23508 type: "MultiPolygon",
23509 coordinates: [[[[4.54042, -54.0949], [2.28941, -54.13089], [3.35353, -55.17558], [4.54042, -54.0949]]]]
23518 nameEn: "Botswana",
23519 groups: ["018", "202", "002", "UN"],
23521 callingCodes: ["267"]
23524 type: "MultiPolygon",
23525 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]]]]
23535 groups: ["151", "150", "UN"],
23536 callingCodes: ["375"]
23539 type: "MultiPolygon",
23540 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]]]]
23550 groups: ["013", "003", "419", "019", "UN"],
23551 roadSpeedUnit: "mph",
23552 callingCodes: ["501"]
23555 type: "MultiPolygon",
23556 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]]]]
23566 groups: ["021", "003", "019", "UN"],
23567 callingCodes: ["1"]
23570 type: "MultiPolygon",
23571 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], [-77.52957, 77.23408], [-68.21821, 80.48551], [-49.33696, 84.57952], [-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], [-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]]]]
23579 wikidata: "Q36004",
23580 nameEn: "Cocos (Keeling) Islands",
23582 groups: ["053", "009", "UN"],
23584 callingCodes: ["61"]
23587 type: "MultiPolygon",
23588 coordinates: [[[[96.61846, -10.82438], [96.02343, -12.68334], [97.93979, -12.33309], [96.61846, -10.82438]]]]
23597 nameEn: "Democratic Republic of the Congo",
23599 groups: ["017", "202", "002", "UN"],
23600 callingCodes: ["243"]
23603 type: "MultiPolygon",
23604 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]]]]
23613 nameEn: "Central African Republic",
23614 groups: ["017", "202", "002", "UN"],
23615 callingCodes: ["236"]
23618 type: "MultiPolygon",
23619 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]]]]
23628 nameEn: "Republic of the Congo",
23629 groups: ["017", "202", "002", "UN"],
23630 callingCodes: ["242"]
23633 type: "MultiPolygon",
23634 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]]]]
23643 nameEn: "Switzerland",
23644 groups: ["155", "150", "UN"],
23645 callingCodes: ["41"]
23648 type: "MultiPolygon",
23649 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]]]]
23658 nameEn: "C\xF4te d'Ivoire",
23659 groups: ["011", "202", "002", "UN"],
23660 callingCodes: ["225"]
23663 type: "MultiPolygon",
23664 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]]]]
23672 wikidata: "Q26988",
23673 nameEn: "Cook Islands",
23675 groups: ["061", "009", "UN"],
23677 callingCodes: ["682"]
23680 type: "MultiPolygon",
23681 coordinates: [[[[-168.15106, -10.26955], [-156.45576, -31.75456], [-156.48634, -15.52824], [-156.50903, -7.4975], [-168.15106, -10.26955]]]]
23691 groups: ["005", "419", "019", "UN"],
23692 callingCodes: ["56"]
23695 type: "MultiPolygon",
23696 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]]]]
23705 nameEn: "Cameroon",
23706 groups: ["017", "202", "002", "UN"],
23707 callingCodes: ["237"]
23710 type: "MultiPolygon",
23711 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]]]]
23720 nameEn: "People's Republic of China"
23730 nameEn: "Colombia",
23731 groups: ["005", "419", "019", "UN"],
23732 callingCodes: ["57"]
23735 type: "MultiPolygon",
23736 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]]]]
23743 wikidata: "Q161258",
23744 nameEn: "Clipperton Island",
23746 groups: ["013", "003", "019", "UN"],
23747 isoStatus: "excRes"
23750 type: "MultiPolygon",
23751 coordinates: [[[[-110.36279, 9.79626], [-108.755, 9.84085], [-109.04145, 11.13245], [-110.36279, 9.79626]]]]
23760 nameEn: "Costa Rica",
23761 groups: ["013", "003", "419", "019", "UN"],
23762 callingCodes: ["506"]
23765 type: "MultiPolygon",
23766 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]]]]
23776 groups: ["029", "003", "419", "019", "UN"],
23777 callingCodes: ["53"]
23780 type: "MultiPolygon",
23781 coordinates: [[[[-73.62304, 20.6935], [-82.02215, 24.23074], [-85.77883, 21.92705], [-74.81171, 18.82201], [-73.62304, 20.6935]]]]
23790 nameEn: "Cape Verde",
23791 groups: ["Q105472", "011", "202", "002", "UN"],
23792 callingCodes: ["238"]
23795 type: "MultiPolygon",
23796 coordinates: [[[[-28.81604, 14.57305], [-20.39702, 14.12816], [-23.37101, 19.134], [-28.81604, 14.57305]]]]
23804 wikidata: "Q25279",
23805 nameEn: "Cura\xE7ao",
23806 aliases: ["NL-CW"],
23808 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
23809 callingCodes: ["599"]
23812 type: "MultiPolygon",
23813 coordinates: [[[[-68.90012, 12.62309], [-69.59009, 12.46019], [-68.99639, 11.79035], [-68.33524, 11.78151], [-68.90012, 12.62309]]]]
23821 wikidata: "Q31063",
23822 nameEn: "Christmas Island",
23824 groups: ["053", "009", "UN"],
23826 callingCodes: ["61"]
23829 type: "MultiPolygon",
23830 coordinates: [[[[105.66835, -9.31927], [104.67494, -11.2566], [106.66176, -11.14349], [105.66835, -9.31927]]]]
23839 nameEn: "Republic of Cyprus",
23840 groups: ["Q644636", "EU", "145", "142", "UN"],
23842 callingCodes: ["357"]
23845 type: "MultiPolygon",
23846 coordinates: [[[[32.46489, 35.48584], [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.71514, 35.00294], [33.69731, 35.01754], [33.69938, 35.03123], [33.67678, 35.03866], [33.63765, 35.03869], [33.61215, 35.0527], [33.59658, 35.03635], [33.567, 35.04803], [33.57478, 35.06049], [33.53975, 35.08151], [33.48915, 35.06594], [33.47666, 35.00701], [33.45256, 35.00288], [33.45178, 35.02078], [33.47825, 35.04103], [33.48136, 35.0636], [33.46813, 35.10564], [33.41675, 35.16325], [33.4076, 35.20062], [33.38575, 35.2018], [33.37248, 35.18698], [33.3717, 35.1788], [33.36569, 35.17479], [33.35612, 35.17402], [33.35596, 35.17942], [33.34964, 35.17803], [33.35056, 35.18328], [33.31955, 35.18096], [33.3072, 35.16816], [33.27068, 35.16815], [33.15138, 35.19504], [33.11105, 35.15639], [33.08249, 35.17319], [33.01192, 35.15639], [32.94471, 35.09422], [32.86406, 35.1043], [32.85733, 35.07742], [32.70779, 35.14127], [32.70947, 35.18328], [32.64864, 35.19967], [32.60361, 35.16647], [32.46489, 35.48584]]], [[[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]]]]
23856 groups: ["EU", "151", "150", "UN"],
23857 callingCodes: ["420"]
23860 type: "MultiPolygon",
23861 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]]]]
23871 groups: ["EU", "155", "150", "UN"],
23872 callingCodes: ["49"]
23875 type: "MultiPolygon",
23876 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]]]]
23883 wikidata: "Q184851",
23884 nameEn: "Diego Garcia",
23886 groups: ["IO", "BOTS", "014", "202", "002", "UN"],
23887 isoStatus: "excRes",
23888 callingCodes: ["246"]
23891 type: "MultiPolygon",
23892 coordinates: [[[[73.14823, -7.76302], [73.09982, -6.07324], [71.43792, -7.73904], [73.14823, -7.76302]]]]
23901 nameEn: "Djibouti",
23902 groups: ["014", "202", "002", "UN"],
23903 callingCodes: ["253"]
23906 type: "MultiPolygon",
23907 coordinates: [[[[43.90659, 12.3823], [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.90659, 12.3823]]]]
23915 wikidata: "Q756617",
23916 nameEn: "Kingdom of Denmark"
23926 nameEn: "Dominica",
23927 groups: ["029", "003", "419", "019", "UN"],
23929 roadSpeedUnit: "mph",
23930 callingCodes: ["1 767"]
23933 type: "MultiPolygon",
23934 coordinates: [[[[-61.32485, 14.91445], [-60.86656, 15.82603], [-61.95646, 15.5094], [-61.32485, 14.91445]]]]
23943 nameEn: "Dominican Republic",
23944 groups: ["029", "003", "419", "019", "UN"],
23945 callingCodes: ["1 809", "1 829", "1 849"]
23948 type: "MultiPolygon",
23949 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]]]]
23959 groups: ["015", "002", "UN"],
23960 callingCodes: ["213"]
23963 type: "MultiPolygon",
23964 coordinates: [[[[8.59123, 37.14286], [5.10072, 39.89531], [-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]]]]
23970 wikidata: "Q28868874",
23971 nameEn: "Ceuta, Melilla",
23973 level: "territory",
23974 isoStatus: "excRes"
23996 groups: ["EU", "154", "150", "UN"],
23997 callingCodes: ["372"]
24000 type: "MultiPolygon",
24001 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]]]]
24011 groups: ["015", "002", "UN"],
24012 callingCodes: ["20"]
24015 type: "MultiPolygon",
24016 coordinates: [[[[33.62659, 31.82938], [26.92891, 33.39516], [24.8458, 31.39877], [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.4454, 27.91479], [34.8812, 29.36878], [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]]]]
24025 nameEn: "Western Sahara",
24026 groups: ["015", "002"],
24027 callingCodes: ["212"]
24030 type: "MultiPolygon",
24031 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]]]]
24041 groups: ["014", "202", "002", "UN"],
24042 callingCodes: ["291"]
24045 type: "MultiPolygon",
24046 coordinates: [[[[40.99158, 15.81743], [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], [40.99158, 15.81743]]]]
24065 nameEn: "Ethiopia",
24066 groups: ["014", "202", "002", "UN"],
24067 callingCodes: ["251"]
24070 type: "MultiPolygon",
24071 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]]]]
24079 nameEn: "European Union",
24081 isoStatus: "excRes"
24103 groups: ["054", "009", "UN"],
24105 callingCodes: ["679"]
24108 type: "MultiPolygon",
24109 coordinates: [[[[174.245, -23.1974], [179.99999, -22.5], [179.99999, -11.5], [174, -11.5], [174.245, -23.1974]]], [[[-176.76826, -14.95183], [-180, -14.96041], [-180, -22.90585], [-176.74538, -22.89767], [-176.76826, -14.95183]]]]
24118 nameEn: "Falkland Islands",
24120 groups: ["BOTS", "005", "419", "019", "UN"],
24122 roadSpeedUnit: "mph",
24123 roadHeightUnit: "ft",
24124 callingCodes: ["500"]
24127 type: "MultiPolygon",
24128 coordinates: [[[[-63.67376, -55.11859], [-54.56126, -51.26248], [-61.26735, -50.63919], [-63.67376, -55.11859]]]]
24137 nameEn: "Federated States of Micronesia",
24138 groups: ["057", "009", "UN"],
24139 roadSpeedUnit: "mph",
24140 roadHeightUnit: "ft",
24141 callingCodes: ["691"]
24144 type: "MultiPolygon",
24145 coordinates: [[[[138.20583, 13.3783], [136.27107, 6.73747], [156.88247, -1.39237], [165.19726, 6.22546], [138.20583, 13.3783]]]]
24154 nameEn: "Faroe Islands",
24156 groups: ["154", "150", "UN"],
24157 callingCodes: ["298"]
24160 type: "MultiPolygon",
24161 coordinates: [[[[-8.51774, 62.35338], [-6.51083, 60.95272], [-5.70102, 62.77194], [-8.51774, 62.35338]]]]
24179 wikidata: "Q212429",
24180 nameEn: "Metropolitan France",
24182 groups: ["EU", "155", "150", "UN"],
24183 isoStatus: "excRes",
24184 callingCodes: ["33"]
24187 type: "MultiPolygon",
24188 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.28985, 48.93406], [-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.75438, 42.33445], [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]], [[1.99838, 42.44682], [1.98378, 42.44697], [1.96125, 42.45364], [1.95606, 42.45785], [1.96215, 42.47854], [1.97003, 42.48081], [1.97227, 42.48487], [1.97697, 42.48568], [1.98022, 42.49569], [1.98916, 42.49351], [1.99766, 42.4858], [1.98579, 42.47486], [1.99216, 42.46208], [2.01564, 42.45171], [1.99838, 42.44682]]]]
24198 groups: ["017", "202", "002", "UN"],
24199 callingCodes: ["241"]
24202 type: "MultiPolygon",
24203 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.75065, 1.06753], [9.66433, 1.06723], [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]]]]
24213 nameEn: "United Kingdom",
24226 groups: ["029", "003", "419", "019", "UN"],
24228 roadSpeedUnit: "mph",
24229 callingCodes: ["1 473"]
24232 type: "MultiPolygon",
24233 coordinates: [[[[-62.64026, 12.69984], [-61.77886, 11.36496], [-59.94058, 12.34011], [-62.64026, 12.69984]]]]
24243 groups: ["145", "142", "UN"],
24244 callingCodes: ["995"]
24247 type: "MultiPolygon",
24248 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]]]]
24257 nameEn: "French Guiana",
24259 groups: ["Q3320166", "EU", "005", "419", "019", "UN"],
24260 callingCodes: ["594"]
24263 type: "MultiPolygon",
24264 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]]]]
24272 wikidata: "Q25230",
24273 nameEn: "Bailiwick of Guernsey",
24285 groups: ["011", "202", "002", "UN"],
24286 callingCodes: ["233"]
24289 type: "MultiPolygon",
24290 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], [-908e-5, 10.91644], [-63e-4, 10.96417], [0.03355, 10.9807], [0.02395, 11.06229], [342e-5, 11.08317], [-514e-5, 11.10763], [-0.0275, 11.11202], [-0.05733, 11.08628], [-0.14462, 11.10811], [-0.13493, 11.14075]]]]
24299 nameEn: "Gibraltar",
24301 groups: ["Q12837", "BOTS", "039", "150", "UN"],
24302 callingCodes: ["350"]
24305 type: "MultiPolygon",
24306 coordinates: [[[[-5.34064, 36.03744], [-5.27801, 36.14942], [-5.33822, 36.15272], [-5.34536, 36.15501], [-5.40526, 36.15488], [-5.34064, 36.03744]]]]
24315 nameEn: "Greenland",
24317 groups: ["Q1451600", "021", "003", "019", "UN"],
24318 callingCodes: ["299"]
24321 type: "MultiPolygon",
24322 coordinates: [[[[-49.33696, 84.57952], [-68.21821, 80.48551], [-77.52957, 77.23408], [-46.37635, 57.3249], [-9.68082, 72.73731], [-5.7106, 84.28058], [-49.33696, 84.57952]]]]
24331 nameEn: "The Gambia",
24332 groups: ["011", "202", "002", "UN"],
24333 callingCodes: ["220"]
24336 type: "MultiPolygon",
24337 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]]]]
24347 groups: ["011", "202", "002", "UN"],
24348 callingCodes: ["224"]
24351 type: "MultiPolygon",
24352 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]]]]
24360 wikidata: "Q17012",
24361 nameEn: "Guadeloupe",
24363 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
24364 callingCodes: ["590"]
24367 type: "MultiPolygon",
24368 coordinates: [[[[-60.03183, 16.1129], [-61.60296, 16.73066], [-63.00549, 15.26166], [-60.03183, 16.1129]]]]
24377 nameEn: "Equatorial Guinea",
24378 groups: ["017", "202", "002", "UN"],
24379 callingCodes: ["240"]
24382 type: "MultiPolygon",
24383 coordinates: [[[[9.22018, 3.72052], [8.34397, 4.30689], [7.71762, 0.6674], [3.35016, -3.29031], [9.66433, 1.06723], [9.75065, 1.06753], [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]]]]
24394 groups: ["EU", "039", "150", "UN"],
24395 callingCodes: ["30"]
24398 type: "MultiPolygon",
24399 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]]]]
24407 wikidata: "Q35086",
24408 nameEn: "South Georgia and South Sandwich Islands",
24410 groups: ["BOTS", "005", "419", "019", "UN"],
24412 roadSpeedUnit: "mph",
24413 roadHeightUnit: "ft",
24414 callingCodes: ["500"]
24417 type: "MultiPolygon",
24418 coordinates: [[[[-35.26394, -43.68272], [-53.39656, -59.87088], [-22.31757, -59.85974], [-35.26394, -43.68272]]]]
24427 nameEn: "Guatemala",
24428 groups: ["013", "003", "419", "019", "UN"],
24429 callingCodes: ["502"]
24432 type: "MultiPolygon",
24433 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]]]]
24441 wikidata: "Q16635",
24443 aliases: ["US-GU"],
24445 groups: ["Q1352230", "Q153732", "057", "009", "UN"],
24446 roadSpeedUnit: "mph",
24447 roadHeightUnit: "ft",
24448 callingCodes: ["1 671"]
24451 type: "MultiPolygon",
24452 coordinates: [[[[146.25931, 13.85876], [143.82485, 13.92273], [144.61642, 12.82462], [146.25931, 13.85876]]]]
24461 nameEn: "Guinea-Bissau",
24462 groups: ["011", "202", "002", "UN"],
24463 callingCodes: ["245"]
24466 type: "MultiPolygon",
24467 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]]]]
24477 groups: ["005", "419", "019", "UN"],
24479 callingCodes: ["592"]
24482 type: "MultiPolygon",
24483 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]]]]
24492 nameEn: "Hong Kong",
24494 groups: ["030", "142", "UN"],
24496 callingCodes: ["852"]
24499 type: "MultiPolygon",
24500 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]]]]
24508 wikidata: "Q131198",
24509 nameEn: "Heard Island and McDonald Islands",
24511 groups: ["053", "009", "UN"],
24515 type: "MultiPolygon",
24516 coordinates: [[[[71.08716, -53.87687], [75.44182, -53.99822], [72.87012, -51.48322], [71.08716, -53.87687]]]]
24525 nameEn: "Honduras",
24526 groups: ["013", "003", "419", "019", "UN"],
24527 callingCodes: ["504"]
24530 type: "MultiPolygon",
24531 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]]]]
24541 groups: ["EU", "039", "150", "UN"],
24542 callingCodes: ["385"]
24545 type: "MultiPolygon",
24546 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]]]]
24557 groups: ["029", "003", "419", "019", "UN"],
24558 callingCodes: ["509"]
24561 type: "MultiPolygon",
24562 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]]]]
24572 groups: ["EU", "151", "150", "UN"],
24573 callingCodes: ["36"]
24576 type: "MultiPolygon",
24577 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]]]]
24584 nameEn: "Canary Islands",
24586 groups: ["Q3320166", "Q105472", "EU", "039", "150", "UN"],
24587 isoStatus: "excRes",
24588 callingCodes: ["34"]
24591 type: "MultiPolygon",
24592 coordinates: [[[[-12.00985, 30.24121], [-25.3475, 27.87574], [-14.43883, 27.02969], [-12.00985, 30.24121]]]]
24601 nameEn: "Indonesia",
24612 nameEn: "Republic of Ireland",
24613 groups: ["EU", "Q22890", "154", "150", "UN"],
24615 callingCodes: ["353"]
24618 type: "MultiPolygon",
24619 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.34755, 55.49206], [-7.75229, 55.93854], [-22.01468, 48.19557], [-6.03913, 51.13217], [-5.37267, 53.63269], [-6.26218, 54.09785]]]]
24629 groups: ["145", "142", "UN"],
24630 callingCodes: ["972"]
24633 type: "MultiPolygon",
24634 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]]]]
24643 nameEn: "Isle of Man",
24645 groups: ["Q185086", "154", "150", "UN"],
24647 roadSpeedUnit: "mph",
24648 roadHeightUnit: "ft",
24649 callingCodes: ["44 01624", "44 07624", "44 07524", "44 07924"]
24652 type: "MultiPolygon",
24653 coordinates: [[[[-3.98763, 54.07351], [-4.1819, 54.57861], [-5.6384, 53.81157], [-3.98763, 54.07351]]]]
24671 wikidata: "Q43448",
24672 nameEn: "British Indian Ocean Territory",
24684 groups: ["145", "142", "UN"],
24685 callingCodes: ["964"]
24688 type: "MultiPolygon",
24689 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]]]]
24699 groups: ["034", "142", "UN"],
24700 callingCodes: ["98"]
24703 type: "MultiPolygon",
24704 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.46682, 24.57869], [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]]]]
24714 groups: ["154", "150", "UN"],
24715 callingCodes: ["354"]
24718 type: "MultiPolygon",
24719 coordinates: [[[[-33.15676, 62.62995], [-8.25539, 63.0423], [-15.70914, 69.67442], [-33.15676, 62.62995]]]]
24729 groups: ["EU", "039", "150", "UN"],
24730 callingCodes: ["39"]
24733 type: "MultiPolygon",
24734 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]]]]
24743 nameEn: "Bailiwick of Jersey",
24745 groups: ["830", "Q185086", "154", "150", "UN"],
24747 roadSpeedUnit: "mph",
24748 roadHeightUnit: "ft",
24749 callingCodes: ["44 01534"]
24752 type: "MultiPolygon",
24753 coordinates: [[[[-2.00491, 48.86706], [-1.83944, 49.23037], [-2.09454, 49.46288], [-2.65349, 49.15373], [-2.00491, 48.86706]]]]
24764 groups: ["029", "003", "419", "019", "UN"],
24766 callingCodes: ["1 876", "1 658"]
24769 type: "MultiPolygon",
24770 coordinates: [[[[-74.09729, 17.36817], [-78.9741, 19.59515], [-78.34606, 16.57862], [-74.09729, 17.36817]]]]
24780 groups: ["145", "142", "UN"],
24781 callingCodes: ["962"]
24784 type: "MultiPolygon",
24785 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.8812, 29.36878], [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]]]]
24795 groups: ["030", "142", "UN"],
24797 callingCodes: ["81"]
24800 type: "MultiPolygon",
24801 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]]]]
24811 groups: ["014", "202", "002", "UN"],
24813 callingCodes: ["254"]
24816 type: "MultiPolygon",
24817 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]]]]
24826 nameEn: "Kyrgyzstan",
24827 groups: ["143", "142", "UN"],
24828 callingCodes: ["996"]
24831 type: "MultiPolygon",
24832 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]]]]
24841 nameEn: "Cambodia",
24842 groups: ["035", "142", "UN"],
24843 callingCodes: ["855"]
24846 type: "MultiPolygon",
24847 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]]]]
24856 nameEn: "Kiribati",
24857 groups: ["057", "009", "UN"],
24859 callingCodes: ["686"]
24862 type: "MultiPolygon",
24863 coordinates: [[[[169, 3.9], [169, -3.5], [178, -3.5], [178, 3.9], [169, 3.9]]], [[[-161.06795, 5.2462], [-158.12991, -1.86122], [-175.33482, -1.40631], [-175.31804, -7.54825], [-156.50903, -7.4975], [-156.48634, -15.52824], [-135.59706, -4.70473], [-161.06795, 5.2462]]]]
24873 groups: ["014", "202", "002", "UN"],
24874 callingCodes: ["269"]
24877 type: "MultiPolygon",
24878 coordinates: [[[[42.63904, -10.02522], [43.28731, -13.97126], [45.4971, -11.75965], [42.63904, -10.02522]]]]
24887 nameEn: "St. Kitts and Nevis",
24888 groups: ["029", "003", "419", "019", "UN"],
24890 roadSpeedUnit: "mph",
24891 callingCodes: ["1 869"]
24894 type: "MultiPolygon",
24895 coordinates: [[[[-62.29333, 17.43155], [-62.76692, 17.64353], [-63.09677, 17.21372], [-62.63813, 16.65446], [-62.29333, 17.43155]]]]
24904 nameEn: "North Korea",
24905 groups: ["030", "142", "UN"],
24906 callingCodes: ["850"]
24909 type: "MultiPolygon",
24910 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]]]]
24919 nameEn: "South Korea",
24920 groups: ["030", "142", "UN"],
24921 callingCodes: ["82"]
24924 type: "MultiPolygon",
24925 coordinates: [[[[133.11729, 37.53115], [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.11729, 37.53115]]]]
24935 groups: ["145", "142", "UN"],
24936 callingCodes: ["965"]
24939 type: "MultiPolygon",
24940 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]]]]
24949 nameEn: "Cayman Islands",
24951 groups: ["BOTS", "029", "003", "419", "019", "UN"],
24953 roadSpeedUnit: "mph",
24954 roadHeightUnit: "ft",
24955 callingCodes: ["1 345"]
24958 type: "MultiPolygon",
24959 coordinates: [[[[-82.11509, 19.60401], [-80.36068, 18.11751], [-79.32727, 20.06742], [-82.11509, 19.60401]]]]
24968 nameEn: "Kazakhstan",
24969 groups: ["143", "142", "UN"],
24970 callingCodes: ["7"]
24973 type: "MultiPolygon",
24974 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], [47.04658, 49.19834], [47.00857, 49.04921], [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]]]]
24984 groups: ["035", "142", "UN"],
24985 callingCodes: ["856"]
24988 type: "MultiPolygon",
24989 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]]]]
25000 groups: ["145", "142", "UN"],
25001 callingCodes: ["961"]
25004 type: "MultiPolygon",
25005 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]]]]
25014 nameEn: "St. Lucia",
25016 groups: ["029", "003", "419", "019", "UN"],
25018 roadSpeedUnit: "mph",
25019 callingCodes: ["1 758"]
25022 type: "MultiPolygon",
25023 coordinates: [[[[-59.95997, 14.20285], [-61.69315, 14.26451], [-59.94058, 12.34011], [-59.95997, 14.20285]]]]
25032 nameEn: "Liechtenstein",
25034 groups: ["155", "150", "UN"],
25035 callingCodes: ["423"]
25038 type: "MultiPolygon",
25039 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]]]]
25048 nameEn: "Sri Lanka",
25049 groups: ["034", "142", "UN"],
25051 callingCodes: ["94"]
25054 type: "MultiPolygon",
25055 coordinates: [[[[76.59015, 5.591], [85.15017, 5.21497], [80.48418, 10.20786], [79.42124, 9.80115], [79.50447, 8.91876], [76.59015, 5.591]]]]
25065 groups: ["011", "202", "002", "UN"],
25066 callingCodes: ["231"]
25069 type: "MultiPolygon",
25070 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]]]]
25080 groups: ["018", "202", "002", "UN"],
25082 callingCodes: ["266"]
25085 type: "MultiPolygon",
25086 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]]]]
25095 nameEn: "Lithuania",
25096 groups: ["EU", "154", "150", "UN"],
25097 callingCodes: ["370"]
25100 type: "MultiPolygon",
25101 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]]]]
25110 nameEn: "Luxembourg",
25111 groups: ["EU", "155", "150", "UN"],
25112 callingCodes: ["352"]
25115 type: "MultiPolygon",
25116 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]]]]
25126 groups: ["EU", "154", "150", "UN"],
25127 callingCodes: ["371"]
25130 type: "MultiPolygon",
25131 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]]]]
25141 groups: ["015", "002", "UN"],
25142 callingCodes: ["218"]
25145 type: "MultiPolygon",
25146 coordinates: [[[[26.92891, 33.39516], [11.58941, 33.36891], [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.8458, 31.39877], [26.92891, 33.39516]]]]
25156 groups: ["015", "002", "UN"],
25157 callingCodes: ["212"]
25160 type: "MultiPolygon",
25161 coordinates: [[[[-2.27707, 35.35051], [-5.10878, 36.05227], [-7.2725, 35.73269], [-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.91909, 35.33927], [-2.92272, 35.27509], [-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.91909, 35.33927]], [[-3.90602, 35.21494], [-3.89343, 35.22728], [-3.88372, 35.20767], [-3.90602, 35.21494]], [[-4.30191, 35.17419], [-4.29436, 35.17149], [-4.30112, 35.17058], [-4.30191, 35.17419]], [[-2.40316, 35.16893], [-2.45965, 35.16527], [-2.43262, 35.20652], [-2.40316, 35.16893]], [[-5.38491, 35.92591], [-5.21179, 35.90091], [-5.34379, 35.8711], [-5.35844, 35.87375], [-5.37338, 35.88417], [-5.38491, 35.92591]]]]
25171 groups: ["155", "150", "UN"],
25172 callingCodes: ["377"]
25175 type: "MultiPolygon",
25176 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]]]]
25186 groups: ["151", "150", "UN"],
25187 callingCodes: ["373"]
25190 type: "MultiPolygon",
25191 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]]]]
25200 nameEn: "Montenegro",
25201 groups: ["039", "150", "UN"],
25202 callingCodes: ["382"]
25205 type: "MultiPolygon",
25206 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]]]]
25214 wikidata: "Q126125",
25215 nameEn: "Saint-Martin",
25217 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25218 callingCodes: ["590"]
25221 type: "MultiPolygon",
25222 coordinates: [[[[-62.93924, 18.02904], [-62.62718, 18.26185], [-63.35989, 18.06012], [-63.33064, 17.9615], [-63.13502, 18.05445], [-63.11042, 18.05339], [-63.09686, 18.04608], [-63.07759, 18.04943], [-63.0579, 18.06614], [-63.04039, 18.05619], [-63.02323, 18.05757], [-62.93924, 18.02904]]]]
25231 nameEn: "Madagascar",
25233 groups: ["014", "202", "002", "UN"],
25234 callingCodes: ["261"]
25237 type: "MultiPolygon",
25238 coordinates: [[[[51.93891, -10.85085], [45.84651, -12.77177], [42.14681, -19.63341], [45.80092, -33.00974], [51.93891, -10.85085]]]]
25247 nameEn: "Marshall Islands",
25248 groups: ["057", "009", "UN"],
25249 roadSpeedUnit: "mph",
25250 callingCodes: ["692"]
25253 type: "MultiPolygon",
25254 coordinates: [[[[169, 3.9], [173.53711, 5.70687], [169.29099, 15.77133], [159.04653, 10.59067], [169, 3.9]]]]
25263 nameEn: "North Macedonia",
25264 groups: ["039", "150", "UN"],
25265 callingCodes: ["389"]
25268 type: "MultiPolygon",
25269 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]]]]
25279 groups: ["011", "202", "002", "UN"],
25280 callingCodes: ["223"]
25283 type: "MultiPolygon",
25284 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]]]]
25294 aliases: ["Burma", "BU"],
25295 groups: ["035", "142", "UN"],
25296 callingCodes: ["95"]
25299 type: "MultiPolygon",
25300 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.47409, 20.38654], [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]]]]
25309 nameEn: "Mongolia",
25310 groups: ["030", "142", "UN"],
25311 callingCodes: ["976"]
25314 type: "MultiPolygon",
25315 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.80515, 42.50074], [102.07645, 42.22519], [102.72403, 42.14675], [103.92804, 41.78246], [104.52258, 41.8706], [104.51667, 41.66113], [105.0123, 41.63188], [106.76517, 42.28741], [107.24774, 42.36107], [107.29755, 42.41395], [107.49681, 42.46221], [107.57258, 42.40898], [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.70918, 44.72891], [114.5166, 45.27189], [114.54801, 45.38337], [114.74612, 45.43585], [114.94546, 45.37377], [115.60329, 45.44717], [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.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.32827, 46.61433], [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], [116.9723, 47.87285], [116.67405, 47.89039], [116.4465, 47.83662], [116.21879, 47.88505], [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]]]]
25323 wikidata: "Q14773",
25325 aliases: ["Macao"],
25327 groups: ["030", "142", "UN"],
25329 callingCodes: ["853"]
25332 type: "MultiPolygon",
25333 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]]]]
25341 wikidata: "Q16644",
25342 nameEn: "Northern Mariana Islands",
25343 aliases: ["US-MP"],
25345 groups: ["Q1352230", "Q153732", "057", "009", "UN"],
25346 roadSpeedUnit: "mph",
25347 callingCodes: ["1 670"]
25350 type: "MultiPolygon",
25351 coordinates: [[[[135.52896, 14.32623], [152.19114, 13.63487], [145.05972, 21.28731], [135.52896, 14.32623]]]]
25359 wikidata: "Q17054",
25360 nameEn: "Martinique",
25362 groups: ["Q3320166", "EU", "029", "003", "419", "019", "UN"],
25363 callingCodes: ["596"]
25366 type: "MultiPolygon",
25367 coordinates: [[[[-59.95997, 14.20285], [-61.07821, 15.25109], [-61.69315, 14.26451], [-59.95997, 14.20285]]]]
25376 nameEn: "Mauritania",
25377 groups: ["011", "202", "002", "UN"],
25378 callingCodes: ["222"]
25381 type: "MultiPolygon",
25382 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]]]]
25390 wikidata: "Q13353",
25391 nameEn: "Montserrat",
25393 groups: ["BOTS", "029", "003", "419", "019", "UN"],
25395 roadSpeedUnit: "mph",
25396 roadHeightUnit: "ft",
25397 callingCodes: ["1 664"]
25400 type: "MultiPolygon",
25401 coordinates: [[[[-61.91508, 16.51165], [-62.1023, 16.97277], [-62.58307, 16.68909], [-61.91508, 16.51165]]]]
25411 groups: ["EU", "039", "150", "UN"],
25413 callingCodes: ["356"]
25416 type: "MultiPolygon",
25417 coordinates: [[[[15.70991, 35.79901], [14.07544, 36.41525], [13.27636, 35.20764], [15.70991, 35.79901]]]]
25426 nameEn: "Mauritius",
25427 groups: ["014", "202", "002", "UN"],
25429 callingCodes: ["230"]
25432 type: "MultiPolygon",
25433 coordinates: [[[[56.09755, -9.55401], [57.50644, -31.92637], [68.4673, -19.15185], [56.09755, -9.55401]]]]
25442 nameEn: "Maldives",
25443 groups: ["034", "142", "UN"],
25445 callingCodes: ["960"]
25448 type: "MultiPolygon",
25449 coordinates: [[[[71.9161, 8.55531], [72.57428, -3.7623], [76.59015, 5.591], [71.9161, 8.55531]]]]
25459 groups: ["014", "202", "002", "UN"],
25461 callingCodes: ["265"]
25464 type: "MultiPolygon",
25465 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]]]]
25475 groups: ["013", "003", "419", "019", "UN"],
25476 callingCodes: ["52"]
25479 type: "MultiPolygon",
25480 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], [-111.07523, 31.33232], [-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]]]]
25499 nameEn: "Mozambique",
25500 groups: ["014", "202", "002", "UN"],
25502 callingCodes: ["258"]
25505 type: "MultiPolygon",
25506 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]]]]
25516 groups: ["018", "202", "002", "UN"],
25518 callingCodes: ["264"]
25521 type: "MultiPolygon",
25522 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]]]]
25530 wikidata: "Q33788",
25531 nameEn: "New Caledonia",
25533 groups: ["Q1451600", "054", "009", "UN"],
25534 callingCodes: ["687"]
25537 type: "MultiPolygon",
25538 coordinates: [[[[159.77159, -28.41151], [174.245, -23.1974], [156.73836, -14.50464], [159.77159, -28.41151]]]]
25549 groups: ["011", "202", "002", "UN"],
25550 callingCodes: ["227"]
25553 type: "MultiPolygon",
25554 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]]]]
25562 wikidata: "Q31057",
25563 nameEn: "Norfolk Island",
25565 groups: ["053", "009", "UN"],
25567 callingCodes: ["672 3"]
25570 type: "MultiPolygon",
25571 coordinates: [[[[169.82316, -28.16667], [166.29505, -28.29175], [167.94076, -30.60745], [169.82316, -28.16667]]]]
25581 groups: ["011", "202", "002", "UN"],
25582 callingCodes: ["234"]
25585 type: "MultiPolygon",
25586 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]]]]
25595 nameEn: "Nicaragua",
25596 groups: ["013", "003", "419", "019", "UN"],
25597 callingCodes: ["505"]
25600 type: "MultiPolygon",
25601 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]]]]
25609 wikidata: "Q29999",
25610 nameEn: "Kingdom of the Netherlands"
25631 groups: ["034", "142", "UN"],
25633 callingCodes: ["977"]
25636 type: "MultiPolygon",
25637 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.5459, 30.37688], [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.92547, 30.17193], [80.91143, 30.22173], [80.86673, 30.17321], [80.8778, 30.13384], [80.67076, 29.95732], [80.60226, 29.95732], [80.57179, 29.91422], [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], [81.03471, 28.40054], [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]]]]
25647 groups: ["057", "009", "UN"],
25649 callingCodes: ["674"]
25652 type: "MultiPolygon",
25653 coordinates: [[[[166.95155, 0.14829], [166.21778, -0.7977], [167.60042, -0.88259], [166.95155, 0.14829]]]]
25661 wikidata: "Q34020",
25664 groups: ["061", "009", "UN"],
25666 callingCodes: ["683"]
25669 type: "MultiPolygon",
25670 coordinates: [[[[-170.83899, -18.53439], [-170.82274, -20.44429], [-168.63096, -18.60489], [-170.83899, -18.53439]]]]
25679 nameEn: "New Zealand"
25690 groups: ["145", "142", "UN"],
25691 callingCodes: ["968"]
25694 type: "MultiPolygon",
25695 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], [57.49095, 8.14549], [61.45114, 22.55394]]]]
25705 groups: ["013", "003", "419", "019", "UN"],
25706 callingCodes: ["507"]
25709 type: "MultiPolygon",
25710 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]]]]
25720 groups: ["005", "419", "019", "UN"],
25721 callingCodes: ["51"]
25724 type: "MultiPolygon",
25725 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.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]]]]
25733 wikidata: "Q30971",
25734 nameEn: "French Polynesia",
25736 groups: ["Q1451600", "061", "009", "UN"],
25737 callingCodes: ["689"]
25740 type: "MultiPolygon",
25741 coordinates: [[[[-135.59706, -4.70473], [-156.48634, -15.52824], [-156.45576, -31.75456], [-133.59543, -28.4709], [-135.59706, -4.70473]]]]
25750 nameEn: "Papua New Guinea",
25751 groups: ["054", "009", "UN"],
25753 callingCodes: ["675"]
25756 type: "MultiPolygon",
25757 coordinates: [[[[141.03157, 2.12829], [140.99813, -6.3233], [140.85295, -6.72996], [140.90448, -6.85033], [141.01763, -6.90181], [141.01842, -9.35091], [141.88934, -9.36111], [142.19246, -9.15378], [142.48658, -9.36754], [143.29772, -9.33993], [143.87386, -9.02382], [145.2855, -9.62524], [156.73836, -14.50464], [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]]]]
25766 nameEn: "Philippines",
25767 aliases: ["PI", "RP"],
25768 groups: ["035", "142", "UN"],
25769 callingCodes: ["63"]
25772 type: "MultiPolygon",
25773 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.93857, 6.89845], [117.98544, 6.27477], [119.52945, 5.35672], [118.93936, 4.09009], [118.06469, 4.16638], [121.14448, 2.12444], [129.19694, 7.84182]]]]
25782 nameEn: "Pakistan",
25783 groups: ["034", "142", "UN"],
25785 callingCodes: ["92"]
25788 type: "MultiPolygon",
25789 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.46682, 24.57869], [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]]]]
25799 groups: ["EU", "151", "150", "UN"],
25800 callingCodes: ["48"]
25803 type: "MultiPolygon",
25804 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]]]]
25812 wikidata: "Q34617",
25813 nameEn: "Saint Pierre and Miquelon",
25815 groups: ["Q1451600", "021", "003", "019", "UN"],
25816 callingCodes: ["508"]
25819 type: "MultiPolygon",
25820 coordinates: [[[[-56.72993, 46.65575], [-55.90758, 46.6223], [-56.27503, 47.39728], [-56.72993, 46.65575]]]]
25828 wikidata: "Q35672",
25829 nameEn: "Pitcairn Islands",
25831 groups: ["BOTS", "061", "009", "UN"],
25833 callingCodes: ["64"]
25836 type: "MultiPolygon",
25837 coordinates: [[[[-133.59543, -28.4709], [-122.0366, -24.55017], [-133.61511, -21.93325], [-133.59543, -28.4709]]]]
25846 nameEn: "Puerto Rico",
25847 aliases: ["US-PR"],
25849 groups: ["Q1352230", "029", "003", "419", "019", "UN"],
25850 roadSpeedUnit: "mph",
25851 callingCodes: ["1 787", "1 939"]
25854 type: "MultiPolygon",
25855 coordinates: [[[[-65.27974, 17.56928], [-65.02435, 18.73231], [-67.99519, 18.97186], [-68.23894, 17.84663], [-65.27974, 17.56928]]]]
25863 wikidata: "Q219060",
25864 nameEn: "Palestine"
25885 groups: ["057", "009", "UN"],
25886 roadSpeedUnit: "mph",
25887 callingCodes: ["680"]
25890 type: "MultiPolygon",
25891 coordinates: [[[[128.97621, 3.08804], [136.39296, 1.54187], [136.04605, 12.45908], [128.97621, 3.08804]]]]
25900 nameEn: "Paraguay",
25901 groups: ["005", "419", "019", "UN"],
25902 callingCodes: ["595"]
25905 type: "MultiPolygon",
25906 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]]]]
25916 groups: ["145", "142", "UN"],
25917 callingCodes: ["974"]
25920 type: "MultiPolygon",
25921 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]]]]
25929 wikidata: "Q17070",
25930 nameEn: "R\xE9union",
25932 groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
25933 callingCodes: ["262"]
25936 type: "MultiPolygon",
25937 coordinates: [[[[53.37984, -21.23941], [56.73473, -21.9174], [56.62373, -20.2711], [53.37984, -21.23941]]]]
25947 groups: ["EU", "151", "150", "UN"],
25948 callingCodes: ["40"]
25951 type: "MultiPolygon",
25952 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]]]]
25962 groups: ["039", "150", "UN"],
25963 callingCodes: ["381"]
25966 type: "MultiPolygon",
25967 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]]]]
25987 groups: ["014", "202", "002", "UN"],
25988 callingCodes: ["250"]
25991 type: "MultiPolygon",
25992 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]]]]
26001 nameEn: "Saudi Arabia",
26002 groups: ["145", "142", "UN"],
26003 callingCodes: ["966"]
26006 type: "MultiPolygon",
26007 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.8812, 29.36878], [34.4454, 27.91479], [37.8565, 22.00903], [39.63762, 18.37348], [40.99158, 15.81743], [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]]]]
26016 nameEn: "Solomon Islands",
26017 groups: ["054", "009", "UN"],
26019 callingCodes: ["677"]
26022 type: "MultiPolygon",
26023 coordinates: [[[[172.71443, -12.01327], [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], [156.73836, -14.50464], [172.71443, -12.01327]]]]
26032 nameEn: "Seychelles",
26033 groups: ["014", "202", "002", "UN"],
26035 callingCodes: ["248"]
26038 type: "MultiPolygon",
26039 coordinates: [[[[43.75112, -10.38913], [54.83239, -10.93575], [66.3222, 5.65313], [43.75112, -10.38913]]]]
26049 groups: ["015", "002", "UN"],
26050 callingCodes: ["249"]
26053 type: "MultiPolygon",
26054 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]]]]
26064 groups: ["EU", "154", "150", "UN"],
26065 callingCodes: ["46"]
26068 type: "MultiPolygon",
26069 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]]]]
26078 nameEn: "Singapore",
26079 groups: ["035", "142", "UN"],
26081 callingCodes: ["65"]
26084 type: "MultiPolygon",
26085 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]]]]
26093 wikidata: "Q192184",
26094 nameEn: "Saint Helena, Ascension and Tristan da Cunha",
26105 nameEn: "Slovenia",
26106 groups: ["EU", "039", "150", "UN"],
26107 callingCodes: ["386"]
26110 type: "MultiPolygon",
26111 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]]]]
26119 wikidata: "Q842829",
26120 nameEn: "Svalbard and Jan Mayen",
26131 nameEn: "Slovakia",
26132 groups: ["EU", "151", "150", "UN"],
26133 callingCodes: ["421"]
26136 type: "MultiPolygon",
26137 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]]]]
26146 nameEn: "Sierra Leone",
26147 groups: ["011", "202", "002", "UN"],
26148 callingCodes: ["232"]
26151 type: "MultiPolygon",
26152 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]]]]
26161 nameEn: "San Marino",
26162 groups: ["039", "150", "UN"],
26163 callingCodes: ["378"]
26166 type: "MultiPolygon",
26167 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]]]]
26177 groups: ["011", "202", "002", "UN"],
26178 callingCodes: ["221"]
26181 type: "MultiPolygon",
26182 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]]]]
26192 groups: ["014", "202", "002", "UN"],
26193 callingCodes: ["252"]
26196 type: "MultiPolygon",
26197 coordinates: [[[[51.12877, 12.56479], [43.90659, 12.3823], [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], [57.49095, 8.14549], [51.12877, 12.56479]]]]
26206 nameEn: "Suriname",
26207 groups: ["005", "419", "019", "UN"],
26209 callingCodes: ["597"]
26212 type: "MultiPolygon",
26213 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]]]]
26222 nameEn: "South Sudan",
26223 groups: ["014", "202", "002", "UN"],
26224 callingCodes: ["211"]
26227 type: "MultiPolygon",
26228 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]]]]
26237 nameEn: "S\xE3o Tom\xE9 and Principe",
26238 groups: ["017", "202", "002", "UN"],
26239 callingCodes: ["239"]
26242 type: "MultiPolygon",
26243 coordinates: [[[[4.34149, 1.91417], [6.6507, -0.28606], [7.9035, 1.92304], [4.34149, 1.91417]]]]
26252 nameEn: "El Salvador",
26253 groups: ["013", "003", "419", "019", "UN"],
26254 callingCodes: ["503"]
26257 type: "MultiPolygon",
26258 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]]]]
26266 wikidata: "Q26273",
26267 nameEn: "Sint Maarten",
26268 aliases: ["NL-SX"],
26270 groups: ["Q1451600", "029", "003", "419", "019", "UN"],
26271 callingCodes: ["1 721"]
26274 type: "MultiPolygon",
26275 coordinates: [[[[-63.33064, 17.9615], [-63.1055, 17.86651], [-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.11042, 18.05339], [-63.13502, 18.05445], [-63.33064, 17.9615]]]]
26285 groups: ["145", "142", "UN"],
26286 callingCodes: ["963"]
26289 type: "MultiPolygon",
26290 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]]]]
26299 nameEn: "Eswatini",
26300 aliases: ["Swaziland"],
26301 groups: ["018", "202", "002", "UN"],
26303 callingCodes: ["268"]
26306 type: "MultiPolygon",
26307 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]]]]
26314 wikidata: "Q220982",
26315 nameEn: "Tristan da Cunha",
26316 aliases: ["SH-TA"],
26318 groups: ["SH", "BOTS", "011", "202", "002", "UN"],
26319 isoStatus: "excRes",
26321 roadSpeedUnit: "mph",
26322 roadHeightUnit: "ft",
26323 callingCodes: ["290 8", "44 20"]
26326 type: "MultiPolygon",
26327 coordinates: [[[[-13.38232, -34.07258], [-16.67337, -41.9188], [-5.88482, -41.4829], [-13.38232, -34.07258]]]]
26335 wikidata: "Q18221",
26336 nameEn: "Turks and Caicos Islands",
26338 groups: ["BOTS", "029", "003", "419", "019", "UN"],
26340 roadSpeedUnit: "mph",
26341 roadHeightUnit: "ft",
26342 callingCodes: ["1 649"]
26345 type: "MultiPolygon",
26346 coordinates: [[[[-71.70065, 25.7637], [-72.98446, 20.4801], [-69.80718, 21.35956], [-71.70065, 25.7637]]]]
26356 groups: ["017", "202", "002", "UN"],
26357 callingCodes: ["235"]
26360 type: "MultiPolygon",
26361 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]]]]
26369 wikidata: "Q129003",
26370 nameEn: "French Southern Territories",
26382 groups: ["011", "202", "002", "UN"],
26383 callingCodes: ["228"]
26386 type: "MultiPolygon",
26387 coordinates: [[[[0.50388, 11.01011], [-0.13493, 11.14075], [-0.14462, 11.10811], [-0.05733, 11.08628], [-0.0275, 11.11202], [-514e-5, 11.10763], [342e-5, 11.08317], [0.02395, 11.06229], [0.03355, 10.9807], [-63e-4, 10.96417], [-908e-5, 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]]]]
26396 nameEn: "Thailand",
26397 groups: ["035", "142", "UN"],
26399 callingCodes: ["66"]
26402 type: "MultiPolygon",
26403 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]]]]
26412 nameEn: "Tajikistan",
26413 groups: ["143", "142", "UN"],
26414 callingCodes: ["992"]
26417 type: "MultiPolygon",
26418 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]]]]
26426 wikidata: "Q36823",
26429 groups: ["061", "009", "UN"],
26431 callingCodes: ["690"]
26434 type: "MultiPolygon",
26435 coordinates: [[[[-168.251, -9.44289], [-174.18635, -7.80441], [-174.17993, -10.13616], [-168.251, -9.44289]]]]
26444 nameEn: "East Timor",
26445 aliases: ["Timor-Leste", "TP"],
26446 groups: ["035", "142", "UN"],
26448 callingCodes: ["670"]
26451 type: "MultiPolygon",
26452 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.58506, -7.95311], [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]]]]
26461 nameEn: "Turkmenistan",
26462 groups: ["143", "142", "UN"],
26463 callingCodes: ["993"]
26466 type: "MultiPolygon",
26467 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]]]]
26477 groups: ["015", "002", "UN"],
26478 callingCodes: ["216"]
26481 type: "MultiPolygon",
26482 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.58941, 33.36891], [11.2718, 37.6713]]]]
26492 groups: ["061", "009", "UN"],
26494 callingCodes: ["676"]
26497 type: "MultiPolygon",
26498 coordinates: [[[[-176.74538, -22.89767], [-180, -22.90585], [-180, -24.21376], [-173.10761, -24.19665], [-173.13438, -14.94228], [-176.76826, -14.95183], [-176.74538, -22.89767]]]]
26508 groups: ["145", "142", "UN"],
26509 callingCodes: ["90"]
26512 type: "MultiPolygon",
26513 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]]]]
26522 nameEn: "Trinidad and Tobago",
26523 groups: ["029", "003", "419", "019", "UN"],
26525 callingCodes: ["1 868"]
26528 type: "MultiPolygon",
26529 coordinates: [[[[-61.62505, 11.18974], [-62.08693, 10.04435], [-60.89962, 9.81445], [-60.07172, 11.77667], [-61.62505, 11.18974]]]]
26539 groups: ["061", "009", "UN"],
26541 callingCodes: ["688"]
26544 type: "MultiPolygon",
26545 coordinates: [[[[174, -5], [174, -11.5], [179.99999, -11.5], [179.99999, -5], [174, -5]]]]
26556 groups: ["030", "142"],
26557 callingCodes: ["886"]
26560 type: "MultiPolygon",
26561 coordinates: [[[[121.8109, 21.77688], [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], [121.8109, 21.77688]]]]
26570 nameEn: "Tanzania",
26571 groups: ["014", "202", "002", "UN"],
26573 callingCodes: ["255"]
26576 type: "MultiPolygon",
26577 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]]]]
26587 groups: ["151", "150", "UN"],
26588 callingCodes: ["380"]
26591 type: "MultiPolygon",
26592 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]]]]
26602 groups: ["014", "202", "002", "UN"],
26604 callingCodes: ["256"]
26607 type: "MultiPolygon",
26608 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]]]]
26616 wikidata: "Q16645",
26617 nameEn: "United States Minor Outlying Islands",
26626 nameEn: "United Nations",
26627 level: "unitedNations",
26628 isoStatus: "excRes"
26638 nameEn: "United States of America"
26649 groups: ["005", "419", "019", "UN"],
26650 callingCodes: ["598"]
26653 type: "MultiPolygon",
26654 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]]]]
26663 nameEn: "Uzbekistan",
26664 groups: ["143", "142", "UN"],
26665 callingCodes: ["998"]
26668 type: "MultiPolygon",
26669 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]]]]
26678 nameEn: "Vatican City",
26679 aliases: ["Holy See"],
26680 groups: ["039", "150"],
26681 callingCodes: ["379", "39 06"]
26684 type: "MultiPolygon",
26685 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]]]]
26694 nameEn: "St. Vincent and the Grenadines",
26696 groups: ["029", "003", "419", "019", "UN"],
26698 roadSpeedUnit: "mph",
26699 callingCodes: ["1 784"]
26702 type: "MultiPolygon",
26703 coordinates: [[[[-62.64026, 12.69984], [-59.94058, 12.34011], [-61.69315, 14.26451], [-62.64026, 12.69984]]]]
26712 nameEn: "Venezuela",
26714 groups: ["005", "419", "019", "UN"],
26715 callingCodes: ["58"]
26718 type: "MultiPolygon",
26719 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]]]]
26727 wikidata: "Q25305",
26728 nameEn: "British Virgin Islands",
26730 groups: ["BOTS", "029", "003", "419", "019", "UN"],
26732 roadSpeedUnit: "mph",
26733 roadHeightUnit: "ft",
26734 callingCodes: ["1 284"]
26737 type: "MultiPolygon",
26738 coordinates: [[[[-64.47127, 17.55688], [-63.88746, 19.15706], [-65.02435, 18.73231], [-64.86027, 18.39056], [-64.64673, 18.36549], [-64.47127, 17.55688]]]]
26746 wikidata: "Q11703",
26747 nameEn: "United States Virgin Islands",
26748 aliases: ["US-VI"],
26750 groups: ["Q1352230", "029", "003", "419", "019", "UN"],
26752 roadSpeedUnit: "mph",
26753 roadHeightUnit: "ft",
26754 callingCodes: ["1 340"]
26757 type: "MultiPolygon",
26758 coordinates: [[[[-65.02435, 18.73231], [-65.27974, 17.56928], [-64.47127, 17.55688], [-64.64673, 18.36549], [-64.86027, 18.39056], [-65.02435, 18.73231]]]]
26768 groups: ["035", "142", "UN"],
26769 callingCodes: ["84"]
26772 type: "MultiPolygon",
26773 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]]]]
26783 groups: ["054", "009", "UN"],
26784 callingCodes: ["678"]
26787 type: "MultiPolygon",
26788 coordinates: [[[[156.73836, -14.50464], [174.245, -23.1974], [172.71443, -12.01327], [156.73836, -14.50464]]]]
26796 wikidata: "Q35555",
26797 nameEn: "Wallis and Futuna",
26799 groups: ["Q1451600", "061", "009", "UN"],
26800 callingCodes: ["681"]
26803 type: "MultiPolygon",
26804 coordinates: [[[[-178.66551, -14.32452], [-176.76826, -14.95183], [-175.59809, -12.61507], [-178.66551, -14.32452]]]]
26814 groups: ["061", "009", "UN"],
26816 callingCodes: ["685"]
26819 type: "MultiPolygon",
26820 coordinates: [[[[-173.74402, -14.26669], [-170.99605, -15.1275], [-171.39864, -10.21587], [-173.74402, -14.26669]]]]
26830 groups: ["039", "150"],
26831 isoStatus: "usrAssn",
26832 callingCodes: ["383"]
26835 type: "MultiPolygon",
26836 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]]]]
26846 groups: ["145", "142", "UN"],
26847 callingCodes: ["967"]
26850 type: "MultiPolygon",
26851 coordinates: [[[[57.49095, 8.14549], [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], [40.99158, 15.81743], [43.29075, 12.79154], [43.32909, 12.59711], [43.90659, 12.3823], [51.12877, 12.56479], [57.49095, 8.14549]]]]
26859 wikidata: "Q17063",
26862 groups: ["Q3320166", "EU", "014", "202", "002", "UN"],
26863 callingCodes: ["262"]
26866 type: "MultiPolygon",
26867 coordinates: [[[[43.28731, -13.97126], [45.54824, -13.22353], [45.4971, -11.75965], [43.28731, -13.97126]]]]
26876 nameEn: "South Africa",
26877 groups: ["018", "202", "002", "UN"],
26879 callingCodes: ["27"]
26882 type: "MultiPolygon",
26883 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]]]]
26893 groups: ["014", "202", "002", "UN"],
26895 callingCodes: ["260"]
26898 type: "MultiPolygon",
26899 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]]]]
26908 nameEn: "Zimbabwe",
26909 groups: ["014", "202", "002", "UN"],
26911 callingCodes: ["263"]
26914 type: "MultiPolygon",
26915 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]]]]
26918 var borders_default = {
26921 }; // src/country-coder.ts
26923 var borders = borders_default;
26924 var whichPolygonGetter = {};
26925 var featuresByCode = {};
26926 var idFilterRegex = /(?=(?!^(and|the|of|el|la|de)$))(\b(and|the|of|el|la|de)\b)|[-_ .,'()&[\]/]/gi;
26928 function canonicalID(id) {
26931 if (s.charAt(0) === ".") {
26932 return s.toUpperCase();
26934 return s.replace(idFilterRegex, "").toUpperCase();
26938 var levels = ["subterritory", "territory", "subcountryGroup", "country", "sharedLandform", "intermediateRegion", "subregion", "region", "subunion", "union", "unitedNations", "world"];
26939 loadDerivedDataAndCaches(borders);
26941 function loadDerivedDataAndCaches(borders2) {
26942 var identifierProps = ["iso1A2", "iso1A3", "m49", "wikidata", "emojiFlag", "ccTLD", "nameEn"];
26943 var geometryFeatures = [];
26945 for (var i in borders2.features) {
26946 var feature2 = borders2.features[i];
26947 feature2.properties.id = feature2.properties.iso1A2 || feature2.properties.m49 || feature2.properties.wikidata;
26950 loadIsoStatus(feature2);
26951 loadLevel(feature2);
26952 loadGroups(feature2);
26953 loadFlag(feature2);
26954 cacheFeatureByIDs(feature2);
26955 if (feature2.geometry) geometryFeatures.push(feature2);
26958 for (var _i in borders2.features) {
26959 var _feature = borders2.features[_i];
26960 _feature.properties.groups = _feature.properties.groups.map(function (groupID) {
26961 return featuresByCode[groupID].properties.id;
26963 loadMembersForGroupsOf(_feature);
26966 for (var _i2 in borders2.features) {
26967 var _feature2 = borders2.features[_i2];
26968 loadRoadSpeedUnit(_feature2);
26969 loadRoadHeightUnit(_feature2);
26970 loadDriveSide(_feature2);
26971 loadCallingCodes(_feature2);
26972 loadGroupGroups(_feature2);
26975 for (var _i3 in borders2.features) {
26976 var _feature3 = borders2.features[_i3];
26978 _feature3.properties.groups.sort(function (groupID1, groupID2) {
26979 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
26982 if (_feature3.properties.members) _feature3.properties.members.sort(function (id1, id2) {
26983 var diff = levels.indexOf(featuresByCode[id1].properties.level) - levels.indexOf(featuresByCode[id2].properties.level);
26986 return borders2.features.indexOf(featuresByCode[id1]) - borders2.features.indexOf(featuresByCode[id2]);
26993 var geometryOnlyCollection = {
26994 type: "FeatureCollection",
26995 features: geometryFeatures
26997 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
26999 function loadGroups(feature2) {
27000 var props = feature2.properties;
27002 if (!props.groups) {
27006 if (feature2.geometry && props.country) {
27007 props.groups.push(props.country);
27010 if (props.m49 !== "001") {
27011 props.groups.push("001");
27015 function loadM49(feature2) {
27016 var props = feature2.properties;
27018 if (!props.m49 && props.iso1N3) {
27019 props.m49 = props.iso1N3;
27023 function loadTLD(feature2) {
27024 var props = feature2.properties;
27025 if (props.level === "unitedNations") return;
27027 if (!props.ccTLD && props.iso1A2) {
27028 props.ccTLD = "." + props.iso1A2.toLowerCase();
27032 function loadIsoStatus(feature2) {
27033 var props = feature2.properties;
27035 if (!props.isoStatus && props.iso1A2) {
27036 props.isoStatus = "official";
27040 function loadLevel(feature2) {
27041 var props = feature2.properties;
27042 if (props.level) return;
27044 if (!props.country) {
27045 props.level = "country";
27046 } else if (!props.iso1A2 || props.isoStatus === "official") {
27047 props.level = "territory";
27049 props.level = "subterritory";
27053 function loadGroupGroups(feature2) {
27054 var props = feature2.properties;
27055 if (feature2.geometry || !props.members) return;
27056 var featureLevelIndex = levels.indexOf(props.level);
27057 var sharedGroups = [];
27059 var _loop = function _loop(_i4) {
27060 var memberID = props.members[_i4];
27061 var member = featuresByCode[memberID];
27062 var memberGroups = member.properties.groups.filter(function (groupID) {
27063 return groupID !== feature2.properties.id && featureLevelIndex < levels.indexOf(featuresByCode[groupID].properties.level);
27067 sharedGroups = memberGroups;
27069 sharedGroups = sharedGroups.filter(function (groupID) {
27070 return memberGroups.indexOf(groupID) !== -1;
27075 for (var _i4 in props.members) {
27079 props.groups = props.groups.concat(sharedGroups.filter(function (groupID) {
27080 return props.groups.indexOf(groupID) === -1;
27083 for (var j in sharedGroups) {
27084 var groupFeature = featuresByCode[sharedGroups[j]];
27086 if (groupFeature.properties.members.indexOf(props.id) === -1) {
27087 groupFeature.properties.members.push(props.id);
27092 function loadRoadSpeedUnit(feature2) {
27093 var props = feature2.properties;
27095 if (feature2.geometry) {
27096 if (!props.roadSpeedUnit) props.roadSpeedUnit = "km/h";
27097 } else if (props.members) {
27098 var vals = Array.from(new Set(props.members.map(function (id) {
27099 var member = featuresByCode[id];
27100 if (member.geometry) return member.properties.roadSpeedUnit || "km/h";
27101 }).filter(Boolean)));
27102 if (vals.length === 1) props.roadSpeedUnit = vals[0];
27106 function loadRoadHeightUnit(feature2) {
27107 var props = feature2.properties;
27109 if (feature2.geometry) {
27110 if (!props.roadHeightUnit) props.roadHeightUnit = "m";
27111 } else if (props.members) {
27112 var vals = Array.from(new Set(props.members.map(function (id) {
27113 var member = featuresByCode[id];
27114 if (member.geometry) return member.properties.roadHeightUnit || "m";
27115 }).filter(Boolean)));
27116 if (vals.length === 1) props.roadHeightUnit = vals[0];
27120 function loadDriveSide(feature2) {
27121 var props = feature2.properties;
27123 if (feature2.geometry) {
27124 if (!props.driveSide) props.driveSide = "right";
27125 } else if (props.members) {
27126 var vals = Array.from(new Set(props.members.map(function (id) {
27127 var member = featuresByCode[id];
27128 if (member.geometry) return member.properties.driveSide || "right";
27129 }).filter(Boolean)));
27130 if (vals.length === 1) props.driveSide = vals[0];
27134 function loadCallingCodes(feature2) {
27135 var props = feature2.properties;
27137 if (!feature2.geometry && props.members) {
27138 props.callingCodes = Array.from(new Set(props.members.reduce(function (array, id) {
27139 var member = featuresByCode[id];
27140 if (member.geometry && member.properties.callingCodes) return array.concat(member.properties.callingCodes);
27146 function loadFlag(feature2) {
27147 if (!feature2.properties.iso1A2) return;
27148 var flag = feature2.properties.iso1A2.replace(/./g, function (_char) {
27149 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
27151 feature2.properties.emojiFlag = flag;
27154 function loadMembersForGroupsOf(feature2) {
27155 for (var j in feature2.properties.groups) {
27156 var groupID = feature2.properties.groups[j];
27157 var groupFeature = featuresByCode[groupID];
27158 if (!groupFeature.properties.members) groupFeature.properties.members = [];
27159 groupFeature.properties.members.push(feature2.properties.id);
27163 function cacheFeatureByIDs(feature2) {
27166 for (var k in identifierProps) {
27167 var prop = identifierProps[k];
27168 var id = feature2.properties[prop];
27169 if (id) ids.push(id);
27172 if (feature2.properties.aliases) {
27173 for (var j in feature2.properties.aliases) {
27174 ids.push(feature2.properties.aliases[j]);
27178 for (var _i5 in ids) {
27179 var _id = canonicalID(ids[_i5]);
27181 featuresByCode[_id] = feature2;
27186 function locArray(loc) {
27187 if (Array.isArray(loc)) {
27189 } else if (loc.coordinates) {
27190 return loc.coordinates;
27193 return loc.geometry.coordinates;
27196 function smallestFeature(loc) {
27197 var query = locArray(loc);
27198 var featureProperties = whichPolygonGetter(query);
27199 if (!featureProperties) return null;
27200 return featuresByCode[featureProperties.id];
27203 function countryFeature(loc) {
27204 var feature2 = smallestFeature(loc);
27205 if (!feature2) return null;
27206 var countryCode = feature2.properties.country || feature2.properties.iso1A2;
27207 return featuresByCode[countryCode] || null;
27210 var defaultOpts = {
27216 function featureForLoc(loc, opts) {
27217 var targetLevel = opts.level || "country";
27218 var maxLevel = opts.maxLevel || "world";
27219 var withProp = opts.withProp;
27220 var targetLevelIndex = levels.indexOf(targetLevel);
27221 if (targetLevelIndex === -1) return null;
27222 var maxLevelIndex = levels.indexOf(maxLevel);
27223 if (maxLevelIndex === -1) return null;
27224 if (maxLevelIndex < targetLevelIndex) return null;
27226 if (targetLevel === "country") {
27227 var fastFeature = countryFeature(loc);
27230 if (!withProp || fastFeature.properties[withProp]) {
27231 return fastFeature;
27236 var features2 = featuresContaining(loc);
27238 for (var i in features2) {
27239 var feature2 = features2[i];
27240 var levelIndex = levels.indexOf(feature2.properties.level);
27242 if (feature2.properties.level === targetLevel || levelIndex > targetLevelIndex && levelIndex <= maxLevelIndex) {
27243 if (!withProp || feature2.properties[withProp]) {
27252 function featureForID(id) {
27255 if (typeof id === "number") {
27256 stringID = id.toString();
27258 if (stringID.length === 1) {
27259 stringID = "00" + stringID;
27260 } else if (stringID.length === 2) {
27261 stringID = "0" + stringID;
27264 stringID = canonicalID(id);
27267 return featuresByCode[stringID] || null;
27270 function smallestFeaturesForBbox(bbox) {
27271 return whichPolygonGetter.bbox(bbox).map(function (props) {
27272 return featuresByCode[props.id];
27276 function smallestOrMatchingFeature(query) {
27277 if (_typeof(query) === "object") {
27278 return smallestFeature(query);
27281 return featureForID(query);
27284 function feature$1(query) {
27285 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27287 if (_typeof(query) === "object") {
27288 return featureForLoc(query, opts);
27291 return featureForID(query);
27294 function iso1A2Code(query) {
27295 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOpts;
27296 opts.withProp = "iso1A2";
27297 var match = feature$1(query, opts);
27298 if (!match) return null;
27299 return match.properties.iso1A2 || null;
27302 function featuresContaining(query, strict) {
27303 var matchingFeatures;
27305 if (Array.isArray(query) && query.length === 4) {
27306 matchingFeatures = smallestFeaturesForBbox(query);
27308 var smallestOrMatching = smallestOrMatchingFeature(query);
27309 matchingFeatures = smallestOrMatching ? [smallestOrMatching] : [];
27312 if (!matchingFeatures.length) return [];
27313 var returnFeatures;
27315 if (!strict || _typeof(query) === "object") {
27316 returnFeatures = matchingFeatures.slice();
27318 returnFeatures = [];
27321 for (var j in matchingFeatures) {
27322 var properties = matchingFeatures[j].properties;
27324 for (var i in properties.groups) {
27325 var groupID = properties.groups[i];
27326 var groupFeature = featuresByCode[groupID];
27328 if (returnFeatures.indexOf(groupFeature) === -1) {
27329 returnFeatures.push(groupFeature);
27334 return returnFeatures;
27337 function featuresIn(id, strict) {
27338 var feature2 = featureForID(id);
27339 if (!feature2) return [];
27340 var features2 = [];
27343 features2.push(feature2);
27346 var properties = feature2.properties;
27348 if (properties.members) {
27349 for (var i in properties.members) {
27350 var memberID = properties.members[i];
27351 features2.push(featuresByCode[memberID]);
27358 function aggregateFeature(id) {
27359 var features2 = featuresIn(id, false);
27360 if (features2.length === 0) return null;
27361 var aggregateCoordinates = [];
27363 for (var i in features2) {
27364 var feature2 = features2[i];
27366 if (feature2.geometry && feature2.geometry.type === "MultiPolygon" && feature2.geometry.coordinates) {
27367 aggregateCoordinates = aggregateCoordinates.concat(feature2.geometry.coordinates);
27373 properties: features2[0].properties,
27375 type: "MultiPolygon",
27376 coordinates: aggregateCoordinates
27381 function roadSpeedUnit(query) {
27382 var feature2 = smallestOrMatchingFeature(query);
27383 return feature2 && feature2.properties.roadSpeedUnit || null;
27386 var RADIUS = 6378137;
27387 var FLATTENING = 1 / 298.257223563;
27388 var POLAR_RADIUS = 6356752.3142;
27391 FLATTENING: FLATTENING,
27392 POLAR_RADIUS: POLAR_RADIUS
27395 var geometry_1 = geometry;
27396 var ring = ringArea;
27398 function geometry(_) {
27404 return polygonArea(_.coordinates);
27406 case 'MultiPolygon':
27407 for (i = 0; i < _.coordinates.length; i++) {
27408 area += polygonArea(_.coordinates[i]);
27416 case 'MultiLineString':
27419 case 'GeometryCollection':
27420 for (i = 0; i < _.geometries.length; i++) {
27421 area += geometry(_.geometries[i]);
27428 function polygonArea(coords) {
27431 if (coords && coords.length > 0) {
27432 area += Math.abs(ringArea(coords[0]));
27434 for (var i = 1; i < coords.length; i++) {
27435 area -= Math.abs(ringArea(coords[i]));
27442 * Calculate the approximate area of the polygon were it projected onto
27443 * the earth. Note that this area will be positive if ring is oriented
27444 * clockwise, otherwise it will be negative.
27447 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
27448 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
27449 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
27452 * {float} The approximate signed geodesic area of the polygon in square
27457 function ringArea(coords) {
27466 coordsLength = coords.length;
27468 if (coordsLength > 2) {
27469 for (i = 0; i < coordsLength; i++) {
27470 if (i === coordsLength - 2) {
27472 lowerIndex = coordsLength - 2;
27473 middleIndex = coordsLength - 1;
27475 } else if (i === coordsLength - 1) {
27477 lowerIndex = coordsLength - 1;
27483 middleIndex = i + 1;
27484 upperIndex = i + 2;
27487 p1 = coords[lowerIndex];
27488 p2 = coords[middleIndex];
27489 p3 = coords[upperIndex];
27490 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
27493 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
27500 return _ * Math.PI / 180;
27503 var geojsonArea = {
27504 geometry: geometry_1,
27508 var $includes = arrayIncludes.includes;
27511 // `Array.prototype.includes` method
27512 // https://tc39.es/ecma262/#sec-array.prototype.includes
27513 _export({ target: 'Array', proto: true }, {
27514 includes: function includes(el /* , fromIndex = 0 */) {
27515 return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
27519 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
27520 addToUnscopables('includes');
27522 var validateCenter_1$1 = function validateCenter(center) {
27523 var validCenterLengths = [2, 3];
27525 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
27526 throw new Error("ERROR! Center has to be an array of length two or three");
27529 var _center = _slicedToArray(center, 2),
27533 if (typeof lng !== "number" || typeof lat !== "number") {
27534 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
27537 if (lng > 180 || lng < -180) {
27538 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
27541 if (lat > 90 || lat < -90) {
27542 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
27546 var validateCenter$1 = {
27547 validateCenter: validateCenter_1$1
27550 var validateRadius_1$1 = function validateRadius(radius) {
27551 if (typeof radius !== "number") {
27552 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
27556 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
27560 var validateRadius$1 = {
27561 validateRadius: validateRadius_1$1
27564 var validateNumberOfEdges_1$1 = function validateNumberOfEdges(numberOfEdges) {
27565 if (typeof numberOfEdges !== "number") {
27566 var ARGUMENT_TYPE = Array.isArray(numberOfEdges) ? "array" : _typeof(numberOfEdges);
27567 throw new Error("ERROR! Number of edges has to be a number but was: ".concat(ARGUMENT_TYPE));
27570 if (numberOfEdges < 3) {
27571 throw new Error("ERROR! Number of edges has to be at least 3 but was: ".concat(numberOfEdges));
27575 var validateNumberOfEdges$1 = {
27576 validateNumberOfEdges: validateNumberOfEdges_1$1
27579 var validateEarthRadius_1$1 = function validateEarthRadius(earthRadius) {
27580 if (typeof earthRadius !== "number") {
27581 var ARGUMENT_TYPE = Array.isArray(earthRadius) ? "array" : _typeof(earthRadius);
27582 throw new Error("ERROR! Earth radius has to be a number but was: ".concat(ARGUMENT_TYPE));
27585 if (earthRadius <= 0) {
27586 throw new Error("ERROR! Earth radius has to be a positive number but was: ".concat(earthRadius));
27590 var validateEarthRadius$1 = {
27591 validateEarthRadius: validateEarthRadius_1$1
27594 var validateBearing_1$1 = function validateBearing(bearing) {
27595 if (typeof bearing !== "number") {
27596 var ARGUMENT_TYPE = Array.isArray(bearing) ? "array" : _typeof(bearing);
27597 throw new Error("ERROR! Bearing has to be a number but was: ".concat(ARGUMENT_TYPE));
27601 var validateBearing$1 = {
27602 validateBearing: validateBearing_1$1
27605 var validateCenter = validateCenter$1.validateCenter;
27606 var validateRadius = validateRadius$1.validateRadius;
27607 var validateNumberOfEdges = validateNumberOfEdges$1.validateNumberOfEdges;
27608 var validateEarthRadius = validateEarthRadius$1.validateEarthRadius;
27609 var validateBearing = validateBearing$1.validateBearing;
27611 function validateInput$1(_ref) {
27612 var center = _ref.center,
27613 radius = _ref.radius,
27614 numberOfEdges = _ref.numberOfEdges,
27615 earthRadius = _ref.earthRadius,
27616 bearing = _ref.bearing;
27617 validateCenter(center);
27618 validateRadius(radius);
27619 validateNumberOfEdges(numberOfEdges);
27620 validateEarthRadius(earthRadius);
27621 validateBearing(bearing);
27624 var validateCenter_1 = validateCenter;
27625 var validateRadius_1 = validateRadius;
27626 var validateNumberOfEdges_1 = validateNumberOfEdges;
27627 var validateEarthRadius_1 = validateEarthRadius;
27628 var validateBearing_1 = validateBearing;
27629 var validateInput_1 = validateInput$1;
27630 var inputValidation = {
27631 validateCenter: validateCenter_1,
27632 validateRadius: validateRadius_1,
27633 validateNumberOfEdges: validateNumberOfEdges_1,
27634 validateEarthRadius: validateEarthRadius_1,
27635 validateBearing: validateBearing_1,
27636 validateInput: validateInput_1
27639 var validateInput = inputValidation.validateInput;
27640 var defaultEarthRadius = 6378137; // equatorial Earth radius
27642 function toRadians(angleInDegrees) {
27643 return angleInDegrees * Math.PI / 180;
27646 function toDegrees(angleInRadians) {
27647 return angleInRadians * 180 / Math.PI;
27650 function offset(c1, distance, earthRadius, bearing) {
27651 var lat1 = toRadians(c1[1]);
27652 var lon1 = toRadians(c1[0]);
27653 var dByR = distance / earthRadius;
27654 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
27655 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
27656 return [toDegrees(lon), toDegrees(lat)];
27659 var circleToPolygon = function circleToPolygon(center, radius, options) {
27660 var n = getNumberOfEdges(options);
27661 var earthRadius = getEarthRadius(options);
27662 var bearing = getBearing(options);
27663 var direction = getDirection(options); // validateInput() throws error on invalid input and do nothing on valid input
27669 earthRadius: earthRadius,
27672 var start = toRadians(bearing);
27673 var coordinates = [];
27675 for (var i = 0; i < n; ++i) {
27676 coordinates.push(offset(center, radius, earthRadius, start + direction * 2 * Math.PI * -i / n));
27679 coordinates.push(coordinates[0]);
27682 coordinates: [coordinates]
27686 function getNumberOfEdges(options) {
27687 if (isUndefinedOrNull(options)) {
27689 } else if (isObjectNotArray(options)) {
27690 var numberOfEdges = options.numberOfEdges;
27691 return numberOfEdges === undefined ? 32 : numberOfEdges;
27697 function getEarthRadius(options) {
27698 if (isUndefinedOrNull(options)) {
27699 return defaultEarthRadius;
27700 } else if (isObjectNotArray(options)) {
27701 var earthRadius = options.earthRadius;
27702 return earthRadius === undefined ? defaultEarthRadius : earthRadius;
27705 return defaultEarthRadius;
27708 function getDirection(options) {
27709 if (isObjectNotArray(options) && options.rightHandRule) {
27716 function getBearing(options) {
27717 if (isUndefinedOrNull(options)) {
27719 } else if (isObjectNotArray(options)) {
27720 var bearing = options.bearing;
27721 return bearing === undefined ? 0 : bearing;
27727 function isObjectNotArray(argument) {
27728 return argument !== null && _typeof(argument) === "object" && !Array.isArray(argument);
27731 function isUndefinedOrNull(argument) {
27732 return argument === null || argument === undefined;
27735 // `Number.EPSILON` constant
27736 // https://tc39.es/ecma262/#sec-number.epsilon
27737 _export({ target: 'Number', stat: true }, {
27738 EPSILON: Math.pow(2, -52)
27743 // `CreateHTML` abstract operation
27744 // https://tc39.es/ecma262/#sec-createhtml
27745 var createHtml = function (string, tag, attribute, value) {
27746 var S = String(requireObjectCoercible(string));
27747 var p1 = '<' + tag;
27748 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
27749 return p1 + '>' + S + '</' + tag + '>';
27752 // check the existence of a method, lowercase
27753 // of a tag and escaping quotes in arguments
27754 var stringHtmlForced = function (METHOD_NAME) {
27755 return fails(function () {
27756 var test = ''[METHOD_NAME]('"');
27757 return test !== test.toLowerCase() || test.split('"').length > 3;
27761 // `String.prototype.link` method
27762 // https://tc39.es/ecma262/#sec-string.prototype.link
27763 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
27764 link: function link(url) {
27765 return createHtml(this, 'a', 'href', url);
27771 * Fast Splay tree for Node and browser
27773 * @author Alexander Milevski <info@w8r.name>
27780 function Node(key, data) {
27790 /* follows "An implementation of top-down splaying"
27791 * by D. Sleator <sleator@cs.cmu.edu> March 1992
27795 function DEFAULT_COMPARE(a, b) {
27796 return a > b ? 1 : a < b ? -1 : 0;
27799 * Simple top down splay, not requiring i to be in the tree t.
27803 function splay(i, t, comparator) {
27804 var N = new Node(null, null);
27809 var cmp = comparator(i, t.key); //if (i < t.key) {
27812 if (t.left === null) break; //if (i < t.left.key) {
27814 if (comparator(i, t.left.key) < 0) {
27821 if (t.left === null) break;
27828 t = t.left; //} else if (i > t.key) {
27829 } else if (cmp > 0) {
27830 if (t.right === null) break; //if (i > t.right.key) {
27832 if (comparator(i, t.right.key) > 0) {
27839 if (t.right === null) break;
27859 function insert(i, data, t, comparator) {
27860 var node = new Node(i, data);
27863 node.left = node.right = null;
27867 t = splay(i, t, comparator);
27868 var cmp = comparator(i, t.key);
27871 node.left = t.left;
27874 } else if (cmp >= 0) {
27875 node.right = t.right;
27883 function split(key, v, comparator) {
27888 v = splay(key, v, comparator);
27889 var cmp = comparator(v.key, key);
27894 } else if (cmp < 0) {
27911 function merge$3(left, right, comparator) {
27912 if (right === null) return left;
27913 if (left === null) return right;
27914 right = splay(left.key, right, comparator);
27919 * Prints level of the tree
27923 function printRow(root, prefix, isTail, out, printNode) {
27925 out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n");
27926 var indent = prefix + (isTail ? ' ' : '│ ');
27927 if (root.left) printRow(root.left, indent, false, out, printNode);
27928 if (root.right) printRow(root.right, indent, true, out, printNode);
27935 function Tree(comparator) {
27936 if (comparator === void 0) {
27937 comparator = DEFAULT_COMPARE;
27942 this._comparator = comparator;
27945 * Inserts a key, allows duplicates
27949 Tree.prototype.insert = function (key, data) {
27951 return this._root = insert(key, data, this._root, this._comparator);
27954 * Adds a key, if it is not present in the tree
27958 Tree.prototype.add = function (key, data) {
27959 var node = new Node(key, data);
27961 if (this._root === null) {
27962 node.left = node.right = null;
27967 var comparator = this._comparator;
27968 var t = splay(key, this._root, comparator);
27969 var cmp = comparator(key, t.key);
27970 if (cmp === 0) this._root = t;else {
27972 node.left = t.left;
27975 } else if (cmp > 0) {
27976 node.right = t.right;
27988 * @return {Node|null}
27992 Tree.prototype.remove = function (key) {
27993 this._root = this._remove(key, this._root, this._comparator);
27996 * Deletes i from the tree if it's there
28000 Tree.prototype._remove = function (i, t, comparator) {
28002 if (t === null) return null;
28003 t = splay(i, t, comparator);
28004 var cmp = comparator(i, t.key);
28008 if (t.left === null) {
28011 x = splay(i, t.left, comparator);
28020 /* It wasn't there */
28023 * Removes and returns the node with smallest key
28027 Tree.prototype.pop = function () {
28028 var node = this._root;
28031 while (node.left) {
28035 this._root = splay(node.key, this._root, this._comparator);
28036 this._root = this._remove(node.key, this._root, this._comparator);
28046 * Find without splaying
28050 Tree.prototype.findStatic = function (key) {
28051 var current = this._root;
28052 var compare = this._comparator;
28055 var cmp = compare(key, current.key);
28056 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
28062 Tree.prototype.find = function (key) {
28064 this._root = splay(key, this._root, this._comparator);
28065 if (this._comparator(key, this._root.key) !== 0) return null;
28071 Tree.prototype.contains = function (key) {
28072 var current = this._root;
28073 var compare = this._comparator;
28076 var cmp = compare(key, current.key);
28077 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
28083 Tree.prototype.forEach = function (visitor, ctx) {
28084 var current = this._root;
28086 /* Initialize stack s */
28091 if (current !== null) {
28093 current = current.left;
28095 if (Q.length !== 0) {
28097 visitor.call(ctx, current);
28098 current = current.right;
28099 } else done = true;
28106 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
28110 Tree.prototype.range = function (low, high, fn, ctx) {
28112 var compare = this._comparator;
28113 var node = this._root;
28116 while (Q.length !== 0 || node) {
28122 cmp = compare(node.key, high);
28126 } else if (compare(node.key, low) >= 0) {
28127 if (fn.call(ctx, node)) return this; // stop if smth is returned
28137 * Returns array of keys
28141 Tree.prototype.keys = function () {
28143 this.forEach(function (_a) {
28145 return keys.push(key);
28150 * Returns array of all the data in the nodes
28154 Tree.prototype.values = function () {
28156 this.forEach(function (_a) {
28157 var data = _a.data;
28158 return values.push(data);
28163 Tree.prototype.min = function () {
28164 if (this._root) return this.minNode(this._root).key;
28168 Tree.prototype.max = function () {
28169 if (this._root) return this.maxNode(this._root).key;
28173 Tree.prototype.minNode = function (t) {
28174 if (t === void 0) {
28178 if (t) while (t.left) {
28184 Tree.prototype.maxNode = function (t) {
28185 if (t === void 0) {
28189 if (t) while (t.right) {
28195 * Returns node at given index
28199 Tree.prototype.at = function (index) {
28200 var current = this._root;
28208 current = current.left;
28210 if (Q.length > 0) {
28212 if (i === index) return current;
28214 current = current.right;
28215 } else done = true;
28222 Tree.prototype.next = function (d) {
28223 var root = this._root;
28224 var successor = null;
28227 successor = d.right;
28229 while (successor.left) {
28230 successor = successor.left;
28236 var comparator = this._comparator;
28239 var cmp = comparator(d.key, root.key);
28240 if (cmp === 0) break;else if (cmp < 0) {
28243 } else root = root.right;
28249 Tree.prototype.prev = function (d) {
28250 var root = this._root;
28251 var predecessor = null;
28253 if (d.left !== null) {
28254 predecessor = d.left;
28256 while (predecessor.right) {
28257 predecessor = predecessor.right;
28260 return predecessor;
28263 var comparator = this._comparator;
28266 var cmp = comparator(d.key, root.key);
28267 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
28268 predecessor = root;
28273 return predecessor;
28276 Tree.prototype.clear = function () {
28282 Tree.prototype.toList = function () {
28283 return toList(this._root);
28286 * Bulk-load items. Both array have to be same size
28290 Tree.prototype.load = function (keys, values, presort) {
28291 if (values === void 0) {
28295 if (presort === void 0) {
28299 var size = keys.length;
28300 var comparator = this._comparator; // sort if needed
28302 if (presort) sort(keys, values, 0, size - 1, comparator);
28304 if (this._root === null) {
28306 this._root = loadRecursive(keys, values, 0, size);
28309 // that re-builds the whole tree from two in-order traversals
28310 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
28311 size = this._size + size;
28312 this._root = sortedListToBST({
28320 Tree.prototype.isEmpty = function () {
28321 return this._root === null;
28324 Object.defineProperty(Tree.prototype, "size", {
28325 get: function get() {
28331 Object.defineProperty(Tree.prototype, "root", {
28332 get: function get() {
28339 Tree.prototype.toString = function (printNode) {
28340 if (printNode === void 0) {
28341 printNode = function printNode(n) {
28342 return String(n.key);
28347 printRow(this._root, '', true, function (v) {
28348 return out.push(v);
28350 return out.join('');
28353 Tree.prototype.update = function (key, newKey, newData) {
28354 var comparator = this._comparator;
28356 var _a = split(key, this._root, comparator),
28360 if (comparator(key, newKey) < 0) {
28361 right = insert(newKey, newData, right, comparator);
28363 left = insert(newKey, newData, left, comparator);
28366 this._root = merge$3(left, right, comparator);
28369 Tree.prototype.split = function (key) {
28370 return split(key, this._root, this._comparator);
28376 function loadRecursive(keys, values, start, end) {
28377 var size = end - start;
28380 var middle = start + Math.floor(size / 2);
28381 var key = keys[middle];
28382 var data = values[middle];
28383 var node = new Node(key, data);
28384 node.left = loadRecursive(keys, values, start, middle);
28385 node.right = loadRecursive(keys, values, middle + 1, end);
28392 function createList(keys, values) {
28393 var head = new Node(null, null);
28396 for (var i = 0; i < keys.length; i++) {
28397 p = p.next = new Node(keys[i], values[i]);
28404 function toList(root) {
28405 var current = root;
28408 var head = new Node(null, null);
28414 current = current.left;
28416 if (Q.length > 0) {
28417 current = p = p.next = Q.pop();
28418 current = current.right;
28419 } else done = true;
28423 p.next = null; // that'll work even if the tree was empty
28428 function sortedListToBST(list, start, end) {
28429 var size = end - start;
28432 var middle = start + Math.floor(size / 2);
28433 var left = sortedListToBST(list, start, middle);
28434 var root = list.head;
28436 list.head = list.head.next;
28437 root.right = sortedListToBST(list, middle + 1, end);
28444 function mergeLists(l1, l2, compare) {
28445 var head = new Node(null, null); // dummy
28451 while (p1 !== null && p2 !== null) {
28452 if (compare(p1.key, p2.key) < 0) {
28465 } else if (p2 !== null) {
28472 function sort(keys, values, left, right, compare) {
28473 if (left >= right) return;
28474 var pivot = keys[left + right >> 1];
28481 } while (compare(keys[i], pivot) < 0);
28485 } while (compare(keys[j], pivot) > 0);
28492 values[i] = values[j];
28496 sort(keys, values, left, j, compare);
28497 sort(keys, values, j + 1, right, compare);
28500 function _classCallCheck(instance, Constructor) {
28501 if (!(instance instanceof Constructor)) {
28502 throw new TypeError("Cannot call a class as a function");
28506 function _defineProperties(target, props) {
28507 for (var i = 0; i < props.length; i++) {
28508 var descriptor = props[i];
28509 descriptor.enumerable = descriptor.enumerable || false;
28510 descriptor.configurable = true;
28511 if ("value" in descriptor) descriptor.writable = true;
28512 Object.defineProperty(target, descriptor.key, descriptor);
28516 function _createClass(Constructor, protoProps, staticProps) {
28517 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
28518 if (staticProps) _defineProperties(Constructor, staticProps);
28519 return Constructor;
28522 * A bounding box has the format:
28524 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
28529 var isInBbox = function isInBbox(bbox, point) {
28530 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
28532 /* Returns either null, or a bbox (aka an ordered pair of points)
28533 * If there is only one point of overlap, a bbox with identical points
28534 * will be returned */
28537 var getBboxOverlap = function getBboxOverlap(b1, b2) {
28538 // check if the bboxes overlap at all
28539 if (b2.ur.x < b1.ll.x || b1.ur.x < b2.ll.x || b2.ur.y < b1.ll.y || b1.ur.y < b2.ll.y) return null; // find the middle two X values
28541 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
28542 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
28544 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
28545 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
28558 /* Javascript doesn't do integer math. Everything is
28559 * floating point with percision Number.EPSILON.
28561 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
28565 var epsilon = Number.EPSILON; // IE Polyfill
28567 if (epsilon === undefined) epsilon = Math.pow(2, -52);
28568 var EPSILON_SQ = epsilon * epsilon;
28569 /* FLP comparator */
28571 var cmp = function cmp(a, b) {
28572 // check if they're both 0
28573 if (-epsilon < a && a < epsilon) {
28574 if (-epsilon < b && b < epsilon) {
28577 } // check if they're flp equal
28582 if (ab * ab < EPSILON_SQ * a * b) {
28584 } // normal comparison
28587 return a < b ? -1 : 1;
28590 * This class rounds incoming values sufficiently so that
28591 * floating points problems are, for the most part, avoided.
28593 * Incoming points are have their x & y values tested against
28594 * all previously seen x & y values. If either is 'too close'
28595 * to a previously seen value, it's value is 'snapped' to the
28596 * previously seen value.
28598 * All points should be rounded by this class before being
28599 * stored in any data structures in the rest of this algorithm.
28603 var PtRounder = /*#__PURE__*/function () {
28604 function PtRounder() {
28605 _classCallCheck(this, PtRounder);
28610 _createClass(PtRounder, [{
28612 value: function reset() {
28613 this.xRounder = new CoordRounder();
28614 this.yRounder = new CoordRounder();
28618 value: function round(x, y) {
28620 x: this.xRounder.round(x),
28621 y: this.yRounder.round(y)
28629 var CoordRounder = /*#__PURE__*/function () {
28630 function CoordRounder() {
28631 _classCallCheck(this, CoordRounder);
28633 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
28636 } // Note: this can rounds input values backwards or forwards.
28637 // You might ask, why not restrict this to just rounding
28638 // forwards? Wouldn't that allow left endpoints to always
28639 // remain left endpoints during splitting (never change to
28640 // right). No - it wouldn't, because we snap intersections
28641 // to endpoints (to establish independence from the segment
28642 // angle for t-intersections).
28645 _createClass(CoordRounder, [{
28647 value: function round(coord) {
28648 var node = this.tree.add(coord);
28649 var prevNode = this.tree.prev(node);
28651 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
28652 this.tree.remove(coord);
28653 return prevNode.key;
28656 var nextNode = this.tree.next(node);
28658 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
28659 this.tree.remove(coord);
28660 return nextNode.key;
28667 return CoordRounder;
28668 }(); // singleton available by import
28671 var rounder = new PtRounder();
28672 /* Cross Product of two vectors with first point at origin */
28674 var crossProduct = function crossProduct(a, b) {
28675 return a.x * b.y - a.y * b.x;
28677 /* Dot Product of two vectors with first point at origin */
28680 var dotProduct = function dotProduct(a, b) {
28681 return a.x * b.x + a.y * b.y;
28683 /* Comparator for two vectors with same starting point */
28686 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
28688 x: endPt1.x - basePt.x,
28689 y: endPt1.y - basePt.y
28692 x: endPt2.x - basePt.x,
28693 y: endPt2.y - basePt.y
28695 var kross = crossProduct(v1, v2);
28696 return cmp(kross, 0);
28699 var length = function length(v) {
28700 return Math.sqrt(dotProduct(v, v));
28702 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
28705 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
28707 x: pBase.x - pShared.x,
28708 y: pBase.y - pShared.y
28711 x: pAngle.x - pShared.x,
28712 y: pAngle.y - pShared.y
28714 return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28716 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
28719 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
28721 x: pBase.x - pShared.x,
28722 y: pBase.y - pShared.y
28725 x: pAngle.x - pShared.x,
28726 y: pAngle.y - pShared.y
28728 return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase);
28730 /* Get the x coordinate where the given line (defined by a point and vector)
28731 * crosses the horizontal line with the given y coordiante.
28732 * In the case of parrallel lines (including overlapping ones) returns null. */
28735 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
28736 if (v.y === 0) return null;
28738 x: pt.x + v.x / v.y * (y - pt.y),
28742 /* Get the y coordinate where the given line (defined by a point and vector)
28743 * crosses the vertical line with the given x coordiante.
28744 * In the case of parrallel lines (including overlapping ones) returns null. */
28747 var verticalIntersection = function verticalIntersection(pt, v, x) {
28748 if (v.x === 0) return null;
28751 y: pt.y + v.y / v.x * (x - pt.x)
28754 /* Get the intersection of two lines, each defined by a base point and a vector.
28755 * In the case of parrallel lines (including overlapping ones) returns null. */
28758 var intersection = function intersection(pt1, v1, pt2, v2) {
28759 // take some shortcuts for vertical and horizontal lines
28760 // this also ensures we don't calculate an intersection and then discover
28761 // it's actually outside the bounding box of the line
28762 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
28763 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
28764 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
28765 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
28766 // This algorithm is based on Schneider and Eberly.
28767 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
28769 var kross = crossProduct(v1, v2);
28770 if (kross == 0) return null;
28775 var d1 = crossProduct(ve, v1) / kross;
28776 var d2 = crossProduct(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
28778 var x1 = pt1.x + d2 * v1.x,
28779 x2 = pt2.x + d1 * v2.x;
28780 var y1 = pt1.y + d2 * v1.y,
28781 y2 = pt2.y + d1 * v2.y;
28782 var x = (x1 + x2) / 2;
28783 var y = (y1 + y2) / 2;
28790 var SweepEvent = /*#__PURE__*/function () {
28791 _createClass(SweepEvent, null, [{
28793 // for ordering sweep events in the sweep event queue
28794 value: function compare(a, b) {
28795 // favor event with a point that the sweep line hits first
28796 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
28797 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
28799 if (a.point !== b.point) a.link(b); // favor right events over left
28801 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
28802 // ordering of this case is the same as for their segments
28804 return Segment.compare(a.segment, b.segment);
28805 } // for ordering points in sweep line order
28808 key: "comparePoints",
28809 value: function comparePoints(aPt, bPt) {
28810 if (aPt.x < bPt.x) return -1;
28811 if (aPt.x > bPt.x) return 1;
28812 if (aPt.y < bPt.y) return -1;
28813 if (aPt.y > bPt.y) return 1;
28815 } // Warning: 'point' input will be modified and re-used (for performance)
28819 function SweepEvent(point, isLeft) {
28820 _classCallCheck(this, SweepEvent);
28822 if (point.events === undefined) point.events = [this];else point.events.push(this);
28823 this.point = point;
28824 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
28827 _createClass(SweepEvent, [{
28829 value: function link(other) {
28830 if (other.point === this.point) {
28831 throw new Error('Tried to link already linked events');
28834 var otherEvents = other.point.events;
28836 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
28837 var evt = otherEvents[i];
28838 this.point.events.push(evt);
28839 evt.point = this.point;
28842 this.checkForConsuming();
28844 /* Do a pass over our linked events and check to see if any pair
28845 * of segments match, and should be consumed. */
28848 key: "checkForConsuming",
28849 value: function checkForConsuming() {
28850 // FIXME: The loops in this method run O(n^2) => no good.
28851 // Maintain little ordered sweep event trees?
28852 // Can we maintaining an ordering that avoids the need
28853 // for the re-sorting with getLeftmostComparator in geom-out?
28854 // Compare each pair of events to see if other events also match
28855 var numEvents = this.point.events.length;
28857 for (var i = 0; i < numEvents; i++) {
28858 var evt1 = this.point.events[i];
28859 if (evt1.segment.consumedBy !== undefined) continue;
28861 for (var j = i + 1; j < numEvents; j++) {
28862 var evt2 = this.point.events[j];
28863 if (evt2.consumedBy !== undefined) continue;
28864 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
28865 evt1.segment.consume(evt2.segment);
28870 key: "getAvailableLinkedEvents",
28871 value: function getAvailableLinkedEvents() {
28872 // point.events is always of length 2 or greater
28875 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
28876 var evt = this.point.events[i];
28878 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
28886 * Returns a comparator function for sorting linked events that will
28887 * favor the event that will give us the smallest left-side angle.
28888 * All ring construction starts as low as possible heading to the right,
28889 * so by always turning left as sharp as possible we'll get polygons
28890 * without uncessary loops & holes.
28892 * The comparator function has a compute cache such that it avoids
28893 * re-computing already-computed values.
28897 key: "getLeftmostComparator",
28898 value: function getLeftmostComparator(baseEvent) {
28901 var cache = new Map();
28903 var fillCache = function fillCache(linkedEvent) {
28904 var nextEvent = linkedEvent.otherSE;
28905 cache.set(linkedEvent, {
28906 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
28907 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
28911 return function (a, b) {
28912 if (!cache.has(a)) fillCache(a);
28913 if (!cache.has(b)) fillCache(b);
28915 var _cache$get = cache.get(a),
28916 asine = _cache$get.sine,
28917 acosine = _cache$get.cosine;
28919 var _cache$get2 = cache.get(b),
28920 bsine = _cache$get2.sine,
28921 bcosine = _cache$get2.cosine; // both on or above x-axis
28924 if (asine >= 0 && bsine >= 0) {
28925 if (acosine < bcosine) return 1;
28926 if (acosine > bcosine) return -1;
28928 } // both below x-axis
28931 if (asine < 0 && bsine < 0) {
28932 if (acosine < bcosine) return -1;
28933 if (acosine > bcosine) return 1;
28935 } // one above x-axis, one below
28938 if (bsine < asine) return -1;
28939 if (bsine > asine) return 1;
28946 }(); // segments and sweep events when all else is identical
28951 var Segment = /*#__PURE__*/function () {
28952 _createClass(Segment, null, [{
28955 /* This compare() function is for ordering segments in the sweep
28956 * line tree, and does so according to the following criteria:
28958 * Consider the vertical line that lies an infinestimal step to the
28959 * right of the right-more of the two left endpoints of the input
28960 * segments. Imagine slowly moving a point up from negative infinity
28961 * in the increasing y direction. Which of the two segments will that
28962 * point intersect first? That segment comes 'before' the other one.
28964 * If neither segment would be intersected by such a line, (if one
28965 * or more of the segments are vertical) then the line to be considered
28966 * is directly on the right-more of the two left inputs.
28968 value: function compare(a, b) {
28969 var alx = a.leftSE.point.x;
28970 var blx = b.leftSE.point.x;
28971 var arx = a.rightSE.point.x;
28972 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
28974 if (brx < alx) return 1;
28975 if (arx < blx) return -1;
28976 var aly = a.leftSE.point.y;
28977 var bly = b.leftSE.point.y;
28978 var ary = a.rightSE.point.y;
28979 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
28982 // are the two segments in the same horizontal plane?
28983 if (bly < aly && bly < ary) return 1;
28984 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
28986 var aCmpBLeft = a.comparePoint(b.leftSE.point);
28987 if (aCmpBLeft < 0) return 1;
28988 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
28990 var bCmpARight = b.comparePoint(a.rightSE.point);
28991 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
28992 // left endpoint to be first (arbitrary?)
28995 } // is left endpoint of segment A the right-more?
28999 if (aly < bly && aly < bry) return -1;
29000 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
29002 var bCmpALeft = b.comparePoint(a.leftSE.point);
29003 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
29005 var aCmpBRight = a.comparePoint(b.rightSE.point);
29006 if (aCmpBRight < 0) return 1;
29007 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
29008 // left endpoint to be first (arbitrary?)
29011 } // if we get here, the two left endpoints are in the same
29012 // vertical plane, ie alx === blx
29013 // consider the lower left-endpoint to come first
29016 if (aly < bly) return -1;
29017 if (aly > bly) return 1; // left endpoints are identical
29018 // check for colinearity by using the left-more right endpoint
29019 // is the A right endpoint more left-more?
29022 var _bCmpARight = b.comparePoint(a.rightSE.point);
29024 if (_bCmpARight !== 0) return _bCmpARight;
29025 } // is the B right endpoint more left-more?
29029 var _aCmpBRight = a.comparePoint(b.rightSE.point);
29031 if (_aCmpBRight < 0) return 1;
29032 if (_aCmpBRight > 0) return -1;
29036 // are these two [almost] vertical segments with opposite orientation?
29037 // if so, the one with the lower right endpoint comes first
29038 var ay = ary - aly;
29039 var ax = arx - alx;
29040 var by = bry - bly;
29041 var bx = brx - blx;
29042 if (ay > ax && by < bx) return 1;
29043 if (ay < ax && by > bx) return -1;
29044 } // we have colinear segments with matching orientation
29045 // consider the one with more left-more right endpoint to be first
29048 if (arx > brx) return 1;
29049 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
29050 // vertical plane, ie arx === brx
29051 // consider the lower right-endpoint to come first
29053 if (ary < bry) return -1;
29054 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
29055 // fall back on creation order as consistent tie-breaker
29057 if (a.id < b.id) return -1;
29058 if (a.id > b.id) return 1; // identical segment, ie a === b
29062 /* Warning: a reference to ringWindings input will be stored,
29063 * and possibly will be later modified */
29067 function Segment(leftSE, rightSE, rings, windings) {
29068 _classCallCheck(this, Segment);
29070 this.id = ++segmentId;
29071 this.leftSE = leftSE;
29072 leftSE.segment = this;
29073 leftSE.otherSE = rightSE;
29074 this.rightSE = rightSE;
29075 rightSE.segment = this;
29076 rightSE.otherSE = leftSE;
29077 this.rings = rings;
29078 this.windings = windings; // left unset for performance, set later in algorithm
29079 // this.ringOut, this.consumedBy, this.prev
29082 _createClass(Segment, [{
29083 key: "replaceRightSE",
29085 /* When a segment is split, the rightSE is replaced with a new sweep event */
29086 value: function replaceRightSE(newRightSE) {
29087 this.rightSE = newRightSE;
29088 this.rightSE.segment = this;
29089 this.rightSE.otherSE = this.leftSE;
29090 this.leftSE.otherSE = this.rightSE;
29094 value: function bbox() {
29095 var y1 = this.leftSE.point.y;
29096 var y2 = this.rightSE.point.y;
29099 x: this.leftSE.point.x,
29100 y: y1 < y2 ? y1 : y2
29103 x: this.rightSE.point.x,
29104 y: y1 > y2 ? y1 : y2
29108 /* A vector from the left point to the right */
29112 value: function vector() {
29114 x: this.rightSE.point.x - this.leftSE.point.x,
29115 y: this.rightSE.point.y - this.leftSE.point.y
29119 key: "isAnEndpoint",
29120 value: function isAnEndpoint(pt) {
29121 return pt.x === this.leftSE.point.x && pt.y === this.leftSE.point.y || pt.x === this.rightSE.point.x && pt.y === this.rightSE.point.y;
29123 /* Compare this segment with a point.
29125 * A point P is considered to be colinear to a segment if there
29126 * exists a distance D such that if we travel along the segment
29127 * from one * endpoint towards the other a distance D, we find
29128 * ourselves at point P.
29130 * Return value indicates:
29132 * 1: point lies above the segment (to the left of vertical)
29133 * 0: point is colinear to segment
29134 * -1: point lies below the segment (to the right of vertical)
29138 key: "comparePoint",
29139 value: function comparePoint(point) {
29140 if (this.isAnEndpoint(point)) return 0;
29141 var lPt = this.leftSE.point;
29142 var rPt = this.rightSE.point;
29143 var v = this.vector(); // Exactly vertical segments.
29145 if (lPt.x === rPt.x) {
29146 if (point.x === lPt.x) return 0;
29147 return point.x < lPt.x ? 1 : -1;
29148 } // Nearly vertical segments with an intersection.
29149 // Check to see where a point on the line with matching Y coordinate is.
29152 var yDist = (point.y - lPt.y) / v.y;
29153 var xFromYDist = lPt.x + yDist * v.x;
29154 if (point.x === xFromYDist) return 0; // General case.
29155 // Check to see where a point on the line with matching X coordinate is.
29157 var xDist = (point.x - lPt.x) / v.x;
29158 var yFromXDist = lPt.y + xDist * v.y;
29159 if (point.y === yFromXDist) return 0;
29160 return point.y < yFromXDist ? -1 : 1;
29163 * Given another segment, returns the first non-trivial intersection
29164 * between the two segments (in terms of sweep line ordering), if it exists.
29166 * A 'non-trivial' intersection is one that will cause one or both of the
29167 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
29169 * * endpoint of segA with endpoint of segB --> trivial
29170 * * endpoint of segA with point along segB --> non-trivial
29171 * * endpoint of segB with point along segA --> non-trivial
29172 * * point along segA with point along segB --> non-trivial
29174 * If no non-trivial intersection exists, return null
29175 * Else, return null.
29179 key: "getIntersection",
29180 value: function getIntersection(other) {
29181 // If bboxes don't overlap, there can't be any intersections
29182 var tBbox = this.bbox();
29183 var oBbox = other.bbox();
29184 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
29185 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
29186 // This will 'snap' intersections to endpoints if possible, and will
29187 // handle cases of colinearity.
29189 var tlp = this.leftSE.point;
29190 var trp = this.rightSE.point;
29191 var olp = other.leftSE.point;
29192 var orp = other.rightSE.point; // does each endpoint touch the other segment?
29193 // note that we restrict the 'touching' definition to only allow segments
29194 // to touch endpoints that lie forward from where we are in the sweep line pass
29196 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
29197 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
29198 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
29199 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
29201 if (touchesThisLSE && touchesOtherLSE) {
29202 // these two cases are for colinear segments with matching left
29203 // endpoints, and one segment being longer than the other
29204 if (touchesThisRSE && !touchesOtherRSE) return trp;
29205 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
29206 // or just on their left endpoint (one trivial intersection
29209 } // does this left endpoint matches (other doesn't)
29212 if (touchesThisLSE) {
29213 // check for segments that just intersect on opposing endpoints
29214 if (touchesOtherRSE) {
29215 if (tlp.x === orp.x && tlp.y === orp.y) return null;
29216 } // t-intersection on left endpoint
29220 } // does other left endpoint matches (this doesn't)
29223 if (touchesOtherLSE) {
29224 // check for segments that just intersect on opposing endpoints
29225 if (touchesThisRSE) {
29226 if (trp.x === olp.x && trp.y === olp.y) return null;
29227 } // t-intersection on left endpoint
29231 } // trivial intersection on right endpoints
29234 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
29236 if (touchesThisRSE) return trp;
29237 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
29238 // infinite lines laid over the segments
29240 var pt = intersection(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
29241 // they would have an endpoint intersection and that case was already handled above
29243 if (pt === null) return null; // is the intersection found between the lines not on the segments?
29245 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
29247 return rounder.round(pt.x, pt.y);
29250 * Split the given segment into multiple segments on the given points.
29251 * * Each existing segment will retain its leftSE and a new rightSE will be
29252 * generated for it.
29253 * * A new segment will be generated which will adopt the original segment's
29254 * rightSE, and a new leftSE will be generated for it.
29255 * * If there are more than two points given to split on, new segments
29256 * in the middle will be generated with new leftSE and rightSE's.
29257 * * An array of the newly generated SweepEvents will be returned.
29259 * Warning: input array of points is modified
29264 value: function split(point) {
29265 var newEvents = [];
29266 var alreadyLinked = point.events !== undefined;
29267 var newLeftSE = new SweepEvent(point, true);
29268 var newRightSE = new SweepEvent(point, false);
29269 var oldRightSE = this.rightSE;
29270 this.replaceRightSE(newRightSE);
29271 newEvents.push(newRightSE);
29272 newEvents.push(newLeftSE);
29273 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
29274 // sometimes one of the resulting new segments is vertical, in which
29275 // case its left and right events may need to be swapped
29277 if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
29278 newSeg.swapEvents();
29281 if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
29283 } // in the point we just used to create new sweep events with was already
29284 // linked to other events, we need to check if either of the affected
29285 // segments should be consumed
29288 if (alreadyLinked) {
29289 newLeftSE.checkForConsuming();
29290 newRightSE.checkForConsuming();
29295 /* Swap which event is left and right */
29299 value: function swapEvents() {
29300 var tmpEvt = this.rightSE;
29301 this.rightSE = this.leftSE;
29302 this.leftSE = tmpEvt;
29303 this.leftSE.isLeft = true;
29304 this.rightSE.isLeft = false;
29306 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
29307 this.windings[i] *= -1;
29310 /* Consume another segment. We take their rings under our wing
29311 * and mark them as consumed. Use for perfectly overlapping segments */
29315 value: function consume(other) {
29316 var consumer = this;
29317 var consumee = other;
29319 while (consumer.consumedBy) {
29320 consumer = consumer.consumedBy;
29323 while (consumee.consumedBy) {
29324 consumee = consumee.consumedBy;
29327 var cmp = Segment.compare(consumer, consumee);
29328 if (cmp === 0) return; // already consumed
29329 // the winner of the consumption is the earlier segment
29330 // according to sweep line ordering
29333 var tmp = consumer;
29334 consumer = consumee;
29336 } // make sure a segment doesn't consume it's prev
29339 if (consumer.prev === consumee) {
29340 var _tmp = consumer;
29341 consumer = consumee;
29345 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
29346 var ring = consumee.rings[i];
29347 var winding = consumee.windings[i];
29348 var index = consumer.rings.indexOf(ring);
29350 if (index === -1) {
29351 consumer.rings.push(ring);
29352 consumer.windings.push(winding);
29353 } else consumer.windings[index] += winding;
29356 consumee.rings = null;
29357 consumee.windings = null;
29358 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
29360 consumee.leftSE.consumedBy = consumer.leftSE;
29361 consumee.rightSE.consumedBy = consumer.rightSE;
29363 /* The first segment previous segment chain that is in the result */
29366 key: "prevInResult",
29367 value: function prevInResult() {
29368 if (this._prevInResult !== undefined) return this._prevInResult;
29369 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
29370 return this._prevInResult;
29373 key: "beforeState",
29374 value: function beforeState() {
29375 if (this._beforeState !== undefined) return this._beforeState;
29376 if (!this.prev) this._beforeState = {
29381 var seg = this.prev.consumedBy || this.prev;
29382 this._beforeState = seg.afterState();
29384 return this._beforeState;
29388 value: function afterState() {
29389 if (this._afterState !== undefined) return this._afterState;
29390 var beforeState = this.beforeState();
29391 this._afterState = {
29392 rings: beforeState.rings.slice(0),
29393 windings: beforeState.windings.slice(0),
29396 var ringsAfter = this._afterState.rings;
29397 var windingsAfter = this._afterState.windings;
29398 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
29400 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
29401 var ring = this.rings[i];
29402 var winding = this.windings[i];
29403 var index = ringsAfter.indexOf(ring);
29405 if (index === -1) {
29406 ringsAfter.push(ring);
29407 windingsAfter.push(winding);
29408 } else windingsAfter[index] += winding;
29409 } // calcualte polysAfter
29412 var polysAfter = [];
29413 var polysExclude = [];
29415 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
29416 if (windingsAfter[_i] === 0) continue; // non-zero rule
29418 var _ring = ringsAfter[_i];
29419 var poly = _ring.poly;
29420 if (polysExclude.indexOf(poly) !== -1) continue;
29421 if (_ring.isExterior) polysAfter.push(poly);else {
29422 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
29424 var _index = polysAfter.indexOf(_ring.poly);
29426 if (_index !== -1) polysAfter.splice(_index, 1);
29428 } // calculate multiPolysAfter
29431 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
29432 var mp = polysAfter[_i2].multiPoly;
29433 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
29436 return this._afterState;
29438 /* Is this segment part of the final result? */
29442 value: function isInResult() {
29443 // if we've been consumed, we're not in the result
29444 if (this.consumedBy) return false;
29445 if (this._isInResult !== undefined) return this._isInResult;
29446 var mpsBefore = this.beforeState().multiPolys;
29447 var mpsAfter = this.afterState().multiPolys;
29449 switch (operation.type) {
29452 // UNION - included iff:
29453 // * On one side of us there is 0 poly interiors AND
29454 // * On the other side there is 1 or more.
29455 var noBefores = mpsBefore.length === 0;
29456 var noAfters = mpsAfter.length === 0;
29457 this._isInResult = noBefores !== noAfters;
29461 case 'intersection':
29463 // INTERSECTION - included iff:
29464 // * on one side of us all multipolys are rep. with poly interiors AND
29465 // * on the other side of us, not all multipolys are repsented
29466 // with poly interiors
29470 if (mpsBefore.length < mpsAfter.length) {
29471 least = mpsBefore.length;
29472 most = mpsAfter.length;
29474 least = mpsAfter.length;
29475 most = mpsBefore.length;
29478 this._isInResult = most === operation.numMultiPolys && least < most;
29484 // XOR - included iff:
29485 // * the difference between the number of multipolys represented
29486 // with poly interiors on our two sides is an odd number
29487 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
29488 this._isInResult = diff % 2 === 1;
29494 // DIFFERENCE included iff:
29495 // * on exactly one side, we have just the subject
29496 var isJustSubject = function isJustSubject(mps) {
29497 return mps.length === 1 && mps[0].isSubject;
29500 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
29505 throw new Error("Unrecognized operation type found ".concat(operation.type));
29508 return this._isInResult;
29512 value: function fromRing(pt1, pt2, ring) {
29513 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
29515 var cmpPts = SweepEvent.comparePoints(pt1, pt2);
29521 } else if (cmpPts > 0) {
29525 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
29527 var leftSE = new SweepEvent(leftPt, true);
29528 var rightSE = new SweepEvent(rightPt, false);
29529 return new Segment(leftSE, rightSE, [ring], [winding]);
29536 var RingIn = /*#__PURE__*/function () {
29537 function RingIn(geomRing, poly, isExterior) {
29538 _classCallCheck(this, RingIn);
29540 if (!Array.isArray(geomRing) || geomRing.length === 0) {
29541 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29545 this.isExterior = isExterior;
29546 this.segments = [];
29548 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
29549 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29552 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
29563 var prevPoint = firstPoint;
29565 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
29566 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
29567 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29570 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
29572 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
29573 this.segments.push(Segment.fromRing(prevPoint, point, this));
29574 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
29575 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
29576 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
29577 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
29579 } // add segment from last to first if last is not the same as first
29582 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
29583 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
29587 _createClass(RingIn, [{
29588 key: "getSweepEvents",
29589 value: function getSweepEvents() {
29590 var sweepEvents = [];
29592 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
29593 var segment = this.segments[i];
29594 sweepEvents.push(segment.leftSE);
29595 sweepEvents.push(segment.rightSE);
29598 return sweepEvents;
29605 var PolyIn = /*#__PURE__*/function () {
29606 function PolyIn(geomPoly, multiPoly) {
29607 _classCallCheck(this, PolyIn);
29609 if (!Array.isArray(geomPoly)) {
29610 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29613 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
29617 x: this.exteriorRing.bbox.ll.x,
29618 y: this.exteriorRing.bbox.ll.y
29621 x: this.exteriorRing.bbox.ur.x,
29622 y: this.exteriorRing.bbox.ur.y
29625 this.interiorRings = [];
29627 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
29628 var ring = new RingIn(geomPoly[i], this, false);
29629 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
29630 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
29631 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
29632 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
29633 this.interiorRings.push(ring);
29636 this.multiPoly = multiPoly;
29639 _createClass(PolyIn, [{
29640 key: "getSweepEvents",
29641 value: function getSweepEvents() {
29642 var sweepEvents = this.exteriorRing.getSweepEvents();
29644 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29645 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
29647 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
29648 sweepEvents.push(ringSweepEvents[j]);
29652 return sweepEvents;
29659 var MultiPolyIn = /*#__PURE__*/function () {
29660 function MultiPolyIn(geom, isSubject) {
29661 _classCallCheck(this, MultiPolyIn);
29663 if (!Array.isArray(geom)) {
29664 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
29668 // if the input looks like a polygon, convert it to a multipolygon
29669 if (typeof geom[0][0][0] === 'number') geom = [geom];
29670 } catch (ex) {// The input is either malformed or has empty arrays.
29671 // In either case, it will be handled later on.
29677 x: Number.POSITIVE_INFINITY,
29678 y: Number.POSITIVE_INFINITY
29681 x: Number.NEGATIVE_INFINITY,
29682 y: Number.NEGATIVE_INFINITY
29686 for (var i = 0, iMax = geom.length; i < iMax; i++) {
29687 var poly = new PolyIn(geom[i], this);
29688 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
29689 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
29690 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
29691 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
29692 this.polys.push(poly);
29695 this.isSubject = isSubject;
29698 _createClass(MultiPolyIn, [{
29699 key: "getSweepEvents",
29700 value: function getSweepEvents() {
29701 var sweepEvents = [];
29703 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29704 var polySweepEvents = this.polys[i].getSweepEvents();
29706 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
29707 sweepEvents.push(polySweepEvents[j]);
29711 return sweepEvents;
29715 return MultiPolyIn;
29718 var RingOut = /*#__PURE__*/function () {
29719 _createClass(RingOut, null, [{
29722 /* Given the segments from the sweep line pass, compute & return a series
29723 * of closed rings from all the segments marked to be part of the result */
29724 value: function factory(allSegments) {
29727 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
29728 var segment = allSegments[i];
29729 if (!segment.isInResult() || segment.ringOut) continue;
29730 var prevEvent = null;
29731 var event = segment.leftSE;
29732 var nextEvent = segment.rightSE;
29733 var events = [event];
29734 var startingPoint = event.point;
29735 var intersectionLEs = [];
29736 /* Walk the chain of linked events to form a closed ring */
29741 events.push(event);
29742 /* Is the ring complete? */
29744 if (event.point === startingPoint) break;
29747 var availableLEs = event.getAvailableLinkedEvents();
29748 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
29749 * part of the algorithm malfunctioned... please file a bug report. */
29751 if (availableLEs.length === 0) {
29752 var firstPt = events[0].point;
29753 var lastPt = events[events.length - 1].point;
29754 throw new Error("Unable to complete output ring starting at [".concat(firstPt.x, ",") + " ".concat(firstPt.y, "]. Last matching segment found ends at") + " [".concat(lastPt.x, ", ").concat(lastPt.y, "]."));
29756 /* Only one way to go, so cotinue on the path */
29759 if (availableLEs.length === 1) {
29760 nextEvent = availableLEs[0].otherSE;
29763 /* We must have an intersection. Check for a completed loop */
29766 var indexLE = null;
29768 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
29769 if (intersectionLEs[j].point === event.point) {
29774 /* Found a completed loop. Cut that off and make a ring */
29777 if (indexLE !== null) {
29778 var intersectionLE = intersectionLEs.splice(indexLE)[0];
29779 var ringEvents = events.splice(intersectionLE.index);
29780 ringEvents.unshift(ringEvents[0].otherSE);
29781 ringsOut.push(new RingOut(ringEvents.reverse()));
29784 /* register the intersection */
29787 intersectionLEs.push({
29788 index: events.length,
29791 /* Choose the left-most option to continue the walk */
29793 var comparator = event.getLeftmostComparator(prevEvent);
29794 nextEvent = availableLEs.sort(comparator)[0].otherSE;
29799 ringsOut.push(new RingOut(events));
29806 function RingOut(events) {
29807 _classCallCheck(this, RingOut);
29809 this.events = events;
29811 for (var i = 0, iMax = events.length; i < iMax; i++) {
29812 events[i].segment.ringOut = this;
29818 _createClass(RingOut, [{
29820 value: function getGeom() {
29821 // Remove superfluous points (ie extra points along a straight line),
29822 var prevPt = this.events[0].point;
29823 var points = [prevPt];
29825 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
29826 var _pt = this.events[i].point;
29827 var _nextPt = this.events[i + 1].point;
29828 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
29831 } // ring was all (within rounding error of angle calc) colinear points
29834 if (points.length === 1) return null; // check if the starting point is necessary
29836 var pt = points[0];
29837 var nextPt = points[1];
29838 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
29839 points.push(points[0]);
29840 var step = this.isExteriorRing() ? 1 : -1;
29841 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
29842 var iEnd = this.isExteriorRing() ? points.length : -1;
29843 var orderedPoints = [];
29845 for (var _i = iStart; _i != iEnd; _i += step) {
29846 orderedPoints.push([points[_i].x, points[_i].y]);
29849 return orderedPoints;
29852 key: "isExteriorRing",
29853 value: function isExteriorRing() {
29854 if (this._isExteriorRing === undefined) {
29855 var enclosing = this.enclosingRing();
29856 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
29859 return this._isExteriorRing;
29862 key: "enclosingRing",
29863 value: function enclosingRing() {
29864 if (this._enclosingRing === undefined) {
29865 this._enclosingRing = this._calcEnclosingRing();
29868 return this._enclosingRing;
29870 /* Returns the ring that encloses this one, if any */
29873 key: "_calcEnclosingRing",
29874 value: function _calcEnclosingRing() {
29875 // start with the ealier sweep line event so that the prevSeg
29876 // chain doesn't lead us inside of a loop of ours
29877 var leftMostEvt = this.events[0];
29879 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
29880 var evt = this.events[i];
29881 if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
29884 var prevSeg = leftMostEvt.segment.prevInResult();
29885 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29888 // no segment found, thus no ring can enclose us
29889 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
29890 // segment must loop back around and enclose us
29892 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
29893 // segment must either loop around us or the ring of the prev prev
29894 // seg, which would make us and the ring of the prev peers
29896 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
29897 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
29898 return prevSeg.ringOut;
29899 } else return prevSeg.ringOut.enclosingRing();
29900 } // two segments are from the same ring, so this was a penisula
29901 // of that ring. iterate downward, keep searching
29904 prevSeg = prevPrevSeg.prevInResult();
29905 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
29913 var PolyOut = /*#__PURE__*/function () {
29914 function PolyOut(exteriorRing) {
29915 _classCallCheck(this, PolyOut);
29917 this.exteriorRing = exteriorRing;
29918 exteriorRing.poly = this;
29919 this.interiorRings = [];
29922 _createClass(PolyOut, [{
29923 key: "addInterior",
29924 value: function addInterior(ring) {
29925 this.interiorRings.push(ring);
29930 value: function getGeom() {
29931 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
29933 if (geom[0] === null) return null;
29935 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
29936 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
29938 if (ringGeom === null) continue;
29939 geom.push(ringGeom);
29949 var MultiPolyOut = /*#__PURE__*/function () {
29950 function MultiPolyOut(rings) {
29951 _classCallCheck(this, MultiPolyOut);
29953 this.rings = rings;
29954 this.polys = this._composePolys(rings);
29957 _createClass(MultiPolyOut, [{
29959 value: function getGeom() {
29962 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
29963 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
29965 if (polyGeom === null) continue;
29966 geom.push(polyGeom);
29972 key: "_composePolys",
29973 value: function _composePolys(rings) {
29976 for (var i = 0, iMax = rings.length; i < iMax; i++) {
29977 var ring = rings[i];
29978 if (ring.poly) continue;
29979 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
29980 var enclosingRing = ring.enclosingRing();
29981 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
29982 enclosingRing.poly.addInterior(ring);
29990 return MultiPolyOut;
29993 * NOTE: We must be careful not to change any segments while
29994 * they are in the SplayTree. AFAIK, there's no way to tell
29995 * the tree to rebalance itself - thus before splitting
29996 * a segment that's in the tree, we remove it from the tree,
29997 * do the split, then re-insert it. (Even though splitting a
29998 * segment *shouldn't* change its correct position in the
29999 * sweep line tree, the reality is because of rounding errors,
30000 * it sometimes does.)
30004 var SweepLine = /*#__PURE__*/function () {
30005 function SweepLine(queue) {
30006 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
30008 _classCallCheck(this, SweepLine);
30010 this.queue = queue;
30011 this.tree = new Tree(comparator);
30012 this.segments = [];
30015 _createClass(SweepLine, [{
30017 value: function process(event) {
30018 var segment = event.segment;
30019 var newEvents = []; // if we've already been consumed by another segment,
30020 // clean up our body parts and get out
30022 if (event.consumedBy) {
30023 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
30027 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
30028 if (!node) throw new Error("Unable to find segment #".concat(segment.id, " ") + "[".concat(segment.leftSE.point.x, ", ").concat(segment.leftSE.point.y, "] -> ") + "[".concat(segment.rightSE.point.x, ", ").concat(segment.rightSE.point.y, "] ") + 'in SweepLine tree. Please submit a bug report.');
30029 var prevNode = node;
30030 var nextNode = node;
30031 var prevSeg = undefined;
30032 var nextSeg = undefined; // skip consumed segments still in tree
30034 while (prevSeg === undefined) {
30035 prevNode = this.tree.prev(prevNode);
30036 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
30037 } // skip consumed segments still in tree
30040 while (nextSeg === undefined) {
30041 nextNode = this.tree.next(nextNode);
30042 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
30045 if (event.isLeft) {
30046 // Check for intersections against the previous segment in the sweep line
30047 var prevMySplitter = null;
30050 var prevInter = prevSeg.getIntersection(segment);
30052 if (prevInter !== null) {
30053 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
30055 if (!prevSeg.isAnEndpoint(prevInter)) {
30056 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
30058 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
30059 newEvents.push(newEventsFromSplit[i]);
30063 } // Check for intersections against the next segment in the sweep line
30066 var nextMySplitter = null;
30069 var nextInter = nextSeg.getIntersection(segment);
30071 if (nextInter !== null) {
30072 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
30074 if (!nextSeg.isAnEndpoint(nextInter)) {
30075 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
30077 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
30078 newEvents.push(_newEventsFromSplit[_i]);
30082 } // For simplicity, even if we find more than one intersection we only
30083 // spilt on the 'earliest' (sweep-line style) of the intersections.
30084 // The other intersection will be handled in a future process().
30087 if (prevMySplitter !== null || nextMySplitter !== null) {
30088 var mySplitter = null;
30089 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
30090 var cmpSplitters = SweepEvent.comparePoints(prevMySplitter, nextMySplitter);
30091 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
30092 } // Rounding errors can cause changes in ordering,
30093 // so remove afected segments and right sweep events before splitting
30095 this.queue.remove(segment.rightSE);
30096 newEvents.push(segment.rightSE);
30098 var _newEventsFromSplit2 = segment.split(mySplitter);
30100 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
30101 newEvents.push(_newEventsFromSplit2[_i2]);
30105 if (newEvents.length > 0) {
30106 // We found some intersections, so re-do the current event to
30107 // make sure sweep line ordering is totally consistent for later
30108 // use with the segment 'prev' pointers
30109 this.tree.remove(segment);
30110 newEvents.push(event);
30112 // done with left event
30113 this.segments.push(segment);
30114 segment.prev = prevSeg;
30118 // since we're about to be removed from the sweep line, check for
30119 // intersections between our previous and next segments
30120 if (prevSeg && nextSeg) {
30121 var inter = prevSeg.getIntersection(nextSeg);
30123 if (inter !== null) {
30124 if (!prevSeg.isAnEndpoint(inter)) {
30125 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
30127 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
30128 newEvents.push(_newEventsFromSplit3[_i3]);
30132 if (!nextSeg.isAnEndpoint(inter)) {
30133 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
30135 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
30136 newEvents.push(_newEventsFromSplit4[_i4]);
30142 this.tree.remove(segment);
30147 /* Safely split a segment that is currently in the datastructures
30148 * IE - a segment other than the one that is currently being processed. */
30151 key: "_splitSafely",
30152 value: function _splitSafely(seg, pt) {
30153 // Rounding errors can cause changes in ordering,
30154 // so remove afected segments and right sweep events before splitting
30155 // removeNode() doesn't work, so have re-find the seg
30156 // https://github.com/w8r/splay-tree/pull/5
30157 this.tree.remove(seg);
30158 var rightSE = seg.rightSE;
30159 this.queue.remove(rightSE);
30160 var newEvents = seg.split(pt);
30161 newEvents.push(rightSE); // splitting can trigger consumption
30163 if (seg.consumedBy === undefined) this.tree.insert(seg);
30171 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
30172 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
30174 var Operation = /*#__PURE__*/function () {
30175 function Operation() {
30176 _classCallCheck(this, Operation);
30179 _createClass(Operation, [{
30181 value: function run(type, geom, moreGeoms) {
30182 operation.type = type;
30184 /* Convert inputs to MultiPoly objects */
30186 var multipolys = [new MultiPolyIn(geom, true)];
30188 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
30189 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
30192 operation.numMultiPolys = multipolys.length;
30193 /* BBox optimization for difference operation
30194 * If the bbox of a multipolygon that's part of the clipping doesn't
30195 * intersect the bbox of the subject at all, we can just drop that
30198 if (operation.type === 'difference') {
30199 // in place removal
30200 var subject = multipolys[0];
30203 while (_i < multipolys.length) {
30204 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
30207 /* BBox optimization for intersection operation
30208 * If we can find any pair of multipolygons whose bbox does not overlap,
30209 * then the result will be empty. */
30212 if (operation.type === 'intersection') {
30213 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
30214 // it could be optimized to O(n * ln(n))
30215 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
30216 var mpA = multipolys[_i2];
30218 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
30219 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
30223 /* Put segment endpoints in a priority queue */
30226 var queue = new Tree(SweepEvent.compare);
30228 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
30229 var sweepEvents = multipolys[_i3].getSweepEvents();
30231 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
30232 queue.insert(sweepEvents[_j]);
30234 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30235 // prevents an infinite loop, an otherwise common manifestation of bugs
30236 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
30240 /* Pass the sweep line over those endpoints */
30243 var sweepLine = new SweepLine(queue);
30244 var prevQueueSize = queue.size;
30245 var node = queue.pop();
30248 var evt = node.key;
30250 if (queue.size === prevQueueSize) {
30251 // prevents an infinite loop, an otherwise common manifestation of bugs
30252 var seg = evt.segment;
30253 throw new Error("Unable to pop() ".concat(evt.isLeft ? 'left' : 'right', " SweepEvent ") + "[".concat(evt.point.x, ", ").concat(evt.point.y, "] from segment #").concat(seg.id, " ") + "[".concat(seg.leftSE.point.x, ", ").concat(seg.leftSE.point.y, "] -> ") + "[".concat(seg.rightSE.point.x, ", ").concat(seg.rightSE.point.y, "] from queue. ") + 'Please file a bug report.');
30256 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
30257 // prevents an infinite loop, an otherwise common manifestation of bugs
30258 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
30261 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
30262 // prevents an infinite loop, an otherwise common manifestation of bugs
30263 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
30266 var newEvents = sweepLine.process(evt);
30268 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
30269 var _evt = newEvents[_i4];
30270 if (_evt.consumedBy === undefined) queue.insert(_evt);
30273 prevQueueSize = queue.size;
30274 node = queue.pop();
30275 } // free some memory we don't need anymore
30279 /* Collect and compile segments we're keeping into a multipolygon */
30281 var ringsOut = RingOut.factory(sweepLine.segments);
30282 var result = new MultiPolyOut(ringsOut);
30283 return result.getGeom();
30288 }(); // singleton available by import
30291 var operation = new Operation();
30293 var union = function union(geom) {
30294 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
30295 moreGeoms[_key - 1] = arguments[_key];
30298 return operation.run('union', geom, moreGeoms);
30301 var intersection$1 = function intersection(geom) {
30302 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
30303 moreGeoms[_key2 - 1] = arguments[_key2];
30306 return operation.run('intersection', geom, moreGeoms);
30309 var xor = function xor(geom) {
30310 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
30311 moreGeoms[_key3 - 1] = arguments[_key3];
30314 return operation.run('xor', geom, moreGeoms);
30317 var difference = function difference(subjectGeom) {
30318 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
30319 clippingGeoms[_key4 - 1] = arguments[_key4];
30322 return operation.run('difference', subjectGeom, clippingGeoms);
30327 intersection: intersection$1,
30329 difference: difference
30332 var geojsonPrecision = createCommonjsModule(function (module) {
30334 function parse(t, coordinatePrecision, extrasPrecision) {
30335 function point(p) {
30336 return p.map(function (e, index) {
30338 return 1 * e.toFixed(coordinatePrecision);
30340 return 1 * e.toFixed(extrasPrecision);
30345 function multi(l) {
30346 return l.map(point);
30350 return p.map(multi);
30353 function multiPoly(m) {
30354 return m.map(poly);
30357 function geometry(obj) {
30362 switch (obj.type) {
30364 obj.coordinates = point(obj.coordinates);
30369 obj.coordinates = multi(obj.coordinates);
30373 case "MultiLineString":
30374 obj.coordinates = poly(obj.coordinates);
30377 case "MultiPolygon":
30378 obj.coordinates = multiPoly(obj.coordinates);
30381 case "GeometryCollection":
30382 obj.geometries = obj.geometries.map(geometry);
30390 function feature(obj) {
30391 obj.geometry = geometry(obj.geometry);
30395 function featureCollection(f) {
30396 f.features = f.features.map(feature);
30400 function geometryCollection(g) {
30401 g.geometries = g.geometries.map(geometry);
30413 case "GeometryCollection":
30414 return geometryCollection(t);
30416 case "FeatureCollection":
30417 return featureCollection(t);
30423 case "MultiPolygon":
30424 case "MultiLineString":
30425 return geometry(t);
30432 module.exports = parse;
30433 module.exports.parse = parse;
30437 var FORCED$3 = fails(function () {
30438 return new Date(NaN).toJSON() !== null
30439 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
30442 // `Date.prototype.toJSON` method
30443 // https://tc39.es/ecma262/#sec-date.prototype.tojson
30444 _export({ target: 'Date', proto: true, forced: FORCED$3 }, {
30445 // eslint-disable-next-line no-unused-vars -- required for `.length`
30446 toJSON: function toJSON(key) {
30447 var O = toObject(this);
30448 var pv = toPrimitive(O);
30449 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
30453 // `URL.prototype.toJSON` method
30454 // https://url.spec.whatwg.org/#dom-url-tojson
30455 _export({ target: 'URL', proto: true, enumerable: true }, {
30456 toJSON: function toJSON() {
30457 return URL.prototype.toString.call(this);
30461 function isObject$3(obj) {
30462 return _typeof(obj) === 'object' && obj !== null;
30465 function forEach(obj, cb) {
30466 if (Array.isArray(obj)) {
30468 } else if (isObject$3(obj)) {
30469 Object.keys(obj).forEach(function (key) {
30470 var val = obj[key];
30476 function getTreeDepth(obj) {
30479 if (Array.isArray(obj) || isObject$3(obj)) {
30480 forEach(obj, function (val) {
30481 if (Array.isArray(val) || isObject$3(val)) {
30482 var tmpDepth = getTreeDepth(val);
30484 if (tmpDepth > depth) {
30495 function stringify(obj, options) {
30496 options = options || {};
30497 var indent = JSON.stringify([1], null, get(options, 'indent', 2)).slice(2, -3);
30498 var addMargin = get(options, 'margins', false);
30499 var addArrayMargin = get(options, 'arrayMargins', false);
30500 var addObjectMargin = get(options, 'objectMargins', false);
30501 var maxLength = indent === '' ? Infinity : get(options, 'maxLength', 80);
30502 var maxNesting = get(options, 'maxNesting', Infinity);
30503 return function _stringify(obj, currentIndent, reserved) {
30504 if (obj && typeof obj.toJSON === 'function') {
30505 obj = obj.toJSON();
30508 var string = JSON.stringify(obj);
30510 if (string === undefined) {
30514 var length = maxLength - currentIndent.length - reserved;
30515 var treeDepth = getTreeDepth(obj);
30517 if (treeDepth <= maxNesting && string.length <= length) {
30518 var prettified = prettify(string, {
30519 addMargin: addMargin,
30520 addArrayMargin: addArrayMargin,
30521 addObjectMargin: addObjectMargin
30524 if (prettified.length <= length) {
30529 if (isObject$3(obj)) {
30530 var nextIndent = currentIndent + indent;
30534 var comma = function comma(array, index) {
30535 return index === array.length - 1 ? 0 : 1;
30538 if (Array.isArray(obj)) {
30539 for (var index = 0; index < obj.length; index++) {
30540 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
30545 Object.keys(obj).forEach(function (key, index, array) {
30546 var keyPart = JSON.stringify(key) + ': ';
30548 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
30550 if (value !== undefined) {
30551 items.push(keyPart + value);
30557 if (items.length > 0) {
30558 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
30564 } // Note: This regex matches even invalid JSON strings, but since we’re
30565 // working on the output of `JSON.stringify` we know that only valid strings
30566 // are present (unless the user supplied a weird `options.indent` but in
30567 // that case we don’t care since the output would be invalid anyway).
30570 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
30572 function prettify(string, options) {
30573 options = options || {};
30583 if (options.addMargin || options.addObjectMargin) {
30584 tokens['{'] = '{ ';
30585 tokens['}'] = ' }';
30588 if (options.addMargin || options.addArrayMargin) {
30589 tokens['['] = '[ ';
30590 tokens[']'] = ' ]';
30593 return string.replace(stringOrChar, function (match, string) {
30594 return string ? match : tokens[match];
30598 function get(options, name, defaultValue) {
30599 return name in options ? options[name] : defaultValue;
30602 var jsonStringifyPrettyCompact = stringify;
30604 var _default = /*#__PURE__*/function () {
30607 // `fc` Optional FeatureCollection of known features
30609 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
30610 // Each feature must have a filename-like `id`, for example: `something.geojson`
30613 // "type": "FeatureCollection"
30616 // "type": "Feature",
30617 // "id": "philly_metro.geojson",
30618 // "properties": { … },
30619 // "geometry": { … }
30623 function _default(fc) {
30626 _classCallCheck$1(this, _default);
30628 // The _cache retains resolved features, so if you ask for the same thing multiple times
30629 // we don't repeat the expensive resolving/clipping operations.
30631 // Each feature has a stable identifier that is used as the cache key.
30632 // The identifiers look like:
30633 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
30634 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
30635 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
30636 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
30637 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
30638 // When strict mode = false, return `null` for invalid locations or locationSets.
30640 this._strict = true; // process input FeatureCollection
30642 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
30643 fc.features.forEach(function (feature) {
30644 feature.properties = feature.properties || {};
30645 var props = feature.properties; // Get `id` from either `id` or `properties`
30647 var id = feature.id || props.id;
30648 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
30650 id = id.toLowerCase();
30652 props.id = id; // Ensure `area` property exists
30655 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
30657 props.area = Number(area.toFixed(2));
30660 _this._cache[id] = feature;
30662 } // Replace CountryCoder world geometry to be a polygon covering the world.
30665 var world = _cloneDeep(feature$1('Q2'));
30669 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
30672 world.properties.id = 'Q2';
30673 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
30675 this._cache.Q2 = world;
30676 } // validateLocation
30677 // `location` The location to validate
30679 // Pass a `location` value to validate
30681 // Returns a result like:
30683 // type: 'point', 'geojson', or 'countrycoder'
30684 // location: the queried location
30685 // id: the stable identifier for the feature
30687 // or `null` if the location is invalid
30691 _createClass$1(_default, [{
30692 key: "validateLocation",
30693 value: function validateLocation(location) {
30694 if (Array.isArray(location) && (location.length === 2 || location.length === 3)) {
30695 // [lon, lat] or [lon, lat, radius] point?
30696 var lon = location[0];
30697 var lat = location[1];
30698 var radius = location[2];
30700 if (Number.isFinite(lon) && lon >= -180 && lon <= 180 && Number.isFinite(lat) && lat >= -90 && lat <= 90 && (location.length === 2 || Number.isFinite(radius) && radius > 0)) {
30701 var id = '[' + location.toString() + ']';
30704 location: location,
30708 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
30709 // a .geojson filename?
30710 var _id = location.toLowerCase();
30712 if (this._cache[_id]) {
30715 location: location,
30719 } else if (typeof location === 'string' || typeof location === 'number') {
30720 // a country-coder value?
30721 var feature = feature$1(location);
30724 // Use wikidata QID as the identifier, since that seems to be the one
30725 // property that everything in CountryCoder is guaranteed to have.
30726 var _id2 = feature.properties.wikidata;
30728 type: 'countrycoder',
30729 location: location,
30735 if (this._strict) {
30736 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
30740 } // resolveLocation
30741 // `location` The location to resolve
30743 // Pass a `location` value to resolve
30745 // Returns a result like:
30747 // type: 'point', 'geojson', or 'countrycoder'
30748 // location: the queried location
30749 // id: a stable identifier for the feature
30750 // feature: the resolved GeoJSON feature
30752 // or `null` if the location is invalid
30756 key: "resolveLocation",
30757 value: function resolveLocation(location) {
30758 var valid = this.validateLocation(location);
30759 if (!valid) return null;
30760 var id = valid.id; // Return a result from cache if we can
30762 if (this._cache[id]) {
30763 return Object.assign(valid, {
30764 feature: this._cache[id]
30766 } // A [lon,lat] coordinate pair?
30769 if (valid.type === 'point') {
30770 var lon = location[0];
30771 var lat = location[1];
30772 var radius = location[2] || 25; // km
30776 var area = Math.PI * radius * radius;
30777 var feature = this._cache[id] = geojsonPrecision({
30782 area: Number(area.toFixed(2))
30784 geometry: circleToPolygon([lon, lat], radius * 1000, EDGES) // km to m
30787 return Object.assign(valid, {
30789 }); // A .geojson filename?
30790 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
30791 var _feature = _cloneDeep(feature$1(id));
30793 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
30794 // CountryCoder includes higher level features which are made up of members.
30795 // These features don't have their own geometry, but CountryCoder provides an
30796 // `aggregateFeature` method to combine these members into a MultiPolygon.
30797 // In the past, Turf/JSTS/martinez could not handle the aggregated features,
30798 // so we'd iteratively union them all together. (this was slow)
30799 // But now mfogel/polygon-clipping handles these MultiPolygons like a boss.
30800 // This approach also has the benefit of removing all the internal boaders and
30801 // simplifying the regional polygons a lot.
30803 if (Array.isArray(props.members)) {
30804 var aggregate = aggregateFeature(id);
30805 aggregate.geometry.coordinates = _clip([aggregate], 'UNION').geometry.coordinates;
30806 _feature.geometry = aggregate.geometry;
30807 } // Ensure `area` property exists
30811 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
30814 props.area = Number(_area.toFixed(2));
30815 } // Ensure `id` property exists
30820 this._cache[id] = _feature;
30821 return Object.assign(valid, {
30826 if (this._strict) {
30827 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
30831 } // validateLocationSet
30832 // `locationSet` the locationSet to validate
30834 // Pass a locationSet Object to validate like:
30836 // include: [ Array of locations ],
30837 // exclude: [ Array of locations ]
30840 // Returns a result like:
30842 // type: 'locationset'
30843 // locationSet: the queried locationSet
30844 // id: the stable identifier for the feature
30846 // or `null` if the locationSet is invalid
30850 key: "validateLocationSet",
30851 value: function validateLocationSet(locationSet) {
30852 locationSet = locationSet || {};
30853 var validator = this.validateLocation.bind(this);
30854 var include = (locationSet.include || []).map(validator).filter(Boolean);
30855 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
30857 if (!include.length) {
30858 if (this._strict) {
30859 throw new Error("validateLocationSet: LocationSet includes nothing.");
30861 // non-strict mode, replace an empty locationSet with one that includes "the world"
30862 locationSet.include = ['Q2'];
30864 type: 'countrycoder',
30869 } // Generate stable identifier
30872 include.sort(_sortLocations);
30873 var id = '+[' + include.map(function (d) {
30875 }).join(',') + ']';
30877 if (exclude.length) {
30878 exclude.sort(_sortLocations);
30879 id += '-[' + exclude.map(function (d) {
30881 }).join(',') + ']';
30885 type: 'locationset',
30886 locationSet: locationSet,
30889 } // resolveLocationSet
30890 // `locationSet` the locationSet to resolve
30892 // Pass a locationSet Object to validate like:
30894 // include: [ Array of locations ],
30895 // exclude: [ Array of locations ]
30898 // Returns a result like:
30900 // type: 'locationset'
30901 // locationSet: the queried locationSet
30902 // id: the stable identifier for the feature
30903 // feature: the resolved GeoJSON feature
30905 // or `null` if the locationSet is invalid
30909 key: "resolveLocationSet",
30910 value: function resolveLocationSet(locationSet) {
30911 locationSet = locationSet || {};
30912 var valid = this.validateLocationSet(locationSet);
30913 if (!valid) return null;
30914 var id = valid.id; // Return a result from cache if we can
30916 if (this._cache[id]) {
30917 return Object.assign(valid, {
30918 feature: this._cache[id]
30922 var resolver = this.resolveLocation.bind(this);
30923 var includes = (locationSet.include || []).map(resolver).filter(Boolean);
30924 var excludes = (locationSet.exclude || []).map(resolver).filter(Boolean); // Return quickly if it's a single included location..
30926 if (includes.length === 1 && excludes.length === 0) {
30927 return Object.assign(valid, {
30928 feature: includes[0].feature
30930 } // Calculate unions
30933 var includeGeoJSON = _clip(includes.map(function (d) {
30937 var excludeGeoJSON = _clip(excludes.map(function (d) {
30939 }), 'UNION'); // Calculate difference, update `area` and return result
30942 var resultGeoJSON = excludeGeoJSON ? _clip([includeGeoJSON, excludeGeoJSON], 'DIFFERENCE') : includeGeoJSON;
30943 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
30945 resultGeoJSON.id = id;
30946 resultGeoJSON.properties = {
30948 area: Number(area.toFixed(2))
30950 this._cache[id] = resultGeoJSON;
30951 return Object.assign(valid, {
30952 feature: resultGeoJSON
30959 value: function strict(val) {
30960 if (val === undefined) {
30962 return this._strict;
30965 this._strict = val;
30969 // convenience method to access the internal cache
30973 value: function cache() {
30974 return this._cache;
30976 // convenience method to prettyStringify the given object
30980 value: function stringify(obj, options) {
30981 return jsonStringifyPrettyCompact(obj, options);
30986 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
30988 function _clip(features, which) {
30989 if (!Array.isArray(features) || !features.length) return null;
30991 UNION: index.union,
30992 DIFFERENCE: index.difference
30994 var args = features.map(function (feature) {
30995 return feature.geometry.coordinates;
30997 var coords = fn.apply(null, args);
31002 type: whichType(coords),
31003 coordinates: coords
31005 }; // is this a Polygon or a MultiPolygon?
31007 function whichType(coords) {
31008 var a = Array.isArray(coords);
31009 var b = a && Array.isArray(coords[0]);
31010 var c = b && Array.isArray(coords[0][0]);
31011 var d = c && Array.isArray(coords[0][0][0]);
31012 return d ? 'MultiPolygon' : 'Polygon';
31016 function _cloneDeep(obj) {
31017 return JSON.parse(JSON.stringify(obj));
31018 } // Sorting the location lists is ok because they end up unioned together.
31019 // This sorting makes it possible to generate a deterministic id.
31022 function _sortLocations(a, b) {
31028 var aRank = rank[a.type];
31029 var bRank = rank[b.type];
31030 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
31033 // `Number.MAX_SAFE_INTEGER` constant
31034 // https://tc39.es/ecma262/#sec-number.max_safe_integer
31035 _export({ target: 'Number', stat: true }, {
31036 MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
31039 var aesJs = createCommonjsModule(function (module, exports) {
31042 function checkInt(value) {
31043 return parseInt(value) === value;
31046 function checkInts(arrayish) {
31047 if (!checkInt(arrayish.length)) {
31051 for (var i = 0; i < arrayish.length; i++) {
31052 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
31060 function coerceArray(arg, copy) {
31061 // ArrayBuffer view
31062 if (arg.buffer && arg.name === 'Uint8Array') {
31067 arg = Array.prototype.slice.call(arg);
31072 } // It's an array; check it is a valid representation of a byte
31075 if (Array.isArray(arg)) {
31076 if (!checkInts(arg)) {
31077 throw new Error('Array contains invalid value: ' + arg);
31080 return new Uint8Array(arg);
31081 } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
31084 if (checkInt(arg.length) && checkInts(arg)) {
31085 return new Uint8Array(arg);
31088 throw new Error('unsupported array-like object');
31091 function createArray(length) {
31092 return new Uint8Array(length);
31095 function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
31096 if (sourceStart != null || sourceEnd != null) {
31097 if (sourceArray.slice) {
31098 sourceArray = sourceArray.slice(sourceStart, sourceEnd);
31100 sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
31104 targetArray.set(sourceArray, targetStart);
31107 var convertUtf8 = function () {
31108 function toBytes(text) {
31111 text = encodeURI(text);
31113 while (i < text.length) {
31114 var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
31117 result.push(parseInt(text.substr(i, 2), 16));
31118 i += 2; // otherwise, just the actual byte
31124 return coerceArray(result);
31127 function fromBytes(bytes) {
31131 while (i < bytes.length) {
31135 result.push(String.fromCharCode(c));
31137 } else if (c > 191 && c < 224) {
31138 result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
31141 result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
31146 return result.join('');
31151 fromBytes: fromBytes
31155 var convertHex = function () {
31156 function toBytes(text) {
31159 for (var i = 0; i < text.length; i += 2) {
31160 result.push(parseInt(text.substr(i, 2), 16));
31164 } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
31167 var Hex = '0123456789abcdef';
31169 function fromBytes(bytes) {
31172 for (var i = 0; i < bytes.length; i++) {
31174 result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
31177 return result.join('');
31182 fromBytes: fromBytes
31184 }(); // Number of rounds by keysize
31187 var numberOfRounds = {
31191 }; // Round constant words
31193 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)
31195 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];
31196 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
31198 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];
31199 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];
31200 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];
31201 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
31203 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];
31204 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];
31205 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];
31206 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
31208 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];
31209 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];
31210 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];
31211 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];
31213 function convertToInt32(bytes) {
31216 for (var i = 0; i < bytes.length; i += 4) {
31217 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
31223 var AES = function AES(key) {
31224 if (!(this instanceof AES)) {
31225 throw Error('AES must be instanitated with `new`');
31228 Object.defineProperty(this, 'key', {
31229 value: coerceArray(key, true)
31235 AES.prototype._prepare = function () {
31236 var rounds = numberOfRounds[this.key.length];
31238 if (rounds == null) {
31239 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
31240 } // encryption round keys
31243 this._Ke = []; // decryption round keys
31247 for (var i = 0; i <= rounds; i++) {
31248 this._Ke.push([0, 0, 0, 0]);
31250 this._Kd.push([0, 0, 0, 0]);
31253 var roundKeyCount = (rounds + 1) * 4;
31254 var KC = this.key.length / 4; // convert the key into ints
31256 var tk = convertToInt32(this.key); // copy values into round key arrays
31260 for (var i = 0; i < KC; i++) {
31262 this._Ke[index][i % 4] = tk[i];
31263 this._Kd[rounds - index][i % 4] = tk[i];
31264 } // key expansion (fips-197 section 5.2)
31267 var rconpointer = 0;
31271 while (t < roundKeyCount) {
31273 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
31274 rconpointer += 1; // key expansion (for non-256 bit)
31277 for (var i = 1; i < KC; i++) {
31278 tk[i] ^= tk[i - 1];
31279 } // key expansion for 256-bit keys is "slightly different" (fips-197)
31282 for (var i = 1; i < KC / 2; i++) {
31283 tk[i] ^= tk[i - 1];
31286 tt = tk[KC / 2 - 1];
31287 tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
31289 for (var i = KC / 2 + 1; i < KC; i++) {
31290 tk[i] ^= tk[i - 1];
31292 } // copy values into round key arrays
31299 while (i < KC && t < roundKeyCount) {
31302 this._Ke[r][c] = tk[i];
31303 this._Kd[rounds - r][c] = tk[i++];
31306 } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
31309 for (var r = 1; r < rounds; r++) {
31310 for (var c = 0; c < 4; c++) {
31311 tt = this._Kd[r][c];
31312 this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
31317 AES.prototype.encrypt = function (plaintext) {
31318 if (plaintext.length != 16) {
31319 throw new Error('invalid plaintext size (must be 16 bytes)');
31322 var rounds = this._Ke.length - 1;
31323 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31325 var t = convertToInt32(plaintext);
31327 for (var i = 0; i < 4; i++) {
31328 t[i] ^= this._Ke[0][i];
31329 } // apply round transforms
31332 for (var r = 1; r < rounds; r++) {
31333 for (var i = 0; i < 4; i++) {
31334 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];
31338 } // the last round is special
31341 var result = createArray(16),
31344 for (var i = 0; i < 4; i++) {
31345 tt = this._Ke[rounds][i];
31346 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31347 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31348 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31349 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
31355 AES.prototype.decrypt = function (ciphertext) {
31356 if (ciphertext.length != 16) {
31357 throw new Error('invalid ciphertext size (must be 16 bytes)');
31360 var rounds = this._Kd.length - 1;
31361 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
31363 var t = convertToInt32(ciphertext);
31365 for (var i = 0; i < 4; i++) {
31366 t[i] ^= this._Kd[0][i];
31367 } // apply round transforms
31370 for (var r = 1; r < rounds; r++) {
31371 for (var i = 0; i < 4; i++) {
31372 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];
31376 } // the last round is special
31379 var result = createArray(16),
31382 for (var i = 0; i < 4; i++) {
31383 tt = this._Kd[rounds][i];
31384 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
31385 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
31386 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
31387 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
31393 * Mode Of Operation - Electonic Codebook (ECB)
31397 var ModeOfOperationECB = function ModeOfOperationECB(key) {
31398 if (!(this instanceof ModeOfOperationECB)) {
31399 throw Error('AES must be instanitated with `new`');
31402 this.description = "Electronic Code Block";
31404 this._aes = new AES(key);
31407 ModeOfOperationECB.prototype.encrypt = function (plaintext) {
31408 plaintext = coerceArray(plaintext);
31410 if (plaintext.length % 16 !== 0) {
31411 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31414 var ciphertext = createArray(plaintext.length);
31415 var block = createArray(16);
31417 for (var i = 0; i < plaintext.length; i += 16) {
31418 copyArray(plaintext, block, 0, i, i + 16);
31419 block = this._aes.encrypt(block);
31420 copyArray(block, ciphertext, i);
31426 ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
31427 ciphertext = coerceArray(ciphertext);
31429 if (ciphertext.length % 16 !== 0) {
31430 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31433 var plaintext = createArray(ciphertext.length);
31434 var block = createArray(16);
31436 for (var i = 0; i < ciphertext.length; i += 16) {
31437 copyArray(ciphertext, block, 0, i, i + 16);
31438 block = this._aes.decrypt(block);
31439 copyArray(block, plaintext, i);
31445 * Mode Of Operation - Cipher Block Chaining (CBC)
31449 var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
31450 if (!(this instanceof ModeOfOperationCBC)) {
31451 throw Error('AES must be instanitated with `new`');
31454 this.description = "Cipher Block Chaining";
31458 iv = createArray(16);
31459 } else if (iv.length != 16) {
31460 throw new Error('invalid initialation vector size (must be 16 bytes)');
31463 this._lastCipherblock = coerceArray(iv, true);
31464 this._aes = new AES(key);
31467 ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
31468 plaintext = coerceArray(plaintext);
31470 if (plaintext.length % 16 !== 0) {
31471 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
31474 var ciphertext = createArray(plaintext.length);
31475 var block = createArray(16);
31477 for (var i = 0; i < plaintext.length; i += 16) {
31478 copyArray(plaintext, block, 0, i, i + 16);
31480 for (var j = 0; j < 16; j++) {
31481 block[j] ^= this._lastCipherblock[j];
31484 this._lastCipherblock = this._aes.encrypt(block);
31485 copyArray(this._lastCipherblock, ciphertext, i);
31491 ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
31492 ciphertext = coerceArray(ciphertext);
31494 if (ciphertext.length % 16 !== 0) {
31495 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
31498 var plaintext = createArray(ciphertext.length);
31499 var block = createArray(16);
31501 for (var i = 0; i < ciphertext.length; i += 16) {
31502 copyArray(ciphertext, block, 0, i, i + 16);
31503 block = this._aes.decrypt(block);
31505 for (var j = 0; j < 16; j++) {
31506 plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
31509 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
31515 * Mode Of Operation - Cipher Feedback (CFB)
31519 var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
31520 if (!(this instanceof ModeOfOperationCFB)) {
31521 throw Error('AES must be instanitated with `new`');
31524 this.description = "Cipher Feedback";
31528 iv = createArray(16);
31529 } else if (iv.length != 16) {
31530 throw new Error('invalid initialation vector size (must be 16 size)');
31533 if (!segmentSize) {
31537 this.segmentSize = segmentSize;
31538 this._shiftRegister = coerceArray(iv, true);
31539 this._aes = new AES(key);
31542 ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
31543 if (plaintext.length % this.segmentSize != 0) {
31544 throw new Error('invalid plaintext size (must be segmentSize bytes)');
31547 var encrypted = coerceArray(plaintext, true);
31550 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
31551 xorSegment = this._aes.encrypt(this._shiftRegister);
31553 for (var j = 0; j < this.segmentSize; j++) {
31554 encrypted[i + j] ^= xorSegment[j];
31555 } // Shift the register
31558 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31559 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31565 ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
31566 if (ciphertext.length % this.segmentSize != 0) {
31567 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
31570 var plaintext = coerceArray(ciphertext, true);
31573 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
31574 xorSegment = this._aes.encrypt(this._shiftRegister);
31576 for (var j = 0; j < this.segmentSize; j++) {
31577 plaintext[i + j] ^= xorSegment[j];
31578 } // Shift the register
31581 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
31582 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
31588 * Mode Of Operation - Output Feedback (OFB)
31592 var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
31593 if (!(this instanceof ModeOfOperationOFB)) {
31594 throw Error('AES must be instanitated with `new`');
31597 this.description = "Output Feedback";
31601 iv = createArray(16);
31602 } else if (iv.length != 16) {
31603 throw new Error('invalid initialation vector size (must be 16 bytes)');
31606 this._lastPrecipher = coerceArray(iv, true);
31607 this._lastPrecipherIndex = 16;
31608 this._aes = new AES(key);
31611 ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
31612 var encrypted = coerceArray(plaintext, true);
31614 for (var i = 0; i < encrypted.length; i++) {
31615 if (this._lastPrecipherIndex === 16) {
31616 this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
31617 this._lastPrecipherIndex = 0;
31620 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
31624 }; // Decryption is symetric
31627 ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
31629 * Counter object for CTR common mode of operation
31632 var Counter = function Counter(initialValue) {
31633 if (!(this instanceof Counter)) {
31634 throw Error('Counter must be instanitated with `new`');
31635 } // We allow 0, but anything false-ish uses the default 1
31638 if (initialValue !== 0 && !initialValue) {
31642 if (typeof initialValue === 'number') {
31643 this._counter = createArray(16);
31644 this.setValue(initialValue);
31646 this.setBytes(initialValue);
31650 Counter.prototype.setValue = function (value) {
31651 if (typeof value !== 'number' || parseInt(value) != value) {
31652 throw new Error('invalid counter value (must be an integer)');
31653 } // We cannot safely handle numbers beyond the safe range for integers
31656 if (value > Number.MAX_SAFE_INTEGER) {
31657 throw new Error('integer value out of safe range');
31660 for (var index = 15; index >= 0; --index) {
31661 this._counter[index] = value % 256;
31662 value = parseInt(value / 256);
31666 Counter.prototype.setBytes = function (bytes) {
31667 bytes = coerceArray(bytes, true);
31669 if (bytes.length != 16) {
31670 throw new Error('invalid counter bytes size (must be 16 bytes)');
31673 this._counter = bytes;
31676 Counter.prototype.increment = function () {
31677 for (var i = 15; i >= 0; i--) {
31678 if (this._counter[i] === 255) {
31679 this._counter[i] = 0;
31681 this._counter[i]++;
31687 * Mode Of Operation - Counter (CTR)
31691 var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
31692 if (!(this instanceof ModeOfOperationCTR)) {
31693 throw Error('AES must be instanitated with `new`');
31696 this.description = "Counter";
31699 if (!(counter instanceof Counter)) {
31700 counter = new Counter(counter);
31703 this._counter = counter;
31704 this._remainingCounter = null;
31705 this._remainingCounterIndex = 16;
31706 this._aes = new AES(key);
31709 ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
31710 var encrypted = coerceArray(plaintext, true);
31712 for (var i = 0; i < encrypted.length; i++) {
31713 if (this._remainingCounterIndex === 16) {
31714 this._remainingCounter = this._aes.encrypt(this._counter._counter);
31715 this._remainingCounterIndex = 0;
31717 this._counter.increment();
31720 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
31724 }; // Decryption is symetric
31727 ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
31729 // See:https://tools.ietf.org/html/rfc2315
31731 function pkcs7pad(data) {
31732 data = coerceArray(data, true);
31733 var padder = 16 - data.length % 16;
31734 var result = createArray(data.length + padder);
31735 copyArray(data, result);
31737 for (var i = data.length; i < result.length; i++) {
31738 result[i] = padder;
31744 function pkcs7strip(data) {
31745 data = coerceArray(data, true);
31747 if (data.length < 16) {
31748 throw new Error('PKCS#7 invalid length');
31751 var padder = data[data.length - 1];
31754 throw new Error('PKCS#7 padding byte out of range');
31757 var length = data.length - padder;
31759 for (var i = 0; i < padder; i++) {
31760 if (data[length + i] !== padder) {
31761 throw new Error('PKCS#7 invalid padding byte');
31765 var result = createArray(length);
31766 copyArray(data, result, 0, 0, length);
31768 } ///////////////////////
31770 // The block cipher
31777 ecb: ModeOfOperationECB,
31778 cbc: ModeOfOperationCBC,
31779 cfb: ModeOfOperationCFB,
31780 ofb: ModeOfOperationOFB,
31781 ctr: ModeOfOperationCTR
31794 coerceArray: coerceArray,
31795 createArray: createArray,
31796 copyArray: copyArray
31801 module.exports = aesjs; // RequireJS/AMD
31802 // http://www.requirejs.org/docs/api.html
31803 // https://github.com/amdjs/amdjs-api/wiki/AMD
31808 // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
31809 // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16));
31810 // This default signing key is built into iD and can be used to mask/unmask sensitive values.
31812 var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
31813 function utilAesEncrypt(text, key) {
31814 key = key || DEFAULT_128;
31815 var textBytes = aesJs.utils.utf8.toBytes(text);
31816 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31817 var encryptedBytes = aesCtr.encrypt(textBytes);
31818 var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
31819 return encryptedHex;
31821 function utilAesDecrypt(encryptedHex, key) {
31822 key = key || DEFAULT_128;
31823 var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
31824 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
31825 var decryptedBytes = aesCtr.decrypt(encryptedBytes);
31826 var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
31830 function utilCleanTags(tags) {
31833 for (var k in tags) {
31837 if (v !== undefined) {
31838 out[k] = cleanValue(k, v);
31844 function cleanValue(k, v) {
31845 function keepSpaces(k) {
31846 return /_hours|_times|:conditional$/.test(k);
31850 return /^(description|note|fixme)$/.test(k);
31853 if (skip(k)) return v;
31854 var cleaned = v.split(';').map(function (s) {
31856 }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
31857 // It is only intended to prevent obvious copy-paste errors. (#2323)
31858 // clean website- and email-like tags
31860 if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
31861 cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
31870 function utilDetect(refresh) {
31871 if (_detected && !refresh) return _detected;
31873 var ua = navigator.userAgent;
31877 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
31880 _detected.browser = m[1];
31881 _detected.version = m[2];
31884 if (!_detected.browser) {
31885 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
31888 _detected.browser = 'msie';
31889 _detected.version = m[1];
31893 if (!_detected.browser) {
31894 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
31897 _detected.browser = 'Opera';
31898 _detected.version = m[2];
31902 if (!_detected.browser) {
31903 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
31906 _detected.browser = m[1];
31907 _detected.version = m[2];
31908 m = ua.match(/version\/([\.\d]+)/i);
31909 if (m !== null) _detected.version = m[1];
31913 if (!_detected.browser) {
31914 _detected.browser = navigator.appName;
31915 _detected.version = navigator.appVersion;
31916 } // keep major.minor version only..
31919 _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
31920 // Legacy Opera has incomplete svg style support. See #715
31922 _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
31924 if (_detected.browser.toLowerCase() === 'msie') {
31925 _detected.ie = true;
31926 _detected.browser = 'Internet Explorer';
31927 _detected.support = parseFloat(_detected.version) >= 11;
31929 _detected.ie = false;
31930 _detected.support = true;
31933 _detected.filedrop = window.FileReader && 'ondrop' in window;
31934 _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31935 _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
31938 if (/Win/.test(ua)) {
31939 _detected.os = 'win';
31940 _detected.platform = 'Windows';
31941 } else if (/Mac/.test(ua)) {
31942 _detected.os = 'mac';
31943 _detected.platform = 'Macintosh';
31944 } else if (/X11/.test(ua) || /Linux/.test(ua)) {
31945 _detected.os = 'linux';
31946 _detected.platform = 'Linux';
31948 _detected.os = 'win';
31949 _detected.platform = 'Unknown';
31952 _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
31953 // so assume any "mac" with multitouch is actually iOS
31954 navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
31956 // An array of locales requested by the browser in priority order.
31958 _detected.browserLocales = Array.from(new Set( // remove duplicates
31959 [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
31960 navigator.userLanguage]) // remove any undefined values
31961 .filter(Boolean)));
31964 var loc = window.top.location;
31965 var origin = loc.origin;
31968 // for unpatched IE11
31969 origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
31972 _detected.host = origin + loc.pathname;
31976 // Like selection.property('value', ...), but avoids no-op value sets,
31977 // which can result in layout/repaint thrashing in some situations.
31978 function utilGetSetValue(selection, value) {
31979 function d3_selection_value(value) {
31980 function valueNull() {
31984 function valueConstant() {
31985 if (this.value !== value) {
31986 this.value = value;
31990 function valueFunction() {
31991 var x = value.apply(this, arguments);
31993 if (x === null || x === undefined) {
31995 } else if (this.value !== x) {
32000 return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
32003 if (arguments.length === 1) {
32004 return selection.property('value');
32007 return selection.each(d3_selection_value(value));
32010 function utilKeybinding(namespace) {
32011 var _keybindings = {};
32013 function testBindings(d3_event, isCapturing) {
32014 var didMatch = false;
32015 var bindings = Object.keys(_keybindings).map(function (id) {
32016 return _keybindings[id];
32018 var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
32019 // so we don't strictly match on the shift key, but we prioritize
32020 // shifted keybindings first, and fallback to unshifted only if no match.
32021 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
32022 // priority match shifted keybindings first
32024 for (i = 0; i < bindings.length; i++) {
32025 binding = bindings[i];
32026 if (!binding.event.modifiers.shiftKey) continue; // no shift
32028 if (!!binding.capture !== isCapturing) continue;
32030 if (matches(d3_event, binding, true)) {
32031 binding.callback(d3_event);
32032 didMatch = true; // match a max of one binding per event
32038 if (didMatch) return; // then unshifted keybindings
32040 for (i = 0; i < bindings.length; i++) {
32041 binding = bindings[i];
32042 if (binding.event.modifiers.shiftKey) continue; // shift
32044 if (!!binding.capture !== isCapturing) continue;
32046 if (matches(d3_event, binding, false)) {
32047 binding.callback(d3_event);
32052 function matches(d3_event, binding, testShift) {
32053 var event = d3_event;
32054 var isMatch = false;
32055 var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
32057 if (event.key !== undefined) {
32058 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
32062 if (binding.event.key === undefined) {
32064 } else if (Array.isArray(binding.event.key)) {
32065 if (binding.event.key.map(function (s) {
32066 return s.toLowerCase();
32067 }).indexOf(event.key.toLowerCase()) === -1) {
32071 if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) {
32075 } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
32076 // - browser doesn't support `KeyboardEvent.key`
32077 // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
32080 if (!isMatch && tryKeyCode) {
32081 isMatch = event.keyCode === binding.event.keyCode;
32084 if (!isMatch) return false; // test modifier keys
32086 if (!(event.ctrlKey && event.altKey)) {
32087 // if both are set, assume AltGr and skip it - #4096
32088 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
32089 if (event.altKey !== binding.event.modifiers.altKey) return false;
32092 if (event.metaKey !== binding.event.modifiers.metaKey) return false;
32093 if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
32098 function capture(d3_event) {
32099 testBindings(d3_event, true);
32102 function bubble(d3_event) {
32103 var tagName = select(d3_event.target).node().tagName;
32105 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
32109 testBindings(d3_event, false);
32112 function keybinding(selection) {
32113 selection = selection || select(document);
32114 selection.on('keydown.capture.' + namespace, capture, true);
32115 selection.on('keydown.bubble.' + namespace, bubble, false);
32117 } // was: keybinding.off()
32120 keybinding.unbind = function (selection) {
32122 selection = selection || select(document);
32123 selection.on('keydown.capture.' + namespace, null);
32124 selection.on('keydown.bubble.' + namespace, null);
32128 keybinding.clear = function () {
32131 }; // Remove one or more keycode bindings.
32134 keybinding.off = function (codes, capture) {
32135 var arr = utilArrayUniq([].concat(codes));
32137 for (var i = 0; i < arr.length; i++) {
32138 var id = arr[i] + (capture ? '-capture' : '-bubble');
32139 delete _keybindings[id];
32143 }; // Add one or more keycode bindings.
32146 keybinding.on = function (codes, callback, capture) {
32147 if (typeof callback !== 'function') {
32148 return keybinding.off(codes, capture);
32151 var arr = utilArrayUniq([].concat(codes));
32153 for (var i = 0; i < arr.length; i++) {
32154 var id = arr[i] + (capture ? '-capture' : '-bubble');
32158 callback: callback,
32173 if (_keybindings[id]) {
32174 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
32177 _keybindings[id] = binding;
32178 var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
32180 for (var j = 0; j < matches.length; j++) {
32181 // Normalise matching errors
32182 if (matches[j] === '++') matches[j] = '+';
32184 if (matches[j] in utilKeybinding.modifierCodes) {
32185 var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
32186 binding.event.modifiers[prop] = true;
32188 binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
32190 if (matches[j] in utilKeybinding.keyCodes) {
32191 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
32203 * See https://github.com/keithamus/jwerty
32206 utilKeybinding.modifierCodes = {
32210 // CTRL key, on Mac: ⌃
32213 // ALT key, on Mac: ⌥ (Alt)
32217 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
32224 utilKeybinding.modifierProperties = {
32230 utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
32231 utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
32232 utilKeybinding.keys = {
32233 // Backspace key, on Mac: ⌫ (Backspace)
32235 backspace: 'Backspace',
32236 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32249 'pause-break': 'Pause',
32250 // Caps Lock key, ⇪
32253 'caps-lock': 'CapsLock',
32254 // Escape key, on Mac: ⎋, on Windows: Esc
32255 '⎋': ['Escape', 'Esc'],
32256 escape: ['Escape', 'Esc'],
32257 esc: ['Escape', 'Esc'],
32259 space: [' ', 'Spacebar'],
32260 // Page-Up key, or pgup, on Mac: ↖
32263 'page-up': 'PageUp',
32264 // Page-Down key, or pgdown, on Mac: ↘
32266 pgdown: 'PageDown',
32267 'page-down': 'PageDown',
32268 // END key, on Mac: ⇟
32271 // HOME key, on Mac: ⇞
32274 // Insert key, or ins
32277 // Delete key, on Mac: ⌦ (Delete)
32278 '⌦': ['Delete', 'Del'],
32279 del: ['Delete', 'Del'],
32280 'delete': ['Delete', 'Del'],
32281 // Left Arrow Key, or ←
32282 '←': ['ArrowLeft', 'Left'],
32283 left: ['ArrowLeft', 'Left'],
32284 'arrow-left': ['ArrowLeft', 'Left'],
32285 // Up Arrow Key, or ↑
32286 '↑': ['ArrowUp', 'Up'],
32287 up: ['ArrowUp', 'Up'],
32288 'arrow-up': ['ArrowUp', 'Up'],
32289 // Right Arrow Key, or →
32290 '→': ['ArrowRight', 'Right'],
32291 right: ['ArrowRight', 'Right'],
32292 'arrow-right': ['ArrowRight', 'Right'],
32293 // Up Arrow Key, or ↓
32294 '↓': ['ArrowDown', 'Down'],
32295 down: ['ArrowDown', 'Down'],
32296 'arrow-down': ['ArrowDown', 'Down'],
32297 // odities, stuff for backward compatibility (browsers and code):
32298 // Num-Multiply, or *
32299 '*': ['*', 'Multiply'],
32300 star: ['*', 'Multiply'],
32301 asterisk: ['*', 'Multiply'],
32302 multiply: ['*', 'Multiply'],
32305 'plus': ['+', 'Add'],
32306 // Num-Subtract, or -
32307 '-': ['-', 'Subtract'],
32308 subtract: ['-', 'Subtract'],
32309 'dash': ['-', 'Subtract'],
32316 // Period, or ., or full-stop
32319 // Slash, or /, or forward-slash
32321 'forward-slash': '/',
32322 // Tick, or `, or back-quote
32325 // Open bracket, or [
32326 'open-bracket': '[',
32327 // Back slash, or \
32328 'back-slash': '\\',
32329 // Close backet, or ]
32330 'close-bracket': ']',
32331 // Apostrophe, or Quote, or '
32372 utilKeybinding.keyCodes = {
32373 // Backspace key, on Mac: ⌫ (Backspace)
32376 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
32390 // Caps Lock key, ⇪
32394 // Escape key, on Mac: ⎋, on Windows: Esc
32400 // Page-Up key, or pgup, on Mac: ↖
32404 // Page-Down key, or pgdown, on Mac: ↘
32408 // END key, on Mac: ⇟
32411 // HOME key, on Mac: ⇞
32414 // Insert key, or ins
32417 // Delete key, on Mac: ⌦ (Delete)
32421 // Left Arrow Key, or ←
32425 // Up Arrow Key, or ↑
32429 // Right Arrow Key, or →
32433 // Up Arrow Key, or ↓
32437 // odities, printing characters that come out wrong:
32440 // Num-Multiply, or *
32448 // Num-Subtract, or -
32451 // Vertical Bar / Pipe
32466 // Dash / Underscore key
32468 // Period, or ., or full-stop
32472 // Slash, or /, or forward-slash
32475 'forward-slash': 191,
32476 // Tick, or `, or back-quote
32480 // Open bracket, or [
32482 'open-bracket': 219,
32483 // Back slash, or \
32486 // Close backet, or ]
32488 'close-bracket': 221,
32489 // Apostrophe, or Quote, or '
32498 while (++i < 106) {
32499 utilKeybinding.keyCodes['num-' + n] = i;
32508 utilKeybinding.keyCodes[n] = i;
32516 while (++i < 136) {
32517 utilKeybinding.keyCodes['f' + n] = i;
32525 utilKeybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
32528 function utilObjectOmit(obj, omitKeys) {
32529 return Object.keys(obj).reduce(function (result, key) {
32530 if (omitKeys.indexOf(key) === -1) {
32531 result[key] = obj[key]; // keep
32538 // Copies a variable number of methods from source to target.
32539 function utilRebind(target, source) {
32541 n = arguments.length,
32545 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
32549 } // Method is assumed to be a standard D3 getter-setter:
32550 // If passed with no arguments, gets the value.
32551 // If passed with arguments, sets the value and returns the target.
32553 function d3_rebind(target, source, method) {
32554 return function () {
32555 var value = method.apply(source, arguments);
32556 return value === source ? target : value;
32560 // A per-domain session mutex backed by a cookie and dead man's
32561 // switch. If the session crashes, the mutex will auto-release
32562 // after 5 seconds.
32563 // This accepts a string and returns an object that complies with utilSessionMutexType
32564 function utilSessionMutex(name) {
32569 var expires = new Date();
32570 expires.setSeconds(expires.getSeconds() + 5);
32571 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
32574 mutex.lock = function () {
32575 if (intervalID) return true;
32576 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
32577 if (cookie) return false;
32579 intervalID = window.setInterval(renew, 4000);
32583 mutex.unlock = function () {
32584 if (!intervalID) return;
32585 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
32586 clearInterval(intervalID);
32590 mutex.locked = function () {
32591 return !!intervalID;
32597 function utilTiler() {
32598 var _size = [256, 256];
32600 var _tileSize = 256;
32601 var _zoomExtent = [0, 20];
32602 var _translate = [_size[0] / 2, _size[1] / 2];
32604 var _skipNullIsland = false;
32606 function clamp(num, min, max) {
32607 return Math.max(min, Math.min(num, max));
32610 function nearNullIsland(tile) {
32616 var center = Math.pow(2, z - 1);
32617 var width = Math.pow(2, z - 6);
32618 var min = center - width / 2;
32619 var max = center + width / 2 - 1;
32620 return x >= min && x <= max && y >= min && y <= max;
32627 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
32628 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
32630 var tileMax = Math.pow(2, z0) - 1;
32631 var log2ts = Math.log(_tileSize) * Math.LOG2E;
32632 var k = Math.pow(2, z - z0 + log2ts);
32633 var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
32634 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));
32635 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));
32638 for (var i = 0; i < rows.length; i++) {
32641 for (var j = 0; j < cols.length; j++) {
32644 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
32645 tiles.unshift([x, y, z0]); // tiles in view at beginning
32647 tiles.push([x, y, z0]); // tiles in margin at the end
32652 tiles.translate = origin;
32657 * getTiles() returns an array of tiles that cover the map view
32661 tiler.getTiles = function (projection) {
32662 var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
32663 this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
32664 var tiles = tiler();
32665 var ts = tiles.scale;
32666 return tiles.map(function (tile) {
32667 if (_skipNullIsland && nearNullIsland(tile)) {
32671 var x = tile[0] * ts - origin[0];
32672 var y = tile[1] * ts - origin[1];
32674 id: tile.toString(),
32676 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
32678 }).filter(Boolean);
32681 * getGeoJSON() returns a FeatureCollection for debugging tiles
32685 tiler.getGeoJSON = function (projection) {
32686 var features = tiler.getTiles(projection).map(function (tile) {
32695 coordinates: [tile.extent.polygon()]
32700 type: 'FeatureCollection',
32705 tiler.tileSize = function (val) {
32706 if (!arguments.length) return _tileSize;
32711 tiler.zoomExtent = function (val) {
32712 if (!arguments.length) return _zoomExtent;
32717 tiler.size = function (val) {
32718 if (!arguments.length) return _size;
32723 tiler.scale = function (val) {
32724 if (!arguments.length) return _scale;
32729 tiler.translate = function (val) {
32730 if (!arguments.length) return _translate;
32733 }; // number to extend the rows/columns beyond those covering the viewport
32736 tiler.margin = function (val) {
32737 if (!arguments.length) return _margin;
32742 tiler.skipNullIsland = function (val) {
32743 if (!arguments.length) return _skipNullIsland;
32744 _skipNullIsland = val;
32751 function utilTriggerEvent(target, type) {
32752 target.each(function () {
32753 var evt = document.createEvent('HTMLEvents');
32754 evt.initEvent(type, true, true);
32755 this.dispatchEvent(evt);
32759 var _mainLocations = coreLocations(); // singleton
32760 // `coreLocations` maintains an internal index of all the boundaries/geofences used by iD.
32761 // It's used by presets, community index, background imagery, to know where in the world these things are valid.
32762 // These geofences should be defined by `locationSet` objects:
32764 // let locationSet = {
32765 // include: [ Array of locations ],
32766 // exclude: [ Array of locations ]
32769 // For more info see the location-conflation and country-coder projects, see:
32770 // https://github.com/ideditor/location-conflation
32771 // https://github.com/ideditor/country-coder
32774 function coreLocations() {
32776 var _resolvedFeatures = {}; // cache of *resolved* locationSet features
32778 var _loco = new _default(); // instance of a location-conflation resolver
32781 var _wp; // instance of a which-polygon index
32782 // pre-resolve the worldwide locationSet
32790 resolveLocationSet(world);
32794 var _deferred = new Set();
32796 var _inProcess; // Returns a Promise to process the queue
32799 function processQueue() {
32800 if (!_queue.length) return Promise.resolve(); // console.log(`queue length ${_queue.length}`);
32802 var chunk = _queue.pop();
32804 return new Promise(function (resolvePromise) {
32805 var handle = window.requestIdleCallback(function () {
32806 _deferred["delete"](handle); // const t0 = performance.now();
32809 chunk.forEach(resolveLocationSet); // const t1 = performance.now();
32810 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
32815 _deferred.add(handle);
32816 }).then(function () {
32817 return processQueue();
32819 } // Pass an Object with a `locationSet` property,
32820 // Performs the locationSet resolution, caches the result, and sets a `locationSetID` property on the object.
32823 function resolveLocationSet(obj) {
32824 if (obj.locationSetID) return; // work was done already
32827 var locationSet = obj.locationSet;
32829 if (!locationSet) {
32830 throw new Error('object missing locationSet property');
32833 if (!locationSet.include) {
32834 // missing `include`, default to worldwide include
32835 locationSet.include = ['Q2']; // https://github.com/openstreetmap/iD/pull/8305#discussion_r662344647
32838 var resolved = _loco.resolveLocationSet(locationSet);
32840 var locationSetID = resolved.id;
32841 obj.locationSetID = locationSetID;
32843 if (!resolved.feature.geometry.coordinates.length || !resolved.feature.properties.area) {
32844 throw new Error("locationSet ".concat(locationSetID, " resolves to an empty feature."));
32847 if (!_resolvedFeatures[locationSetID]) {
32848 // First time seeing this locationSet feature
32849 var feature = JSON.parse(JSON.stringify(resolved.feature)); // deep clone
32851 feature.id = locationSetID; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
32853 feature.properties.id = locationSetID;
32854 _resolvedFeatures[locationSetID] = feature; // insert into cache
32857 obj.locationSet = {
32859 }; // default worldwide
32861 obj.locationSetID = '+[Q2]';
32863 } // Rebuilds the whichPolygon index with whatever features have been resolved.
32866 function rebuildIndex() {
32867 _wp = whichPolygon_1({
32868 features: Object.values(_resolvedFeatures)
32871 // `mergeCustomGeoJSON`
32872 // Accepts an FeatureCollection-like object containing custom locations
32873 // Each feature must have a filename-like `id`, for example: `something.geojson`
32876 // "type": "FeatureCollection"
32879 // "type": "Feature",
32880 // "id": "philly_metro.geojson",
32881 // "properties": { … },
32882 // "geometry": { … }
32889 _this.mergeCustomGeoJSON = function (fc) {
32890 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
32891 fc.features.forEach(function (feature) {
32892 feature.properties = feature.properties || {};
32893 var props = feature.properties; // Get `id` from either `id` or `properties`
32895 var id = feature.id || props.id;
32896 if (!id || !/^\S+\.geojson$/i.test(id)) return; // Ensure `id` exists and is lowercase
32898 id = id.toLowerCase();
32900 props.id = id; // Ensure `area` property exists
32903 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
32905 props.area = Number(area.toFixed(2));
32908 _loco._cache[id] = feature;
32912 // `mergeLocationSets`
32913 // Accepts an Array of Objects containing `locationSet` properties.
32914 // The locationSets will be resolved and indexed in the background.
32916 // { id: 'preset1', locationSet: {…} },
32917 // { id: 'preset2', locationSet: {…} },
32918 // { id: 'preset3', locationSet: {…} },
32921 // After resolving and indexing, the Objects will be decorated with a
32922 // `locationSetID` property.
32924 // { id: 'preset1', locationSet: {…}, locationSetID: '+[Q2]' },
32925 // { id: 'preset2', locationSet: {…}, locationSetID: '+[Q30]' },
32926 // { id: 'preset3', locationSet: {…}, locationSetID: '+[Q2]' },
32930 // Returns a Promise fulfilled when the resolving/indexing has been completed
32931 // This will take some seconds but happen in the background during browser idle time.
32935 _this.mergeLocationSets = function (objects) {
32936 if (!Array.isArray(objects)) return Promise.reject('nothing to do'); // Resolve all locationSets -> geojson, processing data in chunks
32938 // Because this will happen during idle callbacks, we want to choose a chunk size
32939 // that won't make the browser stutter too badly. LocationSets that are a simple
32940 // country coder include will resolve instantly, but ones that involve complex
32941 // include/exclude operations will take some milliseconds longer.
32943 // Some discussion and performance results on these tickets:
32944 // https://github.com/ideditor/location-conflation/issues/26
32945 // https://github.com/osmlab/name-suggestion-index/issues/4784#issuecomment-742003434
32947 _queue = _queue.concat(utilArrayChunk(objects, 200));
32950 _inProcess = processQueue().then(function () {
32960 // Returns a locationSetID for a given locationSet (fallback to `+[Q2]`, world)
32961 // (The locationset doesn't necessarily need to be resolved to compute its `id`)
32964 // `locationSet`: A locationSet, e.g. `{ include: ['us'] }`
32966 // The locationSetID, e.g. `+[Q30]`
32970 _this.locationSetID = function (locationSet) {
32974 locationSetID = _loco.validateLocationSet(locationSet).id;
32976 locationSetID = '+[Q2]'; // the world
32979 return locationSetID;
32982 // Returns the resolved GeoJSON feature for a given locationSetID (fallback to 'world')
32985 // `locationSetID`: id of the form like `+[Q30]` (United States)
32987 // A GeoJSON feature:
32989 // type: 'Feature',
32991 // properties: { id: '+[Q30]', area: 21817019.17, … },
32996 _this.feature = function (locationSetID) {
32997 return _resolvedFeatures[locationSetID] || _resolvedFeatures['+[Q2]'];
33000 // Find all the resolved locationSets valid at the given location.
33001 // Results include the area (in km²) to facilitate sorting.
33004 // `loc`: the [lon,lat] location to query, e.g. `[-74.4813, 40.7967]`
33006 // Object of locationSetIDs to areas (in km²)
33008 // "+[Q2]": 511207893.3958111,
33009 // "+[Q30]": 21817019.17,
33010 // "+[new_jersey.geojson]": 22390.77,
33016 _this.locationsAt = function (loc) {
33018 (_wp(loc, true) || []).forEach(function (prop) {
33019 return result[prop.id] = prop.area;
33024 // Execute a query directly against which-polygon
33025 // https://github.com/mapbox/which-polygon
33028 // `loc`: the [lon,lat] location to query,
33029 // `multi`: `true` to return all results, `false` to return first result
33031 // Array of GeoJSON *properties* for the locationSet features that exist at `loc`
33035 _this.query = function (loc, multi) {
33036 return _wp(loc, multi);
33037 }; // Direct access to the location-conflation resolver
33040 _this.loco = function () {
33042 }; // Direct access to the which-polygon index
33045 _this.wp = function () {
33052 var $findIndex = arrayIteration.findIndex;
33055 var FIND_INDEX = 'findIndex';
33056 var SKIPS_HOLES = true;
33058 // Shouldn't skip holes
33059 if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES = false; });
33061 // `Array.prototype.findIndex` method
33062 // https://tc39.es/ecma262/#sec-array.prototype.findindex
33063 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES }, {
33064 findIndex: function findIndex(callbackfn /* , that = undefined */) {
33065 return $findIndex(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
33069 // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
33070 addToUnscopables(FIND_INDEX);
33072 var notARegexp = function (it) {
33073 if (isRegexp(it)) {
33074 throw TypeError("The method doesn't accept regular expressions");
33078 var MATCH = wellKnownSymbol('match');
33080 var correctIsRegexpLogic = function (METHOD_NAME) {
33083 '/./'[METHOD_NAME](regexp);
33086 regexp[MATCH] = false;
33087 return '/./'[METHOD_NAME](regexp);
33088 } catch (error2) { /* empty */ }
33092 // `String.prototype.includes` method
33093 // https://tc39.es/ecma262/#sec-string.prototype.includes
33094 _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
33095 includes: function includes(searchString /* , position = 0 */) {
33096 return !!~String(requireObjectCoercible(this))
33097 .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
33101 var _mainLocalizer = coreLocalizer(); // singleton
33104 var _t = _mainLocalizer.t;
33105 // coreLocalizer manages language and locale parameters including translated strings
33108 function coreLocalizer() {
33109 var localizer = {};
33110 var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
33111 // * `rtl` - right-to-left or left-to-right text direction
33112 // * `pct` - the percent of strings translated; 1 = 100%, full coverage
33115 // en: { rtl: false, pct: {…} },
33116 // de: { rtl: false, pct: {…} },
33120 var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
33122 // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33123 // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
33127 var _localeStrings = {}; // the current locale
33129 var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
33131 var _localeCodes = ['en-US', 'en'];
33132 var _languageCode = 'en';
33133 var _textDirection = 'ltr';
33134 var _usesMetric = false;
33135 var _languageNames = {};
33136 var _scriptNames = {}; // getters for the current locale parameters
33138 localizer.localeCode = function () {
33139 return _localeCode;
33142 localizer.localeCodes = function () {
33143 return _localeCodes;
33146 localizer.languageCode = function () {
33147 return _languageCode;
33150 localizer.textDirection = function () {
33151 return _textDirection;
33154 localizer.usesMetric = function () {
33155 return _usesMetric;
33158 localizer.languageNames = function () {
33159 return _languageNames;
33162 localizer.scriptNames = function () {
33163 return _scriptNames;
33164 }; // The client app may want to manually set the locale, regardless of the
33165 // settings provided by the browser
33168 var _preferredLocaleCodes = [];
33170 localizer.preferredLocaleCodes = function (codes) {
33171 if (!arguments.length) return _preferredLocaleCodes;
33173 if (typeof codes === 'string') {
33174 // be generous and accept delimited strings as input
33175 _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
33177 _preferredLocaleCodes = codes;
33185 localizer.ensureLoaded = function () {
33186 if (_loadPromise) return _loadPromise;
33187 var filesToFetch = [// load the list of languages
33188 'languages', // load the list of supported locales
33191 general: 'locales',
33192 tagging: 'https://cdn.jsdelivr.net/npm/@openstreetmap/id-tagging-schema@3/dist/translations'
33194 var fileMap = _mainFileFetcher.fileMap();
33196 for (var scopeId in localeDirs) {
33197 var key = "locales_index_".concat(scopeId);
33198 fileMap[key] = localeDirs[scopeId] + '/index.min.json';
33199 filesToFetch.push(key);
33202 return _loadPromise = Promise.all(filesToFetch.map(function (key) {
33203 return _mainFileFetcher.get(key);
33204 })).then(function (results) {
33205 _dataLanguages = results[0];
33206 _dataLocales = results[1];
33207 var indexes = results.slice(2);
33209 var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
33210 concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
33213 _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
33215 _localeCode = _localeCodes[0];
33216 var loadStringsPromises = [];
33217 indexes.forEach(function (index, i) {
33218 // Will always return the index for `en` if nothing else
33219 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
33220 return index[locale] && index[locale].pct === 1;
33221 }); // We only need to load locales up until we find one with full coverage
33224 _localeCodes.slice(0, fullCoverageIndex + 1).forEach(function (code) {
33225 var scopeId = Object.keys(localeDirs)[i];
33226 var directory = Object.values(localeDirs)[i];
33227 if (index[code]) loadStringsPromises.push(localizer.loadLocale(code, scopeId, directory));
33230 return Promise.all(loadStringsPromises);
33231 }).then(function () {
33232 updateForCurrentLocale();
33233 })["catch"](function (err) {
33234 return console.error(err);
33235 }); // eslint-disable-line
33236 }; // Returns the locales from `requestedLocales` supported by iD that we should use
33239 function localesToUseFrom(requestedLocales) {
33240 var supportedLocales = _dataLocales;
33243 for (var i in requestedLocales) {
33244 var locale = requestedLocales[i];
33245 if (supportedLocales[locale]) toUse.push(locale);
33247 if (locale.includes('-')) {
33248 // Full locale ('es-ES'), add fallback to the base ('es')
33249 var langPart = locale.split('-')[0];
33250 if (supportedLocales[langPart]) toUse.push(langPart);
33252 } // remove duplicates
33255 return utilArrayUniq(toUse);
33258 function updateForCurrentLocale() {
33259 if (!_localeCode) return;
33260 _languageCode = _localeCode.split('-')[0];
33261 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
33262 var hash = utilStringQs(window.location.hash);
33264 if (hash.rtl === 'true') {
33265 _textDirection = 'rtl';
33266 } else if (hash.rtl === 'false') {
33267 _textDirection = 'ltr';
33269 _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
33272 var locale = _localeCode;
33273 if (locale.toLowerCase() === 'en-us') locale = 'en';
33274 _languageNames = _localeStrings.general[locale].languageNames;
33275 _scriptNames = _localeStrings.general[locale].scriptNames;
33276 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
33279 // Returns a Promise to load the strings for the requested locale
33282 localizer.loadLocale = function (locale, scopeId, directory) {
33283 // US English is the default
33284 if (locale.toLowerCase() === 'en-us') locale = 'en';
33286 if (_localeStrings[scopeId] && _localeStrings[scopeId][locale]) {
33288 return Promise.resolve(locale);
33291 var fileMap = _mainFileFetcher.fileMap();
33292 var key = "locale_".concat(scopeId, "_").concat(locale);
33293 fileMap[key] = "".concat(directory, "/").concat(locale, ".min.json");
33294 return _mainFileFetcher.get(key).then(function (d) {
33295 if (!_localeStrings[scopeId]) _localeStrings[scopeId] = {};
33296 _localeStrings[scopeId][locale] = d[locale];
33301 localizer.pluralRule = function (number) {
33302 return pluralRule(number, _localeCode);
33303 }; // Returns the plural rule for the given `number` with the given `localeCode`.
33304 // One of: `zero`, `one`, `two`, `few`, `many`, `other`
33307 function pluralRule(number, localeCode) {
33308 // modern browsers have this functionality built-in
33309 var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
33312 return rules.select(number);
33313 } // fallback to basic one/other, as in English
33316 if (number === 1) return 'one';
33320 * Try to find that string in `locale` or the current `_localeCode` matching
33321 * the given `stringId`. If no string can be found in the requested locale,
33322 * we'll recurse down all the `_localeCodes` until one is found.
33324 * @param {string} stringId string identifier
33325 * @param {object?} replacements token replacements and default string
33326 * @param {string?} locale locale to use (defaults to currentLocale)
33327 * @return {string?} localized string
33331 localizer.tInfo = function (origStringId, replacements, locale) {
33332 var stringId = origStringId.trim();
33333 var scopeId = 'general';
33335 if (stringId[0] === '_') {
33336 var split = stringId.split('.');
33337 scopeId = split[0].slice(1);
33338 stringId = split.slice(1).join('.');
33341 locale = locale || _localeCode;
33342 var path = stringId.split('.').map(function (s) {
33343 return s.replace(/<TX_DOT>/g, '.');
33345 var stringsKey = locale; // US English is the default
33347 if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
33348 var result = _localeStrings && _localeStrings[scopeId] && _localeStrings[scopeId][stringsKey];
33350 while (result !== undefined && path.length) {
33351 result = result[path.pop()];
33354 if (result !== undefined) {
33355 if (replacements) {
33356 if (_typeof(result) === 'object' && Object.keys(result).length) {
33357 // If plural forms are provided, dig one level deeper based on the
33358 // first numeric token replacement provided.
33359 var number = Object.values(replacements).find(function (value) {
33360 return typeof value === 'number';
33363 if (number !== undefined) {
33364 var rule = pluralRule(number, locale);
33366 if (result[rule]) {
33367 result = result[rule];
33369 // We're pretty sure this should be a plural but no string
33370 // could be found for the given rule. Just pick the first
33371 // string and hope it makes sense.
33372 result = Object.values(result)[0];
33377 if (typeof result === 'string') {
33378 for (var key in replacements) {
33379 var value = replacements[key];
33381 if (typeof value === 'number') {
33382 if (value.toLocaleString) {
33383 // format numbers for the locale
33384 value = value.toLocaleString(locale, {
33387 minimumFractionDigits: 0
33390 value = value.toString();
33394 var token = "{".concat(key, "}");
33395 var regex = new RegExp(token, 'g');
33396 result = result.replace(regex, value);
33401 if (typeof result === 'string') {
33402 // found a localized string!
33408 } // no localized string found...
33409 // attempt to fallback to a lower-priority language
33412 var index = _localeCodes.indexOf(locale);
33414 if (index >= 0 && index < _localeCodes.length - 1) {
33415 // eventually this will be 'en' or another locale with 100% coverage
33416 var fallback = _localeCodes[index + 1];
33417 return localizer.tInfo(origStringId, replacements, fallback);
33420 if (replacements && 'default' in replacements) {
33421 // Fallback to a default value if one is specified in `replacements`
33423 text: replacements["default"],
33428 var missing = "Missing ".concat(locale, " translation: ").concat(origStringId);
33429 if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
33437 localizer.hasTextForStringId = function (stringId) {
33438 return !!localizer.tInfo(stringId, {
33439 "default": 'nothing found'
33441 }; // Returns only the localized text, discarding the locale info
33444 localizer.t = function (stringId, replacements, locale) {
33445 return localizer.tInfo(stringId, replacements, locale).text;
33446 }; // Returns the localized text wrapped in an HTML element encoding the locale info
33449 localizer.t.html = function (stringId, replacements, locale) {
33450 var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
33452 return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
33455 localizer.htmlForLocalizedText = function (text, localeCode) {
33456 return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
33459 localizer.languageName = function (code, options) {
33460 if (_languageNames[code]) {
33461 // name in locale language
33463 return _languageNames[code];
33464 } // sometimes we only want the local name
33467 if (options && options.localOnly) return null;
33468 var langInfo = _dataLanguages[code];
33471 if (langInfo.nativeName) {
33472 // name in native language
33473 // e.g. "Deutsch (de)"
33474 return localizer.t('translate.language_and_code', {
33475 language: langInfo.nativeName,
33478 } else if (langInfo.base && langInfo.script) {
33479 var base = langInfo.base; // the code of the language this is based on
33481 if (_languageNames[base]) {
33482 // base language name in locale language
33483 var scriptCode = langInfo.script;
33484 var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
33486 return localizer.t('translate.language_and_code', {
33487 language: _languageNames[base],
33490 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
33491 // e.g. "српски (sr-Cyrl)"
33492 return localizer.t('translate.language_and_code', {
33493 language: _dataLanguages[base].nativeName,
33500 return code; // if not found, use the code
33506 // `presetCollection` is a wrapper around an `Array` of presets `collection`,
33507 // and decorated with some extra methods for searching and matching geometry
33510 function presetCollection(collection) {
33511 var MAXRESULTS = 50;
33514 _this.collection = collection;
33516 _this.item = function (id) {
33517 if (_memo[id]) return _memo[id];
33519 var found = _this.collection.find(function (d) {
33520 return d.id === id;
33523 if (found) _memo[id] = found;
33527 _this.index = function (id) {
33528 return _this.collection.findIndex(function (d) {
33529 return d.id === id;
33533 _this.matchGeometry = function (geometry) {
33534 return presetCollection(_this.collection.filter(function (d) {
33535 return d.matchGeometry(geometry);
33539 _this.matchAllGeometry = function (geometries) {
33540 return presetCollection(_this.collection.filter(function (d) {
33541 return d && d.matchAllGeometry(geometries);
33545 _this.matchAnyGeometry = function (geometries) {
33546 return presetCollection(_this.collection.filter(function (d) {
33547 return geometries.some(function (geom) {
33548 return d.matchGeometry(geom);
33553 _this.fallback = function (geometry) {
33555 if (id === 'vertex') id = 'point';
33556 return _this.item(id);
33559 _this.search = function (value, geometry, loc) {
33560 if (!value) return _this; // don't remove diacritical characters since we're assuming the user is being intentional
33562 value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
33564 function leading(a) {
33565 var index = a.indexOf(value);
33566 return index === 0 || a[index - 1] === ' ';
33567 } // match at name beginning only
33570 function leadingStrict(a) {
33571 var index = a.indexOf(value);
33572 return index === 0;
33575 function sortPresets(nameProp) {
33576 return function sortNames(a, b) {
33577 var aCompare = a[nameProp]();
33578 var bCompare = b[nameProp](); // priority if search string matches preset name exactly - #4325
33580 if (value === aCompare) return -1;
33581 if (value === bCompare) return 1; // priority for higher matchScore
33583 var i = b.originalScore - a.originalScore;
33584 if (i !== 0) return i; // priority if search string appears earlier in preset name
33586 i = aCompare.indexOf(value) - bCompare.indexOf(value);
33587 if (i !== 0) return i; // priority for shorter preset names
33589 return aCompare.length - bCompare.length;
33593 var pool = _this.collection;
33595 if (Array.isArray(loc)) {
33596 var validLocations = _mainLocations.locationsAt(loc);
33597 pool = pool.filter(function (a) {
33598 return !a.locationSetID || validLocations[a.locationSetID];
33602 var searchable = pool.filter(function (a) {
33603 return a.searchable !== false && a.suggestion !== true;
33605 var suggestions = pool.filter(function (a) {
33606 return a.suggestion === true;
33607 }); // matches value to preset.name
33609 var leadingNames = searchable.filter(function (a) {
33610 return leading(a.searchName());
33611 }).sort(sortPresets('searchName')); // matches value to preset suggestion name
33613 var leadingSuggestions = suggestions.filter(function (a) {
33614 return leadingStrict(a.searchName());
33615 }).sort(sortPresets('searchName'));
33616 var leadingNamesStripped = searchable.filter(function (a) {
33617 return leading(a.searchNameStripped());
33618 }).sort(sortPresets('searchNameStripped'));
33619 var leadingSuggestionsStripped = suggestions.filter(function (a) {
33620 return leadingStrict(a.searchNameStripped());
33621 }).sort(sortPresets('searchNameStripped')); // matches value to preset.terms values
33623 var leadingTerms = searchable.filter(function (a) {
33624 return (a.terms() || []).some(leading);
33626 var leadingSuggestionTerms = suggestions.filter(function (a) {
33627 return (a.terms() || []).some(leading);
33628 }); // matches value to preset.tags values
33630 var leadingTagValues = searchable.filter(function (a) {
33631 return Object.values(a.tags || {}).filter(function (val) {
33632 return val !== '*';
33634 }); // finds close matches to value in preset.name
33636 var similarName = searchable.map(function (a) {
33639 dist: utilEditDistance(value, a.searchName())
33641 }).filter(function (a) {
33642 return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 3;
33643 }).sort(function (a, b) {
33644 return a.dist - b.dist;
33645 }).map(function (a) {
33647 }); // finds close matches to value to preset suggestion name
33649 var similarSuggestions = suggestions.map(function (a) {
33652 dist: utilEditDistance(value, a.searchName())
33654 }).filter(function (a) {
33655 return a.dist + Math.min(value.length - a.preset.searchName().length, 0) < 1;
33656 }).sort(function (a, b) {
33657 return a.dist - b.dist;
33658 }).map(function (a) {
33660 }); // finds close matches to value in preset.terms
33662 var similarTerms = searchable.filter(function (a) {
33663 return (a.terms() || []).some(function (b) {
33664 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
33667 var results = leadingNames.concat(leadingSuggestions, leadingNamesStripped, leadingSuggestionsStripped, leadingTerms, leadingSuggestionTerms, leadingTagValues, similarName, similarSuggestions, similarTerms).slice(0, MAXRESULTS - 1);
33670 if (typeof geometry === 'string') {
33671 results.push(_this.fallback(geometry));
33673 geometry.forEach(function (geom) {
33674 return results.push(_this.fallback(geom));
33679 return presetCollection(utilArrayUniq(results));
33685 // `presetCategory` builds a `presetCollection` of member presets,
33686 // decorated with some extra methods for searching and matching geometry
33689 function presetCategory(categoryID, category, allPresets) {
33690 var _this = Object.assign({}, category); // shallow copy
33693 var _searchName; // cache
33696 var _searchNameStripped; // cache
33699 _this.id = categoryID;
33700 _this.members = presetCollection((category.members || []).map(function (presetID) {
33701 return allPresets[presetID];
33702 }).filter(Boolean));
33703 _this.geometry = _this.members.collection.reduce(function (acc, preset) {
33704 for (var i in preset.geometry) {
33705 var geometry = preset.geometry[i];
33707 if (acc.indexOf(geometry) === -1) {
33708 acc.push(geometry);
33715 _this.matchGeometry = function (geom) {
33716 return _this.geometry.indexOf(geom) >= 0;
33719 _this.matchAllGeometry = function (geometries) {
33720 return _this.members.collection.some(function (preset) {
33721 return preset.matchAllGeometry(geometries);
33725 _this.matchScore = function () {
33729 _this.name = function () {
33730 return _t("_tagging.presets.categories.".concat(categoryID, ".name"), {
33731 'default': categoryID
33735 _this.nameLabel = function () {
33736 return _t.html("_tagging.presets.categories.".concat(categoryID, ".name"), {
33737 'default': categoryID
33741 _this.terms = function () {
33745 _this.searchName = function () {
33746 if (!_searchName) {
33747 _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33750 return _searchName;
33753 _this.searchNameStripped = function () {
33754 if (!_searchNameStripped) {
33755 _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33757 if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33759 _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33762 return _searchNameStripped;
33768 // `presetField` decorates a given `field` Object
33769 // with some extra methods for searching and matching geometry
33772 function presetField(fieldID, field) {
33773 var _this = Object.assign({}, field); // shallow copy
33776 _this.id = fieldID; // for use in classes, element ids, css selectors
33778 _this.safeid = utilSafeClassName(fieldID);
33780 _this.matchGeometry = function (geom) {
33781 return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
33784 _this.matchAllGeometry = function (geometries) {
33785 return !_this.geometry || geometries.every(function (geom) {
33786 return _this.geometry.indexOf(geom) !== -1;
33790 _this.t = function (scope, options) {
33791 return _t("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33794 _this.t.html = function (scope, options) {
33795 return _t.html("_tagging.presets.fields.".concat(fieldID, ".").concat(scope), options);
33798 _this.hasTextForStringId = function (scope) {
33799 return _mainLocalizer.hasTextForStringId("_tagging.presets.fields.".concat(fieldID, ".").concat(scope));
33802 _this.title = function () {
33803 return _this.overrideLabel || _this.t('label', {
33808 _this.label = function () {
33809 return _this.overrideLabel || _this.t.html('label', {
33814 var _placeholder = _this.placeholder;
33816 _this.placeholder = function () {
33817 return _this.t('placeholder', {
33818 'default': _placeholder
33822 _this.originalTerms = (_this.terms || []).join();
33824 _this.terms = function () {
33825 return _this.t('terms', {
33826 'default': _this.originalTerms
33827 }).toLowerCase().trim().split(/\s*,+\s*/);
33830 _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
33834 // `Array.prototype.lastIndexOf` method
33835 // https://tc39.es/ecma262/#sec-array.prototype.lastindexof
33836 // eslint-disable-next-line es/no-array-prototype-lastindexof -- required for testing
33837 _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
33838 lastIndexOf: arrayLastIndexOf
33841 // `presetPreset` decorates a given `preset` Object
33842 // with some extra methods for searching and matching geometry
33845 function presetPreset(presetID, preset, addable, allFields, allPresets) {
33846 allFields = allFields || {};
33847 allPresets = allPresets || {};
33849 var _this = Object.assign({}, preset); // shallow copy
33852 var _addable = addable || false;
33854 var _resolvedFields; // cache
33857 var _resolvedMoreFields; // cache
33860 var _searchName; // cache
33863 var _searchNameStripped; // cache
33866 _this.id = presetID;
33867 _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
33869 _this.originalTerms = (_this.terms || []).join();
33870 _this.originalName = _this.name || '';
33871 _this.originalScore = _this.matchScore || 1;
33872 _this.originalReference = _this.reference || {};
33873 _this.originalFields = _this.fields || [];
33874 _this.originalMoreFields = _this.moreFields || [];
33876 _this.fields = function () {
33877 return _resolvedFields || (_resolvedFields = resolve('fields'));
33880 _this.moreFields = function () {
33881 return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
33884 _this.resetFields = function () {
33885 return _resolvedFields = _resolvedMoreFields = null;
33888 _this.tags = _this.tags || {};
33889 _this.addTags = _this.addTags || _this.tags;
33890 _this.removeTags = _this.removeTags || _this.addTags;
33891 _this.geometry = _this.geometry || [];
33893 _this.matchGeometry = function (geom) {
33894 return _this.geometry.indexOf(geom) >= 0;
33897 _this.matchAllGeometry = function (geoms) {
33898 return geoms.every(_this.matchGeometry);
33901 _this.matchScore = function (entityTags) {
33902 var tags = _this.tags;
33904 var score = 0; // match on tags
33906 for (var k in tags) {
33909 if (entityTags[k] === tags[k]) {
33910 score += _this.originalScore;
33911 } else if (tags[k] === '*' && k in entityTags) {
33912 score += _this.originalScore / 2;
33916 } // boost score for additional matches in addTags - #6802
33919 var addTags = _this.addTags;
33921 for (var _k in addTags) {
33922 if (!seen[_k] && entityTags[_k] === addTags[_k]) {
33923 score += _this.originalScore;
33930 _this.t = function (scope, options) {
33931 var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33932 return _t(textID, options);
33935 _this.t.html = function (scope, options) {
33936 var textID = "_tagging.presets.presets.".concat(presetID, ".").concat(scope);
33937 return _t.html(textID, options);
33940 _this.name = function () {
33941 return _this.t('name', {
33942 'default': _this.originalName
33946 _this.nameLabel = function () {
33947 return _this.t.html('name', {
33948 'default': _this.originalName
33952 _this.subtitle = function () {
33953 if (_this.suggestion) {
33954 var path = presetID.split('/');
33955 path.pop(); // remove brand name
33957 return _t('_tagging.presets.presets.' + path.join('/') + '.name');
33963 _this.subtitleLabel = function () {
33964 if (_this.suggestion) {
33965 var path = presetID.split('/');
33966 path.pop(); // remove brand name
33968 return _t.html('_tagging.presets.presets.' + path.join('/') + '.name');
33974 _this.terms = function () {
33975 return _this.t('terms', {
33976 'default': _this.originalTerms
33977 }).toLowerCase().trim().split(/\s*,+\s*/);
33980 _this.searchName = function () {
33981 if (!_searchName) {
33982 _searchName = (_this.suggestion ? _this.originalName : _this.name()).toLowerCase();
33985 return _searchName;
33988 _this.searchNameStripped = function () {
33989 if (!_searchNameStripped) {
33990 _searchNameStripped = _this.searchName(); // split combined diacritical characters into their parts
33992 if (_searchNameStripped.normalize) _searchNameStripped = _searchNameStripped.normalize('NFD'); // remove diacritics
33994 _searchNameStripped = _searchNameStripped.replace(/[\u0300-\u036f]/g, '');
33997 return _searchNameStripped;
34000 _this.isFallback = function () {
34001 var tagCount = Object.keys(_this.tags).length;
34002 return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
34005 _this.addable = function (val) {
34006 if (!arguments.length) return _addable;
34011 _this.reference = function () {
34012 // Lookup documentation on Wikidata...
34013 var qid = _this.tags.wikidata || _this.tags['flag:wikidata'] || _this.tags['brand:wikidata'] || _this.tags['network:wikidata'] || _this.tags['operator:wikidata'];
34019 } // Lookup documentation on OSM Wikibase...
34022 var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
34023 var value = _this.originalReference.value || _this.tags[key];
34025 if (value === '*') {
34037 _this.unsetTags = function (tags, geometry, ignoringKeys, skipFieldDefaults) {
34038 // allow manually keeping some tags
34039 var removeTags = ignoringKeys ? utilObjectOmit(_this.removeTags, ignoringKeys) : _this.removeTags;
34040 tags = utilObjectOmit(tags, Object.keys(removeTags));
34042 if (geometry && !skipFieldDefaults) {
34043 _this.fields().forEach(function (field) {
34044 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
34045 delete tags[field.key];
34054 _this.setTags = function (tags, geometry, skipFieldDefaults) {
34055 var addTags = _this.addTags;
34056 tags = Object.assign({}, tags); // shallow copy
34058 for (var k in addTags) {
34059 if (addTags[k] === '*') {
34060 // if this tag is ancillary, don't override an existing value since any value is okay
34061 if (_this.tags[k] || !tags[k] || tags[k] === 'no') {
34065 tags[k] = addTags[k];
34067 } // Add area=yes if necessary.
34068 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
34069 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
34070 // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
34073 if (!addTags.hasOwnProperty('area')) {
34076 if (geometry === 'area') {
34077 var needsAreaTag = true;
34079 if (_this.geometry.indexOf('line') === -1) {
34080 for (var _k2 in addTags) {
34081 if (_k2 in osmAreaKeys) {
34082 needsAreaTag = false;
34088 if (needsAreaTag) {
34094 if (geometry && !skipFieldDefaults) {
34095 _this.fields().forEach(function (field) {
34096 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
34097 tags[field.key] = field["default"];
34103 }; // For a preset without fields, use the fields of the parent preset.
34104 // Replace {preset} placeholders with the fields of the specified presets.
34107 function resolve(which) {
34108 var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
34110 fieldIDs.forEach(function (fieldID) {
34111 var match = fieldID.match(/\{(.*)\}/);
34113 if (match !== null) {
34114 // a presetID wrapped in braces {}
34115 resolved = resolved.concat(inheritFields(match[1], which));
34116 } else if (allFields[fieldID]) {
34117 // a normal fieldID
34118 resolved.push(allFields[fieldID]);
34120 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
34122 }); // no fields resolved, so use the parent's if possible
34124 if (!resolved.length) {
34125 var endIndex = _this.id.lastIndexOf('/');
34127 var parentID = endIndex && _this.id.substring(0, endIndex);
34130 resolved = inheritFields(parentID, which);
34134 return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
34136 function inheritFields(presetID, which) {
34137 var parent = allPresets[presetID];
34138 if (!parent) return [];
34140 if (which === 'fields') {
34141 return parent.fields().filter(shouldInherit);
34142 } else if (which === 'moreFields') {
34143 return parent.moreFields();
34147 } // Skip `fields` for the keys which define the preset.
34148 // These are usually `typeCombo` fields like `shop=*`
34151 function shouldInherit(f) {
34152 if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
34153 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
34161 var _mainPresetIndex = presetIndex(); // singleton
34162 // `presetIndex` wraps a `presetCollection`
34163 // with methods for loading new data and returning defaults
34166 function presetIndex() {
34167 var dispatch = dispatch$8('favoritePreset', 'recentsChange');
34168 var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
34170 var POINT = presetPreset('point', {
34173 geometry: ['point', 'vertex'],
34176 var LINE = presetPreset('line', {
34179 geometry: ['line'],
34182 var AREA = presetPreset('area', {
34187 geometry: ['area'],
34190 var RELATION = presetPreset('relation', {
34193 geometry: ['relation'],
34197 var _this = presetCollection([POINT, LINE, AREA, RELATION]);
34206 point: presetCollection([POINT]),
34207 vertex: presetCollection([POINT]),
34208 line: presetCollection([LINE]),
34209 area: presetCollection([AREA]),
34210 relation: presetCollection([RELATION])
34213 var _categories = {};
34214 var _universal = [];
34215 var _addablePresetIDs = null; // Set of preset IDs that the user can add
34219 var _favorites; // Index of presets by (geometry, tag key).
34222 var _geometryIndex = {
34232 _this.ensureLoaded = function () {
34233 if (_loadPromise) return _loadPromise;
34234 return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
34236 categories: vals[0],
34242 osmSetAreaKeys(_this.areaKeys());
34243 osmSetPointTags(_this.pointTags());
34244 osmSetVertexTags(_this.vertexTags());
34246 }; // `merge` accepts an object containing new preset data (all properties optional):
34252 // featureCollection: {}
34256 _this.merge = function (d) {
34257 var newLocationSets = []; // Merge Fields
34260 Object.keys(d.fields).forEach(function (fieldID) {
34261 var f = d.fields[fieldID];
34265 f = presetField(fieldID, f);
34266 if (f.locationSet) newLocationSets.push(f);
34267 _fields[fieldID] = f;
34270 delete _fields[fieldID];
34277 Object.keys(d.presets).forEach(function (presetID) {
34278 var p = d.presets[presetID];
34282 var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
34284 p = presetPreset(presetID, p, isAddable, _fields, _presets);
34285 if (p.locationSet) newLocationSets.push(p);
34286 _presets[presetID] = p;
34288 // remove (but not if it's a fallback)
34289 var existing = _presets[presetID];
34291 if (existing && !existing.isFallback()) {
34292 delete _presets[presetID];
34296 } // Merge Categories
34299 if (d.categories) {
34300 Object.keys(d.categories).forEach(function (categoryID) {
34301 var c = d.categories[categoryID];
34305 c = presetCategory(categoryID, c, _presets);
34306 if (c.locationSet) newLocationSets.push(c);
34307 _categories[categoryID] = c;
34310 delete _categories[categoryID];
34313 } // Rebuild _this.collection after changing presets and categories
34316 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
34319 Object.keys(d.defaults).forEach(function (geometry) {
34320 var def = d.defaults[geometry];
34322 if (Array.isArray(def)) {
34324 _defaults[geometry] = presetCollection(def.map(function (id) {
34325 return _presets[id] || _categories[id];
34326 }).filter(Boolean));
34329 delete _defaults[geometry];
34332 } // Rebuild universal fields array
34335 _universal = Object.values(_fields).filter(function (field) {
34336 return field.universal;
34337 }); // Reset all the preset fields - they'll need to be resolved again
34339 Object.values(_presets).forEach(function (preset) {
34340 return preset.resetFields();
34341 }); // Rebuild geometry index
34351 _this.collection.forEach(function (preset) {
34352 (preset.geometry || []).forEach(function (geometry) {
34353 var g = _geometryIndex[geometry];
34355 for (var key in preset.tags) {
34356 (g[key] = g[key] || []).push(preset);
34359 }); // Merge Custom Features
34362 if (d.featureCollection && Array.isArray(d.featureCollection.features)) {
34363 _mainLocations.mergeCustomGeoJSON(d.featureCollection);
34364 } // Resolve all locationSet features.
34367 if (newLocationSets.length) {
34368 _mainLocations.mergeLocationSets(newLocationSets);
34374 _this.match = function (entity, resolver) {
34375 return resolver["transient"](entity, 'presetMatch', function () {
34376 var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
34378 if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
34379 geometry = 'point';
34382 var entityExtent = entity.extent(resolver);
34383 return _this.matchTags(entity.tags, geometry, entityExtent.center());
34387 _this.matchTags = function (tags, geometry, loc) {
34388 var geometryMatches = _geometryIndex[geometry];
34392 var validLocations;
34394 if (Array.isArray(loc)) {
34395 validLocations = _mainLocations.locationsAt(loc);
34398 for (var k in tags) {
34399 // If any part of an address is present, allow fallback to "Address" preset - #4353
34400 if (/^addr:/.test(k) && geometryMatches['addr:*']) {
34401 address = geometryMatches['addr:*'][0];
34404 var keyMatches = geometryMatches[k];
34405 if (!keyMatches) continue;
34407 for (var i = 0; i < keyMatches.length; i++) {
34408 var candidate = keyMatches[i]; // discard candidate preset if location is not valid at `loc`
34410 if (validLocations && candidate.locationSetID) {
34411 if (!validLocations[candidate.locationSetID]) continue;
34414 var score = candidate.matchScore(tags);
34416 if (score > best) {
34423 if (address && (!match || match.isFallback())) {
34427 return match || _this.fallback(geometry);
34430 _this.allowsVertex = function (entity, resolver) {
34431 if (entity.type !== 'node') return false;
34432 if (Object.keys(entity.tags).length === 0) return true;
34433 return resolver["transient"](entity, 'vertexMatch', function () {
34434 // address lines allow vertices to act as standalone points
34435 if (entity.isOnAddressLine(resolver)) return true;
34436 var geometries = osmNodeGeometriesForTags(entity.tags);
34437 if (geometries.vertex) return true;
34438 if (geometries.point) return false; // allow vertices for unspecified points
34442 }; // Because of the open nature of tagging, iD will never have a complete
34443 // list of tags used in OSM, so we want it to have logic like "assume
34444 // that a closed way with an amenity tag is an area, unless the amenity
34445 // is one of these specific types". This function computes a structure
34446 // that allows testing of such conditions, based on the presets designated
34447 // as as supporting (or not supporting) the area geometry.
34449 // The returned object L is a keeplist/discardlist of tags. A closed way
34450 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
34451 // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
34452 // and the subkeys form the discardlist.
34455 _this.areaKeys = function () {
34456 // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
34457 var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
34458 var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
34460 var presets = _this.collection.filter(function (p) {
34461 return !p.suggestion && !p.replacement;
34465 presets.forEach(function (p) {
34466 var keys = p.tags && Object.keys(p.tags);
34467 var key = keys && keys.length && keys[0]; // pick the first tag
34470 if (ignore.indexOf(key) !== -1) return;
34472 if (p.geometry.indexOf('area') !== -1) {
34473 // probably an area..
34474 areaKeys[key] = areaKeys[key] || {};
34478 presets.forEach(function (p) {
34481 for (key in p.addTags) {
34482 // examine all addTags to get a better sense of what can be tagged on lines - #6800
34483 var value = p.addTags[key];
34485 if (key in areaKeys && // probably an area...
34486 p.geometry.indexOf('line') !== -1 && // but sometimes a line
34488 areaKeys[key][value] = true;
34495 _this.pointTags = function () {
34496 return _this.collection.reduce(function (pointTags, d) {
34497 // ignore name-suggestion-index, deprecated, and generic presets
34498 if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
34500 var keys = d.tags && Object.keys(d.tags);
34501 var key = keys && keys.length && keys[0]; // pick the first tag
34503 if (!key) return pointTags; // if this can be a point
34505 if (d.geometry.indexOf('point') !== -1) {
34506 pointTags[key] = pointTags[key] || {};
34507 pointTags[key][d.tags[key]] = true;
34514 _this.vertexTags = function () {
34515 return _this.collection.reduce(function (vertexTags, d) {
34516 // ignore name-suggestion-index, deprecated, and generic presets
34517 if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
34519 var keys = d.tags && Object.keys(d.tags);
34520 var key = keys && keys.length && keys[0]; // pick the first tag
34522 if (!key) return vertexTags; // if this can be a vertex
34524 if (d.geometry.indexOf('vertex') !== -1) {
34525 vertexTags[key] = vertexTags[key] || {};
34526 vertexTags[key][d.tags[key]] = true;
34533 _this.field = function (id) {
34534 return _fields[id];
34537 _this.universal = function () {
34541 _this.defaults = function (geometry, n, startWithRecents, loc) {
34544 if (startWithRecents) {
34545 recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
34550 if (_addablePresetIDs) {
34551 defaults = Array.from(_addablePresetIDs).map(function (id) {
34552 var preset = _this.item(id);
34554 if (preset && preset.matchGeometry(geometry)) return preset;
34556 }).filter(Boolean);
34558 defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
34561 var result = presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
34563 if (Array.isArray(loc)) {
34564 var validLocations = _mainLocations.locationsAt(loc);
34565 result.collection = result.collection.filter(function (a) {
34566 return !a.locationSetID || validLocations[a.locationSetID];
34571 }; // pass a Set of addable preset ids
34574 _this.addablePresetIDs = function (val) {
34575 if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
34577 if (Array.isArray(val)) val = new Set(val);
34578 _addablePresetIDs = val;
34580 if (_addablePresetIDs) {
34581 // reset all presets
34582 _this.collection.forEach(function (p) {
34583 // categories aren't addable
34584 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
34587 _this.collection.forEach(function (p) {
34588 if (p.addable) p.addable(true);
34595 _this.recent = function () {
34596 return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
34601 function RibbonItem(preset, source) {
34603 item.preset = preset;
34604 item.source = source;
34606 item.isFavorite = function () {
34607 return item.source === 'favorite';
34610 item.isRecent = function () {
34611 return item.source === 'recent';
34614 item.matches = function (preset) {
34615 return item.preset.id === preset.id;
34618 item.minified = function () {
34620 pID: item.preset.id
34627 function ribbonItemForMinified(d, source) {
34629 var preset = _this.item(d.pID);
34631 if (!preset) return null;
34632 return RibbonItem(preset, source);
34638 _this.getGenericRibbonItems = function () {
34639 return ['point', 'line', 'area'].map(function (id) {
34640 return RibbonItem(_this.item(id), 'generic');
34644 _this.getAddable = function () {
34645 if (!_addablePresetIDs) return [];
34646 return _addablePresetIDs.map(function (id) {
34647 var preset = _this.item(id);
34649 if (preset) return RibbonItem(preset, 'addable');
34651 }).filter(Boolean);
34654 function setRecents(items) {
34656 var minifiedItems = items.map(function (d) {
34657 return d.minified();
34659 corePreferences('preset_recents', JSON.stringify(minifiedItems));
34660 dispatch.call('recentsChange');
34663 _this.getRecents = function () {
34665 // fetch from local storage
34666 _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
34667 var item = ribbonItemForMinified(d, 'recent');
34668 if (item && item.preset.addable()) acc.push(item);
34676 _this.addRecent = function (preset, besidePreset, after) {
34677 var recents = _this.getRecents();
34679 var beforeItem = _this.recentMatching(besidePreset);
34681 var toIndex = recents.indexOf(beforeItem);
34682 if (after) toIndex += 1;
34683 var newItem = RibbonItem(preset, 'recent');
34684 recents.splice(toIndex, 0, newItem);
34685 setRecents(recents);
34688 _this.removeRecent = function (preset) {
34689 var item = _this.recentMatching(preset);
34692 var items = _this.getRecents();
34694 items.splice(items.indexOf(item), 1);
34699 _this.recentMatching = function (preset) {
34700 var items = _this.getRecents();
34702 for (var i in items) {
34703 if (items[i].matches(preset)) {
34711 _this.moveItem = function (items, fromIndex, toIndex) {
34712 if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
34713 items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
34717 _this.moveRecent = function (item, beforeItem) {
34718 var recents = _this.getRecents();
34720 var fromIndex = recents.indexOf(item);
34721 var toIndex = recents.indexOf(beforeItem);
34723 var items = _this.moveItem(recents, fromIndex, toIndex);
34725 if (items) setRecents(items);
34728 _this.setMostRecent = function (preset) {
34729 if (preset.searchable === false) return;
34731 var items = _this.getRecents();
34733 var item = _this.recentMatching(preset);
34736 items.splice(items.indexOf(item), 1);
34738 item = RibbonItem(preset, 'recent');
34739 } // remove the last recent (first in, first out)
34742 while (items.length >= MAXRECENTS) {
34747 items.unshift(item);
34751 function setFavorites(items) {
34752 _favorites = items;
34753 var minifiedItems = items.map(function (d) {
34754 return d.minified();
34756 corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
34758 dispatch.call('favoritePreset');
34761 _this.addFavorite = function (preset, besidePreset, after) {
34762 var favorites = _this.getFavorites();
34764 var beforeItem = _this.favoriteMatching(besidePreset);
34766 var toIndex = favorites.indexOf(beforeItem);
34767 if (after) toIndex += 1;
34768 var newItem = RibbonItem(preset, 'favorite');
34769 favorites.splice(toIndex, 0, newItem);
34770 setFavorites(favorites);
34773 _this.toggleFavorite = function (preset) {
34774 var favs = _this.getFavorites();
34776 var favorite = _this.favoriteMatching(preset);
34779 favs.splice(favs.indexOf(favorite), 1);
34781 // only allow 10 favorites
34782 if (favs.length === 10) {
34783 // remove the last favorite (last in, first out)
34788 favs.push(RibbonItem(preset, 'favorite'));
34791 setFavorites(favs);
34794 _this.removeFavorite = function (preset) {
34795 var item = _this.favoriteMatching(preset);
34798 var items = _this.getFavorites();
34800 items.splice(items.indexOf(item), 1);
34801 setFavorites(items);
34805 _this.getFavorites = function () {
34807 // fetch from local storage
34808 var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
34810 if (!rawFavorites) {
34812 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
34815 _favorites = rawFavorites.reduce(function (output, d) {
34816 var item = ribbonItemForMinified(d, 'favorite');
34817 if (item && item.preset.addable()) output.push(item);
34825 _this.favoriteMatching = function (preset) {
34826 var favs = _this.getFavorites();
34828 for (var index in favs) {
34829 if (favs[index].matches(preset)) {
34830 return favs[index];
34837 return utilRebind(_this, dispatch, 'on');
34840 function utilTagText(entity) {
34841 var obj = entity && entity.tags || {};
34842 return Object.keys(obj).map(function (k) {
34843 return k + '=' + obj[k];
34846 function utilTotalExtent(array, graph) {
34847 var extent = geoExtent();
34850 for (var i = 0; i < array.length; i++) {
34852 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
34855 extent._extend(entity.extent(graph));
34861 function utilTagDiff(oldTags, newTags) {
34863 var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
34864 keys.forEach(function (k) {
34865 var oldVal = oldTags[k];
34866 var newVal = newTags[k];
34868 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
34874 display: '- ' + k + '=' + oldVal
34878 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
34884 display: '+ ' + k + '=' + newVal
34890 function utilEntitySelector(ids) {
34891 return ids.length ? '.' + ids.join(',.') : 'nothing';
34892 } // returns an selector to select entity ids for:
34893 // - entityIDs passed in
34894 // - shallow descendant entityIDs for any of those entities that are relations
34896 function utilEntityOrMemberSelector(ids, graph) {
34897 var seen = new Set(ids);
34898 ids.forEach(collectShallowDescendants);
34899 return utilEntitySelector(Array.from(seen));
34901 function collectShallowDescendants(id) {
34902 var entity = graph.hasEntity(id);
34903 if (!entity || entity.type !== 'relation') return;
34904 entity.members.map(function (member) {
34906 }).forEach(function (id) {
34910 } // returns an selector to select entity ids for:
34911 // - entityIDs passed in
34912 // - deep descendant entityIDs for any of those entities that are relations
34914 function utilEntityOrDeepMemberSelector(ids, graph) {
34915 return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
34916 } // returns an selector to select entity ids for:
34917 // - entityIDs passed in
34918 // - deep descendant entityIDs for any of those entities that are relations
34920 function utilEntityAndDeepMemberIDs(ids, graph) {
34921 var seen = new Set();
34922 ids.forEach(collectDeepDescendants);
34923 return Array.from(seen);
34925 function collectDeepDescendants(id) {
34926 if (seen.has(id)) return;
34928 var entity = graph.hasEntity(id);
34929 if (!entity || entity.type !== 'relation') return;
34930 entity.members.map(function (member) {
34932 }).forEach(collectDeepDescendants); // recurse
34934 } // returns an selector to select entity ids for:
34935 // - deep descendant entityIDs for any of those entities that are relations
34937 function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
34938 var idsSet = new Set(ids);
34939 var seen = new Set();
34940 var returners = new Set();
34941 ids.forEach(collectDeepDescendants);
34942 return utilEntitySelector(Array.from(returners));
34944 function collectDeepDescendants(id) {
34945 if (seen.has(id)) return;
34948 if (!idsSet.has(id)) {
34952 var entity = graph.hasEntity(id);
34953 if (!entity || entity.type !== 'relation') return;
34954 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
34955 entity.members.map(function (member) {
34957 }).forEach(collectDeepDescendants); // recurse
34959 } // Adds or removes highlight styling for the specified entities
34961 function utilHighlightEntities(ids, highlighted, context) {
34962 context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
34963 } // returns an Array that is the union of:
34964 // - nodes for any nodeIDs passed in
34965 // - child nodes of any wayIDs passed in
34966 // - descendant member and child nodes of relationIDs passed in
34968 function utilGetAllNodes(ids, graph) {
34969 var seen = new Set();
34970 var nodes = new Set();
34971 ids.forEach(collectNodes);
34972 return Array.from(nodes);
34974 function collectNodes(id) {
34975 if (seen.has(id)) return;
34977 var entity = graph.hasEntity(id);
34978 if (!entity) return;
34980 if (entity.type === 'node') {
34982 } else if (entity.type === 'way') {
34983 entity.nodes.forEach(collectNodes);
34985 entity.members.map(function (member) {
34987 }).forEach(collectNodes); // recurse
34991 function utilDisplayName(entity) {
34992 var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
34993 var name = entity.tags[localizedNameKey] || entity.tags.name || '';
34994 if (name) return name;
34996 direction: entity.tags.direction,
34997 from: entity.tags.from,
34998 network: entity.tags.cycle_network || entity.tags.network,
34999 ref: entity.tags.ref,
35000 to: entity.tags.to,
35001 via: entity.tags.via
35003 var keyComponents = [];
35005 if (tags.network) {
35006 keyComponents.push('network');
35010 keyComponents.push('ref');
35011 } // Routes may need more disambiguation based on direction or destination
35014 if (entity.tags.route) {
35015 if (tags.direction) {
35016 keyComponents.push('direction');
35017 } else if (tags.from && tags.to) {
35018 keyComponents.push('from');
35019 keyComponents.push('to');
35022 keyComponents.push('via');
35027 if (keyComponents.length) {
35028 name = _t('inspector.display_name.' + keyComponents.join('_'), tags);
35033 function utilDisplayNameForPath(entity) {
35034 var name = utilDisplayName(entity);
35035 var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
35037 if (!isFirefox && name && rtlRegex.test(name)) {
35038 name = fixRTLTextForSvg(name);
35043 function utilDisplayType(id) {
35045 n: _t('inspector.node'),
35046 w: _t('inspector.way'),
35047 r: _t('inspector.relation')
35049 } // `utilDisplayLabel`
35050 // Returns a string suitable for display
35051 // By default returns something like name/ref, fallback to preset type, fallback to OSM type
35052 // "Main Street" or "Tertiary Road"
35053 // If `verbose=true`, include both preset name and feature name.
35054 // "Tertiary Road Main Street"
35057 function utilDisplayLabel(entity, graphOrGeometry, verbose) {
35059 var displayName = utilDisplayName(entity);
35060 var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
35061 var presetName = preset && (preset.suggestion ? preset.subtitle() : preset.name());
35064 result = [presetName, displayName].filter(Boolean).join(' ');
35066 result = displayName || presetName;
35067 } // Fallback to the OSM type (node/way/relation)
35070 return result || utilDisplayType(entity.id);
35072 function utilEntityRoot(entityType) {
35078 } // Returns a single object containing the tags of all the given entities.
35081 // highway: 'service',
35082 // service: 'parking_aisle'
35086 // highway: 'service',
35087 // service: 'driveway',
35092 // highway: 'service',
35093 // service: [ 'driveway', 'parking_aisle' ],
35094 // width: [ '3', undefined ]
35097 function utilCombinedTags(entityIDs, graph) {
35099 var tagCounts = {};
35100 var allKeys = new Set();
35101 var entities = entityIDs.map(function (entityID) {
35102 return graph.hasEntity(entityID);
35103 }).filter(Boolean); // gather the aggregate keys
35105 entities.forEach(function (entity) {
35106 var keys = Object.keys(entity.tags).filter(Boolean);
35107 keys.forEach(function (key) {
35111 entities.forEach(function (entity) {
35112 allKeys.forEach(function (key) {
35113 var value = entity.tags[key]; // purposely allow `undefined`
35115 if (!tags.hasOwnProperty(key)) {
35116 // first value, set as raw
35119 if (!Array.isArray(tags[key])) {
35120 if (tags[key] !== value) {
35121 // first alternate value, replace single value with array
35122 tags[key] = [tags[key], value];
35126 if (tags[key].indexOf(value) === -1) {
35127 // subsequent alternate value, add to array
35128 tags[key].push(value);
35133 var tagHash = key + '=' + value;
35134 if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
35135 tagCounts[tagHash] += 1;
35139 for (var key in tags) {
35140 if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
35142 tags[key] = tags[key].sort(function (val1, val2) {
35143 var key = key; // capture
35145 var count2 = tagCounts[key + '=' + val2];
35146 var count1 = tagCounts[key + '=' + val1];
35148 if (count2 !== count1) {
35149 return count2 - count1;
35152 if (val2 && val1) {
35153 return val1.localeCompare(val2);
35156 return val1 ? 1 : -1;
35162 function utilStringQs(str) {
35163 var i = 0; // advance past any leading '?' or '#' characters
35165 while (i < str.length && (str[i] === '?' || str[i] === '#')) {
35169 str = str.slice(i);
35170 return str.split('&').reduce(function (obj, pair) {
35171 var parts = pair.split('=');
35173 if (parts.length === 2) {
35174 obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
35180 function utilQsString(obj, noencode) {
35181 // encode everything except special characters used in certain hash parameters:
35182 // "/" in map states, ":", ",", {" and "}" in background
35183 function softEncode(s) {
35184 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
35187 return Object.keys(obj).sort().map(function (key) {
35188 return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
35191 function utilPrefixDOMProperty(property) {
35192 var prefixes = ['webkit', 'ms', 'moz', 'o'];
35194 var n = prefixes.length;
35195 var s = document.body;
35196 if (property in s) return property;
35197 property = property.substr(0, 1).toUpperCase() + property.substr(1);
35200 if (prefixes[i] + property in s) {
35201 return prefixes[i] + property;
35207 function utilPrefixCSSProperty(property) {
35208 var prefixes = ['webkit', 'ms', 'Moz', 'O'];
35210 var n = prefixes.length;
35211 var s = document.body.style;
35213 if (property.toLowerCase() in s) {
35214 return property.toLowerCase();
35218 if (prefixes[i] + property in s) {
35219 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
35225 var transformProperty;
35226 function utilSetTransform(el, x, y, scale) {
35227 var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
35228 var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
35229 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
35230 } // Calculates Levenshtein distance between two strings
35231 // see: https://en.wikipedia.org/wiki/Levenshtein_distance
35232 // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
35234 function utilEditDistance(a, b) {
35235 a = remove$6(a.toLowerCase());
35236 b = remove$6(b.toLowerCase());
35237 if (a.length === 0) return b.length;
35238 if (b.length === 0) return a.length;
35242 for (i = 0; i <= b.length; i++) {
35246 for (j = 0; j <= a.length; j++) {
35250 for (i = 1; i <= b.length; i++) {
35251 for (j = 1; j <= a.length; j++) {
35252 if (b.charAt(i - 1) === a.charAt(j - 1)) {
35253 matrix[i][j] = matrix[i - 1][j - 1];
35255 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
35256 Math.min(matrix[i][j - 1] + 1, // insertion
35257 matrix[i - 1][j] + 1)); // deletion
35262 return matrix[b.length][a.length];
35263 } // a d3.mouse-alike which
35264 // 1. Only works on HTML elements, not SVG
35265 // 2. Does not cause style recalculation
35267 function utilFastMouse(container) {
35268 var rect = container.getBoundingClientRect();
35269 var rectLeft = rect.left;
35270 var rectTop = rect.top;
35271 var clientLeft = +container.clientLeft;
35272 var clientTop = +container.clientTop;
35273 return function (e) {
35274 return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
35277 function utilAsyncMap(inputs, func, callback) {
35278 var remaining = inputs.length;
35281 inputs.forEach(function (d, i) {
35282 func(d, function done(err, data) {
35286 if (!remaining) callback(errors, results);
35289 } // wraps an index to an interval [0..length-1]
35291 function utilWrap(index, length) {
35293 index += Math.ceil(-index / length) * length;
35296 return index % length;
35299 * a replacement for functor
35301 * @param {*} value any value
35302 * @returns {Function} a function that returns that value or the value if it's a function
35305 function utilFunctor(value) {
35306 if (typeof value === 'function') return value;
35307 return function () {
35311 function utilNoAuto(selection) {
35312 var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
35313 return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
35314 .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
35315 } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
35316 // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
35318 function utilHashcode(str) {
35321 if (str.length === 0) {
35325 for (var i = 0; i < str.length; i++) {
35326 var _char = str.charCodeAt(i);
35328 hash = (hash << 5) - hash + _char;
35329 hash = hash & hash; // Convert to 32bit integer
35333 } // Returns version of `str` with all runs of special characters replaced by `_`;
35334 // suitable for HTML ids, classes, selectors, etc.
35336 function utilSafeClassName(str) {
35337 return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
35338 } // Returns string based on `val` that is highly unlikely to collide with an id
35339 // used previously or that's present elsewhere in the document. Useful for preventing
35340 // browser-provided autofills or when embedding iD on pages with unknown elements.
35342 function utilUniqueDomId(val) {
35343 return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
35344 } // Returns the length of `str` in unicode characters. This can be less than
35345 // `String.length()` since a single unicode character can be composed of multiple
35346 // JavaScript UTF-16 code units.
35348 function utilUnicodeCharsCount(str) {
35349 // Native ES2015 implementations of `Array.from` split strings into unicode characters
35350 return Array.from(str).length;
35351 } // Returns a new string representing `str` cut from its start to `limit` length
35352 // in unicode characters. Note that this runs the risk of splitting graphemes.
35354 function utilUnicodeCharsTruncated(str, limit) {
35355 return Array.from(str).slice(0, limit).join('');
35356 } // Variation of d3.json (https://github.com/d3/d3-fetch/blob/master/src/json.js)
35358 function utilFetchJson(resourse, init) {
35359 return fetch(resourse, init).then(function (response) {
35360 // fetch in PhantomJS tests may return ok=false and status=0 even if it's okay
35361 if (!response.ok && response.status !== 0 || !response.json) throw new Error(response.status + ' ' + response.statusText);
35362 if (response.status === 204 || response.status === 205) return;
35363 return response.json();
35367 function osmEntity(attrs) {
35368 // For prototypal inheritance.
35369 if (this instanceof osmEntity) return; // Create the appropriate subtype.
35371 if (attrs && attrs.type) {
35372 return osmEntity[attrs.type].apply(this, arguments);
35373 } else if (attrs && attrs.id) {
35374 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
35375 } // Initialize a generic Entity (used only in tests).
35378 return new osmEntity().initialize(arguments);
35381 osmEntity.id = function (type) {
35382 return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
35385 osmEntity.id.next = {
35392 osmEntity.id.fromOSM = function (type, id) {
35393 return type[0] + id;
35396 osmEntity.id.toOSM = function (id) {
35397 return id.slice(1);
35400 osmEntity.id.type = function (id) {
35407 }; // A function suitable for use as the second argument to d3.selection#data().
35410 osmEntity.key = function (entity) {
35411 return entity.id + 'v' + (entity.v || 0);
35414 var _deprecatedTagValuesByKey;
35416 osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
35417 if (!_deprecatedTagValuesByKey) {
35418 _deprecatedTagValuesByKey = {};
35419 dataDeprecated.forEach(function (d) {
35420 var oldKeys = Object.keys(d.old);
35422 if (oldKeys.length === 1) {
35423 var oldKey = oldKeys[0];
35424 var oldValue = d.old[oldKey];
35426 if (oldValue !== '*') {
35427 if (!_deprecatedTagValuesByKey[oldKey]) {
35428 _deprecatedTagValuesByKey[oldKey] = [oldValue];
35430 _deprecatedTagValuesByKey[oldKey].push(oldValue);
35437 return _deprecatedTagValuesByKey;
35440 osmEntity.prototype = {
35442 initialize: function initialize(sources) {
35443 for (var i = 0; i < sources.length; ++i) {
35444 var source = sources[i];
35446 for (var prop in source) {
35447 if (Object.prototype.hasOwnProperty.call(source, prop)) {
35448 if (source[prop] === undefined) {
35451 this[prop] = source[prop];
35457 if (!this.id && this.type) {
35458 this.id = osmEntity.id(this.type);
35461 if (!this.hasOwnProperty('visible')) {
35462 this.visible = true;
35466 Object.freeze(this);
35467 Object.freeze(this.tags);
35468 if (this.loc) Object.freeze(this.loc);
35469 if (this.nodes) Object.freeze(this.nodes);
35470 if (this.members) Object.freeze(this.members);
35475 copy: function copy(resolver, copies) {
35476 if (copies[this.id]) return copies[this.id];
35477 var copy = osmEntity(this, {
35482 copies[this.id] = copy;
35485 osmId: function osmId() {
35486 return osmEntity.id.toOSM(this.id);
35488 isNew: function isNew() {
35489 return this.osmId() < 0;
35491 update: function update(attrs) {
35492 return osmEntity(this, attrs, {
35493 v: 1 + (this.v || 0)
35496 mergeTags: function mergeTags(tags) {
35497 var merged = Object.assign({}, this.tags); // shallow copy
35499 var changed = false;
35501 for (var k in tags) {
35502 var t1 = merged[k];
35508 } else if (t1 !== t2) {
35510 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
35515 return changed ? this.update({
35519 intersects: function intersects(extent, resolver) {
35520 return this.extent(resolver).intersects(extent);
35522 hasNonGeometryTags: function hasNonGeometryTags() {
35523 return Object.keys(this.tags).some(function (k) {
35524 return k !== 'area';
35527 hasParentRelations: function hasParentRelations(resolver) {
35528 return resolver.parentRelations(this).length > 0;
35530 hasInterestingTags: function hasInterestingTags() {
35531 return Object.keys(this.tags).some(osmIsInterestingTag);
35533 isHighwayIntersection: function isHighwayIntersection() {
35536 isDegenerate: function isDegenerate() {
35539 deprecatedTags: function deprecatedTags(dataDeprecated) {
35540 var tags = this.tags; // if there are no tags, none can be deprecated
35542 if (Object.keys(tags).length === 0) return [];
35543 var deprecated = [];
35544 dataDeprecated.forEach(function (d) {
35545 var oldKeys = Object.keys(d.old);
35548 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
35549 if (!tags[replaceKey] || d.old[replaceKey]) return false;
35550 var replaceValue = d.replace[replaceKey];
35551 if (replaceValue === '*') return false;
35552 if (replaceValue === tags[replaceKey]) return false;
35554 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
35556 if (hasExistingValues) return;
35559 var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
35560 if (!tags[oldKey]) return false;
35561 if (d.old[oldKey] === '*') return true;
35562 if (d.old[oldKey] === tags[oldKey]) return true;
35563 var vals = tags[oldKey].split(';').filter(Boolean);
35565 if (vals.length === 0) {
35567 } else if (vals.length > 1) {
35568 return vals.indexOf(d.old[oldKey]) !== -1;
35570 if (tags[oldKey] === d.old[oldKey]) {
35571 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
35572 var replaceKeys = Object.keys(d.replace);
35573 return !replaceKeys.every(function (replaceKey) {
35574 return tags[replaceKey] === d.replace[replaceKey];
35585 if (matchesDeprecatedTags) {
35586 deprecated.push(d);
35593 function osmLanes(entity) {
35594 if (entity.type !== 'way') return null;
35595 if (!entity.tags.highway) return null;
35596 var tags = entity.tags;
35597 var isOneWay = entity.isOneWay();
35598 var laneCount = getLaneCount(tags, isOneWay);
35599 var maxspeed = parseMaxspeed(tags);
35600 var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
35601 var forward = laneDirections.forward;
35602 var backward = laneDirections.backward;
35603 var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
35605 var turnLanes = {};
35606 turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
35607 turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
35608 turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
35609 var maxspeedLanes = {};
35610 maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
35611 maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
35612 maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
35614 psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
35615 psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
35616 psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
35618 busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
35619 busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
35620 busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
35621 var taxiLanes = {};
35622 taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
35623 taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
35624 taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
35626 hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
35627 hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
35628 hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
35630 hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
35631 hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
35632 hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
35633 var bicyclewayLanes = {};
35634 bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
35635 bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
35636 bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
35641 }; // map forward/backward/unspecified of each lane type to lanesObj
35643 mapToLanesObj(lanesObj, turnLanes, 'turnLane');
35644 mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
35645 mapToLanesObj(lanesObj, psvLanes, 'psv');
35646 mapToLanesObj(lanesObj, busLanes, 'bus');
35647 mapToLanesObj(lanesObj, taxiLanes, 'taxi');
35648 mapToLanesObj(lanesObj, hovLanes, 'hov');
35649 mapToLanesObj(lanesObj, hgvLanes, 'hgv');
35650 mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
35656 backward: backward,
35657 bothways: bothways,
35658 turnLanes: turnLanes,
35659 maxspeed: maxspeed,
35660 maxspeedLanes: maxspeedLanes,
35661 psvLanes: psvLanes,
35662 busLanes: busLanes,
35663 taxiLanes: taxiLanes,
35664 hovLanes: hovLanes,
35665 hgvLanes: hgvLanes,
35666 bicyclewayLanes: bicyclewayLanes
35672 function getLaneCount(tags, isOneWay) {
35676 count = parseInt(tags.lanes, 10);
35683 switch (tags.highway) {
35686 count = isOneWay ? 2 : 4;
35690 count = isOneWay ? 1 : 2;
35697 function parseMaxspeed(tags) {
35698 var maxspeed = tags.maxspeed;
35699 if (!maxspeed) return;
35700 var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
35701 if (!maxspeedRegex.test(maxspeed)) return;
35702 return parseInt(maxspeed, 10);
35705 function parseLaneDirections(tags, isOneWay, laneCount) {
35706 var forward = parseInt(tags['lanes:forward'], 10);
35707 var backward = parseInt(tags['lanes:backward'], 10);
35708 var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
35710 if (parseInt(tags.oneway, 10) === -1) {
35713 backward = laneCount;
35714 } else if (isOneWay) {
35715 forward = laneCount;
35718 } else if (isNaN(forward) && isNaN(backward)) {
35719 backward = Math.floor((laneCount - bothways) / 2);
35720 forward = laneCount - bothways - backward;
35721 } else if (isNaN(forward)) {
35722 if (backward > laneCount - bothways) {
35723 backward = laneCount - bothways;
35726 forward = laneCount - bothways - backward;
35727 } else if (isNaN(backward)) {
35728 if (forward > laneCount - bothways) {
35729 forward = laneCount - bothways;
35732 backward = laneCount - bothways - forward;
35737 backward: backward,
35742 function parseTurnLanes(tag) {
35744 var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
35745 return tag.split('|').map(function (s) {
35746 if (s === '') s = 'none';
35747 return s.split(';').map(function (d) {
35748 return validValues.indexOf(d) === -1 ? 'unknown' : d;
35753 function parseMaxspeedLanes(tag, maxspeed) {
35755 return tag.split('|').map(function (s) {
35756 if (s === 'none') return s;
35757 var m = parseInt(s, 10);
35758 if (s === '' || m === maxspeed) return null;
35759 return isNaN(m) ? 'unknown' : m;
35763 function parseMiscLanes(tag) {
35765 var validValues = ['yes', 'no', 'designated'];
35766 return tag.split('|').map(function (s) {
35767 if (s === '') s = 'no';
35768 return validValues.indexOf(s) === -1 ? 'unknown' : s;
35772 function parseBicycleWay(tag) {
35774 var validValues = ['yes', 'no', 'designated', 'lane'];
35775 return tag.split('|').map(function (s) {
35776 if (s === '') s = 'no';
35777 return validValues.indexOf(s) === -1 ? 'unknown' : s;
35781 function mapToLanesObj(lanesObj, data, key) {
35782 if (data.forward) {
35783 data.forward.forEach(function (l, i) {
35784 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
35785 lanesObj.forward[i][key] = l;
35789 if (data.backward) {
35790 data.backward.forEach(function (l, i) {
35791 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
35792 lanesObj.backward[i][key] = l;
35796 if (data.unspecified) {
35797 data.unspecified.forEach(function (l, i) {
35798 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
35799 lanesObj.unspecified[i][key] = l;
35804 function osmWay() {
35805 if (!(this instanceof osmWay)) {
35806 return new osmWay().initialize(arguments);
35807 } else if (arguments.length) {
35808 this.initialize(arguments);
35811 osmEntity.way = osmWay;
35812 osmWay.prototype = Object.create(osmEntity.prototype);
35813 Object.assign(osmWay.prototype, {
35816 copy: function copy(resolver, copies) {
35817 if (copies[this.id]) return copies[this.id];
35818 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
35819 var nodes = this.nodes.map(function (id) {
35820 return resolver.entity(id).copy(resolver, copies).id;
35822 copy = copy.update({
35825 copies[this.id] = copy;
35828 extent: function extent(resolver) {
35829 return resolver["transient"](this, 'extent', function () {
35830 var extent = geoExtent();
35832 for (var i = 0; i < this.nodes.length; i++) {
35833 var node = resolver.hasEntity(this.nodes[i]);
35836 extent._extend(node.extent());
35843 first: function first() {
35844 return this.nodes[0];
35846 last: function last() {
35847 return this.nodes[this.nodes.length - 1];
35849 contains: function contains(node) {
35850 return this.nodes.indexOf(node) >= 0;
35852 affix: function affix(node) {
35853 if (this.nodes[0] === node) return 'prefix';
35854 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
35856 layer: function layer() {
35857 // explicit layer tag, clamp between -10, 10..
35858 if (isFinite(this.tags.layer)) {
35859 return Math.max(-10, Math.min(+this.tags.layer, 10));
35860 } // implied layer tag..
35863 if (this.tags.covered === 'yes') return -1;
35864 if (this.tags.location === 'overground') return 1;
35865 if (this.tags.location === 'underground') return -1;
35866 if (this.tags.location === 'underwater') return -10;
35867 if (this.tags.power === 'line') return 10;
35868 if (this.tags.power === 'minor_line') return 10;
35869 if (this.tags.aerialway) return 10;
35870 if (this.tags.bridge) return 1;
35871 if (this.tags.cutting) return -1;
35872 if (this.tags.tunnel) return -1;
35873 if (this.tags.waterway) return -1;
35874 if (this.tags.man_made === 'pipeline') return -10;
35875 if (this.tags.boundary) return -10;
35878 // the approximate width of the line based on its tags except its `width` tag
35879 impliedLineWidthMeters: function impliedLineWidthMeters() {
35880 var averageWidths = {
35882 // width is for single lane
35909 // width includes ties and rail bed, not just track gauge
35932 for (var key in averageWidths) {
35933 if (this.tags[key] && averageWidths[key][this.tags[key]]) {
35934 var width = averageWidths[key][this.tags[key]];
35936 if (key === 'highway') {
35937 var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
35938 if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
35939 return width * laneCount;
35948 isOneWay: function isOneWay() {
35949 // explicit oneway tag..
35954 'reversible': true,
35955 'alternating': true,
35960 if (values[this.tags.oneway] !== undefined) {
35961 return values[this.tags.oneway];
35962 } // implied oneway tag..
35965 for (var key in this.tags) {
35966 if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) {
35973 // Some identifier for tag that implies that this way is "sided",
35974 // i.e. the right side is the 'inside' (e.g. the right side of a
35975 // natural=cliff is lower).
35976 sidednessIdentifier: function sidednessIdentifier() {
35977 for (var key in this.tags) {
35978 var value = this.tags[key];
35980 if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
35981 if (osmRightSideIsInsideTags[key][value] === true) {
35984 // if the map's value is something other than a
35985 // literal true, we should use it so we can
35986 // special case some keys (e.g. natural=coastline
35987 // is handled differently to other naturals).
35988 return osmRightSideIsInsideTags[key][value];
35995 isSided: function isSided() {
35996 if (this.tags.two_sided === 'yes') {
36000 return this.sidednessIdentifier() !== null;
36002 lanes: function lanes() {
36003 return osmLanes(this);
36005 isClosed: function isClosed() {
36006 return this.nodes.length > 1 && this.first() === this.last();
36008 isConvex: function isConvex(resolver) {
36009 if (!this.isClosed() || this.isDegenerate()) return null;
36010 var nodes = utilArrayUniq(resolver.childNodes(this));
36011 var coords = nodes.map(function (n) {
36017 for (var i = 0; i < coords.length; i++) {
36018 var o = coords[(i + 1) % coords.length];
36020 var b = coords[(i + 2) % coords.length];
36021 var res = geoVecCross(a, b, o);
36022 curr = res > 0 ? 1 : res < 0 ? -1 : 0;
36026 } else if (prev && curr !== prev) {
36035 // returns an object with the tag that implies this is an area, if any
36036 tagSuggestingArea: function tagSuggestingArea() {
36037 return osmTagSuggestingArea(this.tags);
36039 isArea: function isArea() {
36040 if (this.tags.area === 'yes') return true;
36041 if (!this.isClosed() || this.tags.area === 'no') return false;
36042 return this.tagSuggestingArea() !== null;
36044 isDegenerate: function isDegenerate() {
36045 return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
36047 areAdjacent: function areAdjacent(n1, n2) {
36048 for (var i = 0; i < this.nodes.length; i++) {
36049 if (this.nodes[i] === n1) {
36050 if (this.nodes[i - 1] === n2) return true;
36051 if (this.nodes[i + 1] === n2) return true;
36057 geometry: function geometry(graph) {
36058 return graph["transient"](this, 'geometry', function () {
36059 return this.isArea() ? 'area' : 'line';
36062 // returns an array of objects representing the segments between the nodes in this way
36063 segments: function segments(graph) {
36064 function segmentExtent(graph) {
36065 var n1 = graph.hasEntity(this.nodes[0]);
36066 var n2 = graph.hasEntity(this.nodes[1]);
36067 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])]]);
36070 return graph["transient"](this, 'segments', function () {
36073 for (var i = 0; i < this.nodes.length - 1; i++) {
36075 id: this.id + '-' + i,
36078 nodes: [this.nodes[i], this.nodes[i + 1]],
36079 extent: segmentExtent
36086 // If this way is not closed, append the beginning node to the end of the nodelist to close it.
36087 close: function close() {
36088 if (this.isClosed() || !this.nodes.length) return this;
36089 var nodes = this.nodes.slice();
36090 nodes = nodes.filter(noRepeatNodes);
36091 nodes.push(nodes[0]);
36092 return this.update({
36096 // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
36097 unclose: function unclose() {
36098 if (!this.isClosed()) return this;
36099 var nodes = this.nodes.slice();
36100 var connector = this.first();
36101 var i = nodes.length - 1; // remove trailing connectors..
36103 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36104 nodes.splice(i, 1);
36105 i = nodes.length - 1;
36108 nodes = nodes.filter(noRepeatNodes);
36109 return this.update({
36113 // Adds a node (id) in front of the node which is currently at position index.
36114 // If index is undefined, the node will be added to the end of the way for linear ways,
36115 // or just before the final connecting node for circular ways.
36116 // Consecutive duplicates are eliminated including existing ones.
36117 // Circularity is always preserved when adding a node.
36118 addNode: function addNode(id, index) {
36119 var nodes = this.nodes.slice();
36120 var isClosed = this.isClosed();
36121 var max = isClosed ? nodes.length - 1 : nodes.length;
36123 if (index === undefined) {
36127 if (index < 0 || index > max) {
36128 throw new RangeError('index ' + index + ' out of range 0..' + max);
36129 } // If this is a closed way, remove all connector nodes except the first one
36130 // (there may be duplicates) and adjust index if necessary..
36134 var connector = this.first(); // leading connectors..
36138 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36139 nodes.splice(i, 1);
36140 if (index > i) index--;
36141 } // trailing connectors..
36144 i = nodes.length - 1;
36146 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36147 nodes.splice(i, 1);
36148 if (index > i) index--;
36149 i = nodes.length - 1;
36153 nodes.splice(index, 0, id);
36154 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36156 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36157 nodes.push(nodes[0]);
36160 return this.update({
36164 // Replaces the node which is currently at position index with the given node (id).
36165 // Consecutive duplicates are eliminated including existing ones.
36166 // Circularity is preserved when updating a node.
36167 updateNode: function updateNode(id, index) {
36168 var nodes = this.nodes.slice();
36169 var isClosed = this.isClosed();
36170 var max = nodes.length - 1;
36172 if (index === undefined || index < 0 || index > max) {
36173 throw new RangeError('index ' + index + ' out of range 0..' + max);
36174 } // If this is a closed way, remove all connector nodes except the first one
36175 // (there may be duplicates) and adjust index if necessary..
36179 var connector = this.first(); // leading connectors..
36183 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
36184 nodes.splice(i, 1);
36185 if (index > i) index--;
36186 } // trailing connectors..
36189 i = nodes.length - 1;
36191 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
36192 nodes.splice(i, 1);
36193 if (index === i) index = 0; // update leading connector instead
36195 i = nodes.length - 1;
36199 nodes.splice(index, 1, id);
36200 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36202 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36203 nodes.push(nodes[0]);
36206 return this.update({
36210 // Replaces each occurrence of node id needle with replacement.
36211 // Consecutive duplicates are eliminated including existing ones.
36212 // Circularity is preserved.
36213 replaceNode: function replaceNode(needleID, replacementID) {
36214 var nodes = this.nodes.slice();
36215 var isClosed = this.isClosed();
36217 for (var i = 0; i < nodes.length; i++) {
36218 if (nodes[i] === needleID) {
36219 nodes[i] = replacementID;
36223 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36225 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36226 nodes.push(nodes[0]);
36229 return this.update({
36233 // Removes each occurrence of node id.
36234 // Consecutive duplicates are eliminated including existing ones.
36235 // Circularity is preserved.
36236 removeNode: function removeNode(id) {
36237 var nodes = this.nodes.slice();
36238 var isClosed = this.isClosed();
36239 nodes = nodes.filter(function (node) {
36240 return node !== id;
36241 }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
36243 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
36244 nodes.push(nodes[0]);
36247 return this.update({
36251 asJXON: function asJXON(changeset_id) {
36254 '@id': this.osmId(),
36255 '@version': this.version || 0,
36256 nd: this.nodes.map(function (id) {
36259 ref: osmEntity.id.toOSM(id)
36263 tag: Object.keys(this.tags).map(function (k) {
36274 if (changeset_id) {
36275 r.way['@changeset'] = changeset_id;
36280 asGeoJSON: function asGeoJSON(resolver) {
36281 return resolver["transient"](this, 'GeoJSON', function () {
36282 var coordinates = resolver.childNodes(this).map(function (n) {
36286 if (this.isArea() && this.isClosed()) {
36289 coordinates: [coordinates]
36293 type: 'LineString',
36294 coordinates: coordinates
36299 area: function area(resolver) {
36300 return resolver["transient"](this, 'area', function () {
36301 var nodes = resolver.childNodes(this);
36304 coordinates: [nodes.map(function (n) {
36309 if (!this.isClosed() && nodes.length) {
36310 json.coordinates[0].push(nodes[0].loc);
36313 var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
36314 // that OpenStreetMap polygons are not hemisphere-spanning.
36316 if (area > 2 * Math.PI) {
36317 json.coordinates[0] = json.coordinates[0].reverse();
36318 area = d3_geoArea(json);
36321 return isNaN(area) ? 0 : area;
36324 }); // Filter function to eliminate consecutive duplicates.
36326 function noRepeatNodes(node, i, arr) {
36327 return i === 0 || node !== arr[i - 1];
36331 // 1. Relation tagged with `type=multipolygon` and no interesting tags.
36332 // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
36333 // 3. No members without a role.
36335 // Old multipolygons are no longer recommended but are still rendered as areas by iD.
36337 function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
36338 if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
36344 for (var memberIndex in entity.members) {
36345 var member = entity.members[memberIndex];
36347 if (!member.role || member.role === 'outer') {
36348 if (outerMember) return false;
36349 if (member.type !== 'way') return false;
36350 if (!graph.hasEntity(member.id)) return false;
36351 outerMember = graph.entity(member.id);
36353 if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
36359 return outerMember;
36360 } // For fixing up rendering of multipolygons with tags on the outer member.
36361 // https://github.com/openstreetmap/iD/issues/613
36363 function osmIsOldMultipolygonOuterMember(entity, graph) {
36364 if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) {
36368 var parents = graph.parentRelations(entity);
36369 if (parents.length !== 1) return false;
36370 var parent = parents[0];
36372 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36376 var members = parent.members,
36379 for (var i = 0; i < members.length; i++) {
36380 member = members[i];
36382 if (member.id === entity.id && member.role && member.role !== 'outer') {
36383 // Not outer member
36387 if (member.id !== entity.id && (!member.role || member.role === 'outer')) {
36388 // Not a simple multipolygon
36395 function osmOldMultipolygonOuterMember(entity, graph) {
36396 if (entity.type !== 'way') return false;
36397 var parents = graph.parentRelations(entity);
36398 if (parents.length !== 1) return false;
36399 var parent = parents[0];
36401 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) {
36405 var members = parent.members,
36409 for (var i = 0; i < members.length; i++) {
36410 member = members[i];
36412 if (!member.role || member.role === 'outer') {
36413 if (outerMember) return false; // Not a simple multipolygon
36415 outerMember = member;
36419 if (!outerMember) return false;
36420 var outerEntity = graph.hasEntity(outerMember.id);
36422 if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) {
36426 return outerEntity;
36427 } // Join `toJoin` array into sequences of connecting ways.
36428 // Segments which share identical start/end nodes will, as much as possible,
36429 // be connected with each other.
36431 // The return value is a nested array. Each constituent array contains elements
36432 // of `toJoin` which have been determined to connect.
36434 // Each consitituent array also has a `nodes` property whose value is an
36435 // ordered array of member nodes, with appropriate order reversal and
36436 // start/end coordinate de-duplication.
36438 // Members of `toJoin` must have, at minimum, `type` and `id` properties.
36439 // Thus either an array of `osmWay`s or a relation member array may be used.
36441 // If an member is an `osmWay`, its tags and childnodes may be reversed via
36442 // `actionReverse` in the output.
36444 // The returned sequences array also has an `actions` array property, containing
36445 // any reversal actions that should be applied to the graph, should the calling
36446 // code attempt to actually join the given ways.
36448 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
36449 // false) and non-way members are ignored.
36452 function osmJoinWays(toJoin, graph) {
36453 function resolve(member) {
36454 return graph.childNodes(graph.entity(member.id));
36457 function reverse(item) {
36458 var action = actionReverse(item.id, {
36459 reverseOneway: true
36461 sequences.actions.push(action);
36462 return item instanceof osmWay ? action(graph).entity(item.id) : item;
36463 } // make a copy containing only the items to join
36466 toJoin = toJoin.filter(function (member) {
36467 return member.type === 'way' && graph.hasEntity(member.id);
36468 }); // Are the things we are joining relation members or `osmWays`?
36469 // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
36472 var joinAsMembers = true;
36474 for (i = 0; i < toJoin.length; i++) {
36475 if (toJoin[i] instanceof osmWay) {
36476 joinAsMembers = false;
36481 var sequences = [];
36482 sequences.actions = [];
36484 while (toJoin.length) {
36485 // start a new sequence
36486 var item = toJoin.shift();
36487 var currWays = [item];
36488 var currNodes = resolve(item).slice(); // add to it
36490 while (toJoin.length) {
36491 var start = currNodes[0];
36492 var end = currNodes[currNodes.length - 1];
36494 var nodes = null; // Find the next way/member to join.
36496 for (i = 0; i < toJoin.length; i++) {
36498 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
36499 // Strongly prefer to generate a forward path that preserves the order
36500 // of the members array. For multipolygons and most relations, member
36501 // order does not matter - but for routes, it does. (see #4589)
36502 // If we started this sequence backwards (i.e. next member way attaches to
36503 // the start node and not the end node), reverse the initial way before continuing.
36505 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
36506 currWays[0] = reverse(currWays[0]);
36507 currNodes.reverse();
36508 start = currNodes[0];
36509 end = currNodes[currNodes.length - 1];
36512 if (nodes[0] === end) {
36513 fn = currNodes.push; // join to end
36515 nodes = nodes.slice(1);
36517 } else if (nodes[nodes.length - 1] === end) {
36518 fn = currNodes.push; // join to end
36520 nodes = nodes.slice(0, -1).reverse();
36521 item = reverse(item);
36523 } else if (nodes[nodes.length - 1] === start) {
36524 fn = currNodes.unshift; // join to beginning
36526 nodes = nodes.slice(0, -1);
36528 } else if (nodes[0] === start) {
36529 fn = currNodes.unshift; // join to beginning
36531 nodes = nodes.slice(1).reverse();
36532 item = reverse(item);
36540 // couldn't find a joinable way/member
36544 fn.apply(currWays, [item]);
36545 fn.apply(currNodes, nodes);
36546 toJoin.splice(i, 1);
36549 currWays.nodes = currNodes;
36550 sequences.push(currWays);
36556 function actionAddMember(relationId, member, memberIndex, insertPair) {
36557 return function action(graph) {
36558 var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
36560 var isPTv2 = /stop|platform/.test(member.role);
36562 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
36563 // Try to perform sensible inserts based on how the ways join together
36564 graph = addWayMember(relation, graph);
36566 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36567 // Stops and Platforms for PTv2 should be ordered first.
36568 // hack: We do not currently have the ability to place them in the exactly correct order.
36569 if (isPTv2 && isNaN(memberIndex)) {
36573 graph = graph.replace(relation.addMember(member, memberIndex));
36577 }; // Add a way member into the relation "wherever it makes sense".
36578 // In this situation we were not supplied a memberIndex.
36580 function addWayMember(relation, graph) {
36581 var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
36583 var PTv2members = [];
36586 for (i = 0; i < relation.members.length; i++) {
36587 var m = relation.members[i];
36589 if (/stop|platform/.test(m.role)) {
36590 PTv2members.push(m);
36596 relation = relation.update({
36601 // We're adding a member that must stay paired with an existing member.
36602 // (This feature is used by `actionSplit`)
36604 // This is tricky because the members may exist multiple times in the
36605 // member list, and with different A-B/B-A ordering and different roles.
36606 // (e.g. a bus route that loops out and back - #4589).
36608 // Replace the existing member with a temporary way,
36609 // so that `osmJoinWays` can treat the pair like a single way.
36612 nodes: insertPair.nodes
36614 graph = graph.replace(tempWay);
36620 var tempRelation = relation.replaceMember({
36621 id: insertPair.originalID
36622 }, tempMember, true);
36623 groups = utilArrayGroupBy(tempRelation.members, 'type');
36624 groups.way = groups.way || [];
36626 // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
36627 groups = utilArrayGroupBy(relation.members, 'type');
36628 groups.way = groups.way || [];
36629 groups.way.push(member);
36632 members = withIndex(groups.way);
36633 var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
36634 // But will contain only the completed (downloaded) members
36636 for (i = 0; i < joined.length; i++) {
36637 var segment = joined[i];
36638 var nodes = segment.nodes.slice();
36639 var startIndex = segment[0].index; // j = array index in `members` where this segment starts
36641 for (j = 0; j < members.length; j++) {
36642 if (members[j].index === startIndex) {
36645 } // k = each member in segment
36648 for (k = 0; k < segment.length; k++) {
36650 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
36652 if (tempWay && item.id === tempWay.id) {
36653 if (nodes[0].id === insertPair.nodes[0]) {
36655 id: insertPair.originalID,
36659 id: insertPair.insertedID,
36665 id: insertPair.insertedID,
36669 id: insertPair.originalID,
36674 } // reorder `members` if necessary
36678 if (j + k >= members.length || item.index !== members[j + k].index) {
36679 moveMember(members, item.index, j + k);
36683 nodes.splice(0, way.nodes.length - 1);
36688 graph = graph.remove(tempWay);
36689 } // Final pass: skip dead items, split pairs, remove index properties
36692 var wayMembers = [];
36694 for (i = 0; i < members.length; i++) {
36696 if (item.index === -1) continue;
36699 wayMembers.push(item.pair[0]);
36700 wayMembers.push(item.pair[1]);
36702 wayMembers.push(utilObjectOmit(item, ['index']));
36704 } // Put stops and platforms first, then nodes, ways, relations
36705 // This is recommended for Public Transport v2 routes:
36706 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
36709 var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
36710 return graph.replace(relation.update({
36711 members: newMembers
36712 })); // `moveMember()` changes the `members` array in place by splicing
36713 // the item with `.index = findIndex` to where it belongs,
36714 // and marking the old position as "dead" with `.index = -1`
36718 // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
36722 // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
36723 // members 0 1 2 3 x 5 4 6 7 8 9 moved
36727 // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
36728 // members 0 1 2 3 x 5 4 7 6 x 8 9 moved
36732 // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
36735 function moveMember(arr, findIndex, toIndex) {
36738 for (i = 0; i < arr.length; i++) {
36739 if (arr[i].index === findIndex) {
36744 var item = Object.assign({}, arr[i]); // shallow copy
36746 arr[i].index = -1; // mark as dead
36748 item.index = toIndex;
36749 arr.splice(toIndex, 0, item);
36750 } // This is the same as `Relation.indexedMembers`,
36751 // Except we don't want to index all the members, only the ways
36754 function withIndex(arr) {
36755 var result = new Array(arr.length);
36757 for (var i = 0; i < arr.length; i++) {
36758 result[i] = Object.assign({}, arr[i]); // shallow copy
36760 result[i].index = i;
36768 function actionAddMidpoint(midpoint, node) {
36769 return function (graph) {
36770 graph = graph.replace(node.move(midpoint.loc));
36771 var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
36772 parents.forEach(function (way) {
36773 for (var i = 0; i < way.nodes.length - 1; i++) {
36774 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
36775 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
36776 // turning them into self-intersections.
36786 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
36787 function actionAddVertex(wayId, nodeId, index) {
36788 return function (graph) {
36789 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
36793 function actionChangeMember(relationId, member, memberIndex) {
36794 return function (graph) {
36795 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
36799 function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
36800 return function action(graph) {
36801 var entity = graph.entity(entityID);
36802 var geometry = entity.geometry(graph);
36803 var tags = entity.tags; // preserve tags that the new preset might care about, if any
36805 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry, newPreset && newPreset.addTags ? Object.keys(newPreset.addTags) : null);
36806 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
36807 return graph.replace(entity.update({
36813 function actionChangeTags(entityId, tags) {
36814 return function (graph) {
36815 var entity = graph.entity(entityId);
36816 return graph.replace(entity.update({
36822 function osmNode() {
36823 if (!(this instanceof osmNode)) {
36824 return new osmNode().initialize(arguments);
36825 } else if (arguments.length) {
36826 this.initialize(arguments);
36829 osmEntity.node = osmNode;
36830 osmNode.prototype = Object.create(osmEntity.prototype);
36831 Object.assign(osmNode.prototype, {
36834 extent: function extent() {
36835 return new geoExtent(this.loc);
36837 geometry: function geometry(graph) {
36838 return graph["transient"](this, 'geometry', function () {
36839 return graph.isPoi(this) ? 'point' : 'vertex';
36842 move: function move(loc) {
36843 return this.update({
36847 isDegenerate: function isDegenerate() {
36848 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);
36850 // Inspect tags and geometry to determine which direction(s) this node/vertex points
36851 directions: function directions(resolver, projection) {
36853 var i; // which tag to use?
36855 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
36856 // all-way stop tag on a highway intersection
36859 // generic direction tag
36860 val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
36862 var re = /:direction$/i;
36863 var keys = Object.keys(this.tags);
36865 for (i = 0; i < keys.length; i++) {
36866 if (re.test(keys[i])) {
36867 val = this.tags[keys[i]].toLowerCase();
36873 if (val === '') return [];
36877 northnortheast: 22,
36885 eastsoutheast: 112,
36889 southsoutheast: 157,
36893 southsouthwest: 202,
36897 westsouthwest: 247,
36901 westnorthwest: 292,
36905 northnorthwest: 337,
36908 var values = val.split(';');
36910 values.forEach(function (v) {
36911 // swap cardinal for numeric directions
36912 if (cardinal[v] !== undefined) {
36914 } // numeric direction - just add to results
36917 if (v !== '' && !isNaN(+v)) {
36920 } // string direction - inspect parent ways
36923 var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
36924 var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
36925 if (!lookForward && !lookBackward) return;
36927 resolver.parentWays(this).forEach(function (parent) {
36928 var nodes = parent.nodes;
36930 for (i = 0; i < nodes.length; i++) {
36931 if (nodes[i] === this.id) {
36932 // match current entity
36933 if (lookForward && i > 0) {
36934 nodeIds[nodes[i - 1]] = true; // look back to prev node
36937 if (lookBackward && i < nodes.length - 1) {
36938 nodeIds[nodes[i + 1]] = true; // look ahead to next node
36943 Object.keys(nodeIds).forEach(function (nodeId) {
36944 // +90 because geoAngle returns angle from X axis, not Y (north)
36945 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
36948 return utilArrayUniq(results);
36950 isCrossing: function isCrossing() {
36951 return this.tags.highway === 'crossing' || this.tags.railway && this.tags.railway.indexOf('crossing') !== -1;
36953 isEndpoint: function isEndpoint(resolver) {
36954 return resolver["transient"](this, 'isEndpoint', function () {
36956 return resolver.parentWays(this).filter(function (parent) {
36957 return !parent.isClosed() && !!parent.affix(id);
36961 isConnected: function isConnected(resolver) {
36962 return resolver["transient"](this, 'isConnected', function () {
36963 var parents = resolver.parentWays(this);
36965 if (parents.length > 1) {
36966 // vertex is connected to multiple parent ways
36967 for (var i in parents) {
36968 if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
36970 } else if (parents.length === 1) {
36971 var way = parents[0];
36972 var nodes = way.nodes.slice();
36974 if (way.isClosed()) {
36976 } // ignore connecting node if closed
36977 // return true if vertex appears multiple times (way is self intersecting)
36980 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
36986 parentIntersectionWays: function parentIntersectionWays(resolver) {
36987 return resolver["transient"](this, 'parentIntersectionWays', function () {
36988 return resolver.parentWays(this).filter(function (parent) {
36989 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
36993 isIntersection: function isIntersection(resolver) {
36994 return this.parentIntersectionWays(resolver).length > 1;
36996 isHighwayIntersection: function isHighwayIntersection(resolver) {
36997 return resolver["transient"](this, 'isHighwayIntersection', function () {
36998 return resolver.parentWays(this).filter(function (parent) {
36999 return parent.tags.highway && parent.geometry(resolver) === 'line';
37003 isOnAddressLine: function isOnAddressLine(resolver) {
37004 return resolver["transient"](this, 'isOnAddressLine', function () {
37005 return resolver.parentWays(this).filter(function (parent) {
37006 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
37010 asJXON: function asJXON(changeset_id) {
37013 '@id': this.osmId(),
37014 '@lon': this.loc[0],
37015 '@lat': this.loc[1],
37016 '@version': this.version || 0,
37017 tag: Object.keys(this.tags).map(function (k) {
37027 if (changeset_id) r.node['@changeset'] = changeset_id;
37030 asGeoJSON: function asGeoJSON() {
37033 coordinates: this.loc
37038 function actionCircularize(wayId, projection, maxAngle) {
37039 maxAngle = (maxAngle || 20) * Math.PI / 180;
37041 var action = function action(graph, t) {
37042 if (t === null || !isFinite(t)) t = 1;
37043 t = Math.min(Math.max(+t, 0), 1);
37044 var way = graph.entity(wayId);
37045 var origNodes = {};
37046 graph.childNodes(way).forEach(function (node) {
37047 if (!origNodes[node.id]) origNodes[node.id] = node;
37050 if (!way.isConvex(graph)) {
37051 graph = action.makeConvex(graph);
37054 var nodes = utilArrayUniq(graph.childNodes(way));
37055 var keyNodes = nodes.filter(function (n) {
37056 return graph.parentWays(n).length !== 1;
37058 var points = nodes.map(function (n) {
37059 return projection(n.loc);
37061 var keyPoints = keyNodes.map(function (n) {
37062 return projection(n.loc);
37064 var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
37065 var radius = d3_median(points, function (p) {
37066 return geoVecLength(centroid, p);
37068 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37069 var ids, i, j, k; // we need at least two key nodes for the algorithm to work
37071 if (!keyNodes.length) {
37072 keyNodes = [nodes[0]];
37073 keyPoints = [points[0]];
37076 if (keyNodes.length === 1) {
37077 var index = nodes.indexOf(keyNodes[0]);
37078 var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
37079 keyNodes.push(nodes[oppositeIndex]);
37080 keyPoints.push(points[oppositeIndex]);
37081 } // key points and nodes are those connected to the ways,
37082 // they are projected onto the circle, in between nodes are moved
37083 // to constant intervals between key nodes, extra in between nodes are
37084 // added if necessary.
37087 for (i = 0; i < keyPoints.length; i++) {
37088 var nextKeyNodeIndex = (i + 1) % keyNodes.length;
37089 var startNode = keyNodes[i];
37090 var endNode = keyNodes[nextKeyNodeIndex];
37091 var startNodeIndex = nodes.indexOf(startNode);
37092 var endNodeIndex = nodes.indexOf(endNode);
37093 var numberNewPoints = -1;
37094 var indexRange = endNodeIndex - startNodeIndex;
37095 var nearNodes = {};
37096 var inBetweenNodes = [];
37097 var startAngle, endAngle, totalAngle, eachAngle;
37098 var angle, loc, node, origNode;
37100 if (indexRange < 0) {
37101 indexRange += nodes.length;
37102 } // position this key node
37105 var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
37106 keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
37107 loc = projection.invert(keyPoints[i]);
37108 node = keyNodes[i];
37109 origNode = origNodes[node.id];
37110 node = node.move(geoVecInterp(origNode.loc, loc, t));
37111 graph = graph.replace(node); // figure out the between delta angle we want to match to
37113 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
37114 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
37115 totalAngle = endAngle - startAngle; // detects looping around -pi/pi
37117 if (totalAngle * sign > 0) {
37118 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
37123 eachAngle = totalAngle / (indexRange + numberNewPoints);
37124 } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
37127 for (j = 1; j < indexRange; j++) {
37128 angle = startAngle + j * eachAngle;
37129 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
37130 node = nodes[(j + startNodeIndex) % nodes.length];
37131 origNode = origNodes[node.id];
37132 nearNodes[node.id] = angle;
37133 node = node.move(geoVecInterp(origNode.loc, loc, t));
37134 graph = graph.replace(node);
37135 } // add new in between nodes if necessary
37138 for (j = 0; j < numberNewPoints; j++) {
37139 angle = startAngle + (indexRange + j) * eachAngle;
37140 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
37142 var min = Infinity;
37144 for (var nodeId in nearNodes) {
37145 var nearAngle = nearNodes[nodeId];
37146 var dist = Math.abs(nearAngle - angle);
37150 origNode = origNodes[nodeId];
37155 loc: geoVecInterp(origNode.loc, loc, t)
37157 graph = graph.replace(node);
37158 nodes.splice(endNodeIndex + j, 0, node);
37159 inBetweenNodes.push(node.id);
37160 } // Check for other ways that share these keyNodes..
37161 // If keyNodes are adjacent in both ways,
37162 // we can add inBetweenNodes to that shared way too..
37165 if (indexRange === 1 && inBetweenNodes.length) {
37166 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
37167 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
37168 var wayDirection1 = endIndex1 - startIndex1;
37170 if (wayDirection1 < -1) {
37174 var parentWays = graph.parentWays(keyNodes[i]);
37176 for (j = 0; j < parentWays.length; j++) {
37177 var sharedWay = parentWays[j];
37178 if (sharedWay === way) continue;
37180 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
37181 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
37182 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
37183 var wayDirection2 = endIndex2 - startIndex2;
37184 var insertAt = endIndex2;
37186 if (wayDirection2 < -1) {
37190 if (wayDirection1 !== wayDirection2) {
37191 inBetweenNodes.reverse();
37192 insertAt = startIndex2;
37195 for (k = 0; k < inBetweenNodes.length; k++) {
37196 sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
37199 graph = graph.replace(sharedWay);
37203 } // update the way to have all the new nodes
37206 ids = nodes.map(function (n) {
37213 graph = graph.replace(way);
37217 action.makeConvex = function (graph) {
37218 var way = graph.entity(wayId);
37219 var nodes = utilArrayUniq(graph.childNodes(way));
37220 var points = nodes.map(function (n) {
37221 return projection(n.loc);
37223 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
37224 var hull = d3_polygonHull(points);
37225 var i, j; // D3 convex hulls go counterclockwise..
37232 for (i = 0; i < hull.length - 1; i++) {
37233 var startIndex = points.indexOf(hull[i]);
37234 var endIndex = points.indexOf(hull[i + 1]);
37235 var indexRange = endIndex - startIndex;
37237 if (indexRange < 0) {
37238 indexRange += nodes.length;
37239 } // move interior nodes to the surface of the convex hull..
37242 for (j = 1; j < indexRange; j++) {
37243 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
37244 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
37245 graph = graph.replace(node);
37252 action.disabled = function (graph) {
37253 if (!graph.entity(wayId).isClosed()) {
37254 return 'not_closed';
37255 } //disable when already circular
37258 var way = graph.entity(wayId);
37259 var nodes = utilArrayUniq(graph.childNodes(way));
37260 var points = nodes.map(function (n) {
37261 return projection(n.loc);
37263 var hull = d3_polygonHull(points);
37264 var epsilonAngle = Math.PI / 180;
37266 if (hull.length !== points.length || hull.length < 3) {
37270 var centroid = d3_polygonCentroid(points);
37271 var radius = geoVecLengthSquare(centroid, points[0]);
37272 var i, actualPoint; // compare distances between centroid and points
37274 for (i = 0; i < hull.length; i++) {
37275 actualPoint = hull[i];
37276 var actualDist = geoVecLengthSquare(actualPoint, centroid);
37277 var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
37279 if (diff > 0.05 * radius) {
37282 } //check if central angles are smaller than maxAngle
37285 for (i = 0; i < hull.length; i++) {
37286 actualPoint = hull[i];
37287 var nextPoint = hull[(i + 1) % hull.length];
37288 var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
37289 var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
37290 var angle = endAngle - startAngle;
37296 if (angle > Math.PI) {
37297 angle = 2 * Math.PI - angle;
37300 if (angle > maxAngle + epsilonAngle) {
37305 return 'already_circular';
37308 action.transitionable = true;
37312 function actionDeleteWay(wayID) {
37313 function canDeleteNode(node, graph) {
37314 // don't delete nodes still attached to ways or relations
37315 if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
37316 var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
37318 if (geometries.point) return false; // delete if this node only be a vertex
37320 if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
37321 // so only delete if there are no interesting tags
37323 return !node.hasInterestingTags();
37326 var action = function action(graph) {
37327 var way = graph.entity(wayID);
37328 graph.parentRelations(way).forEach(function (parent) {
37329 parent = parent.removeMembersWithID(wayID);
37330 graph = graph.replace(parent);
37332 if (parent.isDegenerate()) {
37333 graph = actionDeleteRelation(parent.id)(graph);
37336 new Set(way.nodes).forEach(function (nodeID) {
37337 graph = graph.replace(way.removeNode(nodeID));
37338 var node = graph.entity(nodeID);
37340 if (canDeleteNode(node, graph)) {
37341 graph = graph.remove(node);
37344 return graph.remove(way);
37350 function actionDeleteMultiple(ids) {
37352 way: actionDeleteWay,
37353 node: actionDeleteNode,
37354 relation: actionDeleteRelation
37357 var action = function action(graph) {
37358 ids.forEach(function (id) {
37359 if (graph.hasEntity(id)) {
37360 // It may have been deleted already.
37361 graph = actions[graph.entity(id).type](id)(graph);
37370 function actionDeleteRelation(relationID, allowUntaggedMembers) {
37371 function canDeleteEntity(entity, graph) {
37372 return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
37375 var action = function action(graph) {
37376 var relation = graph.entity(relationID);
37377 graph.parentRelations(relation).forEach(function (parent) {
37378 parent = parent.removeMembersWithID(relationID);
37379 graph = graph.replace(parent);
37381 if (parent.isDegenerate()) {
37382 graph = actionDeleteRelation(parent.id)(graph);
37385 var memberIDs = utilArrayUniq(relation.members.map(function (m) {
37388 memberIDs.forEach(function (memberID) {
37389 graph = graph.replace(relation.removeMembersWithID(memberID));
37390 var entity = graph.entity(memberID);
37392 if (canDeleteEntity(entity, graph)) {
37393 graph = actionDeleteMultiple([memberID])(graph);
37396 return graph.remove(relation);
37402 function actionDeleteNode(nodeId) {
37403 var action = function action(graph) {
37404 var node = graph.entity(nodeId);
37405 graph.parentWays(node).forEach(function (parent) {
37406 parent = parent.removeNode(nodeId);
37407 graph = graph.replace(parent);
37409 if (parent.isDegenerate()) {
37410 graph = actionDeleteWay(parent.id)(graph);
37413 graph.parentRelations(node).forEach(function (parent) {
37414 parent = parent.removeMembersWithID(nodeId);
37415 graph = graph.replace(parent);
37417 if (parent.isDegenerate()) {
37418 graph = actionDeleteRelation(parent.id)(graph);
37421 return graph.remove(node);
37428 // First choose a node to be the survivor, with preference given
37429 // to an existing (not new) node.
37431 // Tags and relation memberships of of non-surviving nodes are merged
37432 // to the survivor.
37434 // This is the inverse of `iD.actionDisconnect`.
37437 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
37438 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
37441 function actionConnect(nodeIDs) {
37442 var action = function action(graph) {
37446 var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
37448 for (i = 0; i < nodeIDs.length; i++) {
37449 survivor = graph.entity(nodeIDs[i]);
37450 if (survivor.version) break; // found one
37451 } // Replace all non-surviving nodes with the survivor and merge tags.
37454 for (i = 0; i < nodeIDs.length; i++) {
37455 node = graph.entity(nodeIDs[i]);
37456 if (node.id === survivor.id) continue;
37457 parents = graph.parentWays(node);
37459 for (j = 0; j < parents.length; j++) {
37460 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
37463 parents = graph.parentRelations(node);
37465 for (j = 0; j < parents.length; j++) {
37466 graph = graph.replace(parents[j].replaceMember(node, survivor));
37469 survivor = survivor.mergeTags(node.tags);
37470 graph = actionDeleteNode(node.id)(graph);
37473 graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
37475 parents = graph.parentWays(survivor);
37477 for (i = 0; i < parents.length; i++) {
37478 if (parents[i].isDegenerate()) {
37479 graph = actionDeleteWay(parents[i].id)(graph);
37486 action.disabled = function (graph) {
37488 var restrictionIDs = [];
37491 var relations, relation, role;
37492 var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
37494 for (i = 0; i < nodeIDs.length; i++) {
37495 survivor = graph.entity(nodeIDs[i]);
37496 if (survivor.version) break; // found one
37497 } // 1. disable if the nodes being connected have conflicting relation roles
37500 for (i = 0; i < nodeIDs.length; i++) {
37501 node = graph.entity(nodeIDs[i]);
37502 relations = graph.parentRelations(node);
37504 for (j = 0; j < relations.length; j++) {
37505 relation = relations[j];
37506 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
37508 if (relation.hasFromViaTo()) {
37509 restrictionIDs.push(relation.id);
37512 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
37515 seen[relation.id] = role;
37518 } // gather restrictions for parent ways
37521 for (i = 0; i < nodeIDs.length; i++) {
37522 node = graph.entity(nodeIDs[i]);
37523 var parents = graph.parentWays(node);
37525 for (j = 0; j < parents.length; j++) {
37526 var parent = parents[j];
37527 relations = graph.parentRelations(parent);
37529 for (k = 0; k < relations.length; k++) {
37530 relation = relations[k];
37532 if (relation.hasFromViaTo()) {
37533 restrictionIDs.push(relation.id);
37537 } // test restrictions
37540 restrictionIDs = utilArrayUniq(restrictionIDs);
37542 for (i = 0; i < restrictionIDs.length; i++) {
37543 relation = graph.entity(restrictionIDs[i]);
37544 if (!relation.isComplete(graph)) continue;
37545 var memberWays = relation.members.filter(function (m) {
37546 return m.type === 'way';
37547 }).map(function (m) {
37548 return graph.entity(m.id);
37550 memberWays = utilArrayUniq(memberWays);
37551 var f = relation.memberByRole('from');
37552 var t = relation.memberByRole('to');
37553 var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
37554 // (a key node is a node at the junction of ways)
37564 for (j = 0; j < relation.members.length; j++) {
37565 collectNodes(relation.members[j], nodes);
37568 nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
37569 nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
37570 var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
37571 nodes.from = nodes.from.filter(filter);
37572 nodes.via = nodes.via.filter(filter);
37573 nodes.to = nodes.to.filter(filter);
37574 var connectFrom = false;
37575 var connectVia = false;
37576 var connectTo = false;
37577 var connectKeyFrom = false;
37578 var connectKeyTo = false;
37580 for (j = 0; j < nodeIDs.length; j++) {
37581 var n = nodeIDs[j];
37583 if (nodes.from.indexOf(n) !== -1) {
37584 connectFrom = true;
37587 if (nodes.via.indexOf(n) !== -1) {
37591 if (nodes.to.indexOf(n) !== -1) {
37595 if (nodes.keyfrom.indexOf(n) !== -1) {
37596 connectKeyFrom = true;
37599 if (nodes.keyto.indexOf(n) !== -1) {
37600 connectKeyTo = true;
37604 if (connectFrom && connectTo && !isUturn) {
37605 return 'restriction';
37608 if (connectFrom && connectVia) {
37609 return 'restriction';
37612 if (connectTo && connectVia) {
37613 return 'restriction';
37614 } // connecting to a key node -
37615 // if both nodes are on a member way (i.e. part of the turn restriction),
37616 // the connecting node must be adjacent to the key node.
37619 if (connectKeyFrom || connectKeyTo) {
37620 if (nodeIDs.length !== 2) {
37621 return 'restriction';
37627 for (j = 0; j < memberWays.length; j++) {
37628 way = memberWays[j];
37630 if (way.contains(nodeIDs[0])) {
37634 if (way.contains(nodeIDs[1])) {
37640 // both nodes are part of the restriction
37643 for (j = 0; j < memberWays.length; j++) {
37644 way = memberWays[j];
37646 if (way.areAdjacent(n0, n1)) {
37653 return 'restriction';
37656 } // 2b. disable if nodes being connected will destroy a member way in a restriction
37657 // (to test, make a copy and try actually connecting the nodes)
37660 for (j = 0; j < memberWays.length; j++) {
37661 way = memberWays[j].update({}); // make copy
37663 for (k = 0; k < nodeIDs.length; k++) {
37664 if (nodeIDs[k] === survivor.id) continue;
37666 if (way.areAdjacent(nodeIDs[k], survivor.id)) {
37667 way = way.removeNode(nodeIDs[k]);
37669 way = way.replaceNode(nodeIDs[k], survivor.id);
37673 if (way.isDegenerate()) {
37674 return 'restriction';
37679 return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
37681 function hasDuplicates(n, i, arr) {
37682 return arr.indexOf(n) !== arr.lastIndexOf(n);
37685 function keyNodeFilter(froms, tos) {
37686 return function (n) {
37687 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
37691 function collectNodes(member, collection) {
37692 var entity = graph.hasEntity(member.id);
37693 if (!entity) return;
37694 var role = member.role || '';
37696 if (!collection[role]) {
37697 collection[role] = [];
37700 if (member.type === 'node') {
37701 collection[role].push(member.id);
37703 if (role === 'via') {
37704 collection.keyfrom.push(member.id);
37705 collection.keyto.push(member.id);
37707 } else if (member.type === 'way') {
37708 collection[role].push.apply(collection[role], entity.nodes);
37710 if (role === 'from' || role === 'via') {
37711 collection.keyfrom.push(entity.first());
37712 collection.keyfrom.push(entity.last());
37715 if (role === 'to' || role === 'via') {
37716 collection.keyto.push(entity.first());
37717 collection.keyto.push(entity.last());
37726 function actionCopyEntities(ids, fromGraph) {
37729 var action = function action(graph) {
37730 ids.forEach(function (id) {
37731 fromGraph.entity(id).copy(fromGraph, _copies);
37734 for (var id in _copies) {
37735 graph = graph.replace(_copies[id]);
37741 action.copies = function () {
37748 function actionDeleteMember(relationId, memberIndex) {
37749 return function (graph) {
37750 var relation = graph.entity(relationId).removeMember(memberIndex);
37751 graph = graph.replace(relation);
37753 if (relation.isDegenerate()) {
37754 graph = actionDeleteRelation(relation.id)(graph);
37761 function actionDiscardTags(difference, discardTags) {
37762 discardTags = discardTags || {};
37763 return function (graph) {
37764 difference.modified().forEach(checkTags);
37765 difference.created().forEach(checkTags);
37768 function checkTags(entity) {
37769 var keys = Object.keys(entity.tags);
37770 var didDiscard = false;
37773 for (var i = 0; i < keys.length; i++) {
37776 if (discardTags[k] || !entity.tags[k]) {
37779 tags[k] = entity.tags[k];
37784 graph = graph.replace(entity.update({
37793 // Optionally, disconnect only the given ways.
37795 // For testing convenience, accepts an ID to assign to the (first) new node.
37796 // Normally, this will be undefined and the way will automatically
37797 // be assigned a new ID.
37799 // This is the inverse of `iD.actionConnect`.
37802 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
37803 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
37806 function actionDisconnect(nodeId, newNodeId) {
37809 var action = function action(graph) {
37810 var node = graph.entity(nodeId);
37811 var connections = action.connections(graph);
37812 connections.forEach(function (connection) {
37813 var way = graph.entity(connection.wayID);
37814 var newNode = osmNode({
37819 graph = graph.replace(newNode);
37821 if (connection.index === 0 && way.isArea()) {
37822 // replace shared node with shared node..
37823 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
37824 } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
37825 // replace closing node with new new node..
37826 graph = graph.replace(way.unclose().addNode(newNode.id));
37828 // replace shared node with multiple new nodes..
37829 graph = graph.replace(way.updateNode(newNode.id, connection.index));
37835 action.connections = function (graph) {
37836 var candidates = [];
37837 var keeping = false;
37838 var parentWays = graph.parentWays(graph.entity(nodeId));
37841 for (var i = 0; i < parentWays.length; i++) {
37842 way = parentWays[i];
37844 if (wayIds && wayIds.indexOf(way.id) === -1) {
37849 if (way.isArea() && way.nodes[0] === nodeId) {
37855 for (var j = 0; j < way.nodes.length; j++) {
37856 waynode = way.nodes[j];
37858 if (waynode === nodeId) {
37859 if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
37872 return keeping ? candidates : candidates.slice(1);
37875 action.disabled = function (graph) {
37876 var connections = action.connections(graph);
37877 if (connections.length === 0) return 'not_connected';
37878 var parentWays = graph.parentWays(graph.entity(nodeId));
37879 var seenRelationIds = {};
37880 var sharedRelation;
37881 parentWays.forEach(function (way) {
37882 var relations = graph.parentRelations(way);
37883 relations.forEach(function (relation) {
37884 if (relation.id in seenRelationIds) {
37886 if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
37887 sharedRelation = relation;
37890 sharedRelation = relation;
37893 seenRelationIds[relation.id] = way.id;
37897 if (sharedRelation) return 'relation';
37900 action.limitWays = function (val) {
37901 if (!arguments.length) return wayIds;
37909 function actionExtract(entityID, projection) {
37910 var extractedNodeID;
37912 var action = function action(graph) {
37913 var entity = graph.entity(entityID);
37915 if (entity.type === 'node') {
37916 return extractFromNode(entity, graph);
37919 return extractFromWayOrRelation(entity, graph);
37922 function extractFromNode(node, graph) {
37923 extractedNodeID = node.id; // Create a new node to replace the one we will detach
37925 var replacement = osmNode({
37928 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
37930 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
37931 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
37932 }, graph); // Process any relations too
37934 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
37935 return accGraph.replace(parentRel.replaceMember(node, replacement));
37939 function extractFromWayOrRelation(entity, graph) {
37940 var fromGeometry = entity.geometry(graph);
37941 var keysToCopyAndRetain = ['source', 'wheelchair'];
37942 var keysToRetain = ['area'];
37943 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
37944 var extractedLoc = d3_geoPath(projection).centroid(entity.asGeoJSON(graph));
37945 extractedLoc = extractedLoc && projection.invert(extractedLoc);
37947 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
37948 extractedLoc = entity.extent(graph).center();
37951 var indoorAreaValues = {
37958 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
37959 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
37960 var entityTags = Object.assign({}, entity.tags); // shallow copy
37962 var pointTags = {};
37964 for (var key in entityTags) {
37965 if (entity.type === 'relation' && key === 'type') {
37969 if (keysToRetain.indexOf(key) !== -1) {
37974 // don't transfer building-related tags
37975 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
37976 } // leave `indoor` tag on the area
37979 if (isIndoorArea && key === 'indoor') {
37981 } // copy the tag from the entity to the point
37984 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
37986 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
37988 } else if (isIndoorArea && key === 'level') {
37989 // leave `level` on both features
37991 } // remove the tag from the entity
37994 delete entityTags[key];
37997 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
37998 // ensure that areas keep area geometry
37999 entityTags.area = 'yes';
38002 var replacement = osmNode({
38006 graph = graph.replace(replacement);
38007 extractedNodeID = replacement.id;
38008 return graph.replace(entity.update({
38013 action.getExtractedNodeID = function () {
38014 return extractedNodeID;
38021 // This is the inverse of `iD.actionSplit`.
38024 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
38025 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
38028 function actionJoin(ids) {
38029 function groupEntitiesByGeometry(graph) {
38030 var entities = ids.map(function (id) {
38031 return graph.entity(id);
38033 return Object.assign({
38035 }, utilArrayGroupBy(entities, function (entity) {
38036 return entity.geometry(graph);
38040 var action = function action(graph) {
38041 var ways = ids.map(graph.entity, graph);
38042 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
38043 // sort them first so they establish the overall order - #6033
38045 ways.sort(function (a, b) {
38046 var aSided = a.isSided();
38047 var bSided = b.isSided();
38048 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
38049 }); // Prefer to keep an existing way.
38051 for (var i = 0; i < ways.length; i++) {
38052 if (!ways[i].isNew()) {
38053 survivorID = ways[i].id;
38058 var sequences = osmJoinWays(ways, graph);
38059 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
38060 // `joined.actions` property will contain any actions we need to apply.
38062 graph = sequences.actions.reduce(function (g, action) {
38065 var survivor = graph.entity(survivorID);
38066 survivor = survivor.update({
38067 nodes: joined.nodes.map(function (n) {
38071 graph = graph.replace(survivor);
38072 joined.forEach(function (way) {
38073 if (way.id === survivorID) return;
38074 graph.parentRelations(way).forEach(function (parent) {
38075 graph = graph.replace(parent.replaceMember(way, survivor));
38077 survivor = survivor.mergeTags(way.tags);
38078 graph = graph.replace(survivor);
38079 graph = actionDeleteWay(way.id)(graph);
38080 }); // Finds if the join created a single-member multipolygon,
38081 // and if so turns it into a basic area instead
38083 function checkForSimpleMultipolygon() {
38084 if (!survivor.isClosed()) return;
38085 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
38086 // find multipolygons where the survivor is the only member
38087 return multipolygon.members.length === 1;
38088 }); // skip if this is the single member of multiple multipolygons
38090 if (multipolygons.length !== 1) return;
38091 var multipolygon = multipolygons[0];
38093 for (var key in survivor.tags) {
38094 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
38095 multipolygon.tags[key] !== survivor.tags[key]) return;
38098 survivor = survivor.mergeTags(multipolygon.tags);
38099 graph = graph.replace(survivor);
38100 graph = actionDeleteRelation(multipolygon.id, true
38101 /* allow untagged members */
38103 var tags = Object.assign({}, survivor.tags);
38105 if (survivor.geometry(graph) !== 'area') {
38106 // ensure the feature persists as an area
38110 delete tags.type; // remove type=multipolygon
38112 survivor = survivor.update({
38115 graph = graph.replace(survivor);
38118 checkForSimpleMultipolygon();
38120 }; // Returns the number of nodes the resultant way is expected to have
38123 action.resultingWayNodesLength = function (graph) {
38124 return ids.reduce(function (count, id) {
38125 return count + graph.entity(id).nodes.length;
38126 }, 0) - ids.length - 1;
38129 action.disabled = function (graph) {
38130 var geometries = groupEntitiesByGeometry(graph);
38132 if (ids.length < 2 || ids.length !== geometries.line.length) {
38133 return 'not_eligible';
38136 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
38138 if (joined.length > 1) {
38139 return 'not_adjacent';
38140 } // Loop through all combinations of path-pairs
38141 // to check potential intersections between all pairs
38144 for (var i = 0; i < ids.length - 1; i++) {
38145 for (var j = i + 1; j < ids.length; j++) {
38146 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
38149 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
38152 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
38153 // each other/the line, as opposed to crossing it
38155 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
38156 return n.loc.toString();
38157 }), intersections.map(function (n) {
38158 return n.toString();
38161 if (common.length !== intersections.length) {
38162 return 'paths_intersect';
38167 var nodeIds = joined[0].nodes.map(function (n) {
38172 var conflicting = false;
38173 joined[0].forEach(function (way) {
38174 var parents = graph.parentRelations(way);
38175 parents.forEach(function (parent) {
38176 if (parent.isRestriction() && parent.members.some(function (m) {
38177 return nodeIds.indexOf(m.id) >= 0;
38183 for (var k in way.tags) {
38184 if (!(k in tags)) {
38185 tags[k] = way.tags[k];
38186 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
38187 conflicting = true;
38193 return 'restriction';
38197 return 'conflicting_tags';
38204 function actionMerge(ids) {
38205 function groupEntitiesByGeometry(graph) {
38206 var entities = ids.map(function (id) {
38207 return graph.entity(id);
38209 return Object.assign({
38214 }, utilArrayGroupBy(entities, function (entity) {
38215 return entity.geometry(graph);
38219 var action = function action(graph) {
38220 var geometries = groupEntitiesByGeometry(graph);
38221 var target = geometries.area[0] || geometries.line[0];
38222 var points = geometries.point;
38223 points.forEach(function (point) {
38224 target = target.mergeTags(point.tags);
38225 graph = graph.replace(target);
38226 graph.parentRelations(point).forEach(function (parent) {
38227 graph = graph.replace(parent.replaceMember(point, target));
38229 var nodes = utilArrayUniq(graph.childNodes(target));
38230 var removeNode = point;
38232 for (var i = 0; i < nodes.length; i++) {
38233 var node = nodes[i];
38235 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
38237 } // Found an uninteresting child node on the target way.
38238 // Move orig point into its place to preserve point's history. #3683
38241 graph = graph.replace(point.update({
38245 target = target.replaceNode(node.id, point.id);
38246 graph = graph.replace(target);
38251 graph = graph.remove(removeNode);
38254 if (target.tags.area === 'yes') {
38255 var tags = Object.assign({}, target.tags); // shallow copy
38259 if (osmTagSuggestingArea(tags)) {
38260 // remove the `area` tag if area geometry is now implied - #3851
38261 target = target.update({
38264 graph = graph.replace(target);
38271 action.disabled = function (graph) {
38272 var geometries = groupEntitiesByGeometry(graph);
38274 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
38275 return 'not_eligible';
38283 // 1. move all the nodes to a common location
38284 // 2. `actionConnect` them
38286 function actionMergeNodes(nodeIDs, loc) {
38287 // If there is a single "interesting" node, use that as the location.
38288 // Otherwise return the average location of all the nodes.
38289 function chooseLoc(graph) {
38290 if (!nodeIDs.length) return null;
38292 var interestingCount = 0;
38293 var interestingLoc;
38295 for (var i = 0; i < nodeIDs.length; i++) {
38296 var node = graph.entity(nodeIDs[i]);
38298 if (node.hasInterestingTags()) {
38299 interestingLoc = ++interestingCount === 1 ? node.loc : null;
38302 sum = geoVecAdd(sum, node.loc);
38305 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
38308 var action = function action(graph) {
38309 if (nodeIDs.length < 2) return graph;
38313 toLoc = chooseLoc(graph);
38316 for (var i = 0; i < nodeIDs.length; i++) {
38317 var node = graph.entity(nodeIDs[i]);
38319 if (node.loc !== toLoc) {
38320 graph = graph.replace(node.move(toLoc));
38324 return actionConnect(nodeIDs)(graph);
38327 action.disabled = function (graph) {
38328 if (nodeIDs.length < 2) return 'not_eligible';
38330 for (var i = 0; i < nodeIDs.length; i++) {
38331 var entity = graph.entity(nodeIDs[i]);
38332 if (entity.type !== 'node') return 'not_eligible';
38335 return actionConnect(nodeIDs).disabled(graph);
38341 function osmChangeset() {
38342 if (!(this instanceof osmChangeset)) {
38343 return new osmChangeset().initialize(arguments);
38344 } else if (arguments.length) {
38345 this.initialize(arguments);
38348 osmEntity.changeset = osmChangeset;
38349 osmChangeset.prototype = Object.create(osmEntity.prototype);
38350 Object.assign(osmChangeset.prototype, {
38352 extent: function extent() {
38353 return new geoExtent();
38355 geometry: function geometry() {
38356 return 'changeset';
38358 asJXON: function asJXON() {
38362 tag: Object.keys(this.tags).map(function (k) {
38374 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
38375 // XML. Returns a string.
38376 osmChangeJXON: function osmChangeJXON(changes) {
38377 var changeset_id = this.id;
38379 function nest(x, order) {
38382 for (var i = 0; i < x.length; i++) {
38383 var tagName = Object.keys(x[i])[0];
38384 if (!groups[tagName]) groups[tagName] = [];
38385 groups[tagName].push(x[i][tagName]);
38389 order.forEach(function (o) {
38390 if (groups[o]) ordered[o] = groups[o];
38393 } // sort relations in a changeset by dependencies
38396 function sort(changes) {
38397 // find a referenced relation in the current changeset
38398 function resolve(item) {
38399 return relations.find(function (relation) {
38400 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
38402 } // a new item is an item that has not been already processed
38405 function isNew(item) {
38406 return !sorted[item['@id']] && !processing.find(function (proc) {
38407 return proc['@id'] === item['@id'];
38411 var processing = [];
38413 var relations = changes.relation;
38414 if (!relations) return changes;
38416 for (var i = 0; i < relations.length; i++) {
38417 var relation = relations[i]; // skip relation if already sorted
38419 if (!sorted[relation['@id']]) {
38420 processing.push(relation);
38423 while (processing.length > 0) {
38424 var next = processing[0],
38425 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
38427 if (deps.length === 0) {
38428 sorted[next['@id']] = next;
38429 processing.shift();
38431 processing = deps.concat(processing);
38436 changes.relation = Object.values(sorted);
38440 function rep(entity) {
38441 return entity.asJXON(changeset_id);
38447 '@generator': 'iD',
38448 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
38449 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
38450 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
38456 asGeoJSON: function asGeoJSON() {
38461 function osmNote() {
38462 if (!(this instanceof osmNote)) {
38463 return new osmNote().initialize(arguments);
38464 } else if (arguments.length) {
38465 this.initialize(arguments);
38469 osmNote.id = function () {
38470 return osmNote.id.next--;
38473 osmNote.id.next = -1;
38474 Object.assign(osmNote.prototype, {
38476 initialize: function initialize(sources) {
38477 for (var i = 0; i < sources.length; ++i) {
38478 var source = sources[i];
38480 for (var prop in source) {
38481 if (Object.prototype.hasOwnProperty.call(source, prop)) {
38482 if (source[prop] === undefined) {
38485 this[prop] = source[prop];
38492 this.id = osmNote.id().toString();
38497 extent: function extent() {
38498 return new geoExtent(this.loc);
38500 update: function update(attrs) {
38501 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
38503 isNew: function isNew() {
38504 return this.id < 0;
38506 move: function move(loc) {
38507 return this.update({
38513 function osmRelation() {
38514 if (!(this instanceof osmRelation)) {
38515 return new osmRelation().initialize(arguments);
38516 } else if (arguments.length) {
38517 this.initialize(arguments);
38520 osmEntity.relation = osmRelation;
38521 osmRelation.prototype = Object.create(osmEntity.prototype);
38523 osmRelation.creationOrder = function (a, b) {
38524 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
38525 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
38526 if (aId < 0 || bId < 0) return aId - bId;
38530 Object.assign(osmRelation.prototype, {
38533 copy: function copy(resolver, copies) {
38534 if (copies[this.id]) return copies[this.id];
38535 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
38536 var members = this.members.map(function (member) {
38537 return Object.assign({}, member, {
38538 id: resolver.entity(member.id).copy(resolver, copies).id
38541 copy = copy.update({
38544 copies[this.id] = copy;
38547 extent: function extent(resolver, memo) {
38548 return resolver["transient"](this, 'extent', function () {
38549 if (memo && memo[this.id]) return geoExtent();
38551 memo[this.id] = true;
38552 var extent = geoExtent();
38554 for (var i = 0; i < this.members.length; i++) {
38555 var member = resolver.hasEntity(this.members[i].id);
38558 extent._extend(member.extent(resolver, memo));
38565 geometry: function geometry(graph) {
38566 return graph["transient"](this, 'geometry', function () {
38567 return this.isMultipolygon() ? 'area' : 'relation';
38570 isDegenerate: function isDegenerate() {
38571 return this.members.length === 0;
38573 // Return an array of members, each extended with an 'index' property whose value
38574 // is the member index.
38575 indexedMembers: function indexedMembers() {
38576 var result = new Array(this.members.length);
38578 for (var i = 0; i < this.members.length; i++) {
38579 result[i] = Object.assign({}, this.members[i], {
38586 // Return the first member with the given role. A copy of the member object
38587 // is returned, extended with an 'index' property whose value is the member index.
38588 memberByRole: function memberByRole(role) {
38589 for (var i = 0; i < this.members.length; i++) {
38590 if (this.members[i].role === role) {
38591 return Object.assign({}, this.members[i], {
38597 // Same as memberByRole, but returns all members with the given role
38598 membersByRole: function membersByRole(role) {
38601 for (var i = 0; i < this.members.length; i++) {
38602 if (this.members[i].role === role) {
38603 result.push(Object.assign({}, this.members[i], {
38611 // Return the first member with the given id. A copy of the member object
38612 // is returned, extended with an 'index' property whose value is the member index.
38613 memberById: function memberById(id) {
38614 for (var i = 0; i < this.members.length; i++) {
38615 if (this.members[i].id === id) {
38616 return Object.assign({}, this.members[i], {
38622 // Return the first member with the given id and role. A copy of the member object
38623 // is returned, extended with an 'index' property whose value is the member index.
38624 memberByIdAndRole: function memberByIdAndRole(id, role) {
38625 for (var i = 0; i < this.members.length; i++) {
38626 if (this.members[i].id === id && this.members[i].role === role) {
38627 return Object.assign({}, this.members[i], {
38633 addMember: function addMember(member, index) {
38634 var members = this.members.slice();
38635 members.splice(index === undefined ? members.length : index, 0, member);
38636 return this.update({
38640 updateMember: function updateMember(member, index) {
38641 var members = this.members.slice();
38642 members.splice(index, 1, Object.assign({}, members[index], member));
38643 return this.update({
38647 removeMember: function removeMember(index) {
38648 var members = this.members.slice();
38649 members.splice(index, 1);
38650 return this.update({
38654 removeMembersWithID: function removeMembersWithID(id) {
38655 var members = this.members.filter(function (m) {
38656 return m.id !== id;
38658 return this.update({
38662 moveMember: function moveMember(fromIndex, toIndex) {
38663 var members = this.members.slice();
38664 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
38665 return this.update({
38669 // Wherever a member appears with id `needle.id`, replace it with a member
38670 // with id `replacement.id`, type `replacement.type`, and the original role,
38671 // By default, adding a duplicate member (by id and role) is prevented.
38672 // Return an updated relation.
38673 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
38674 if (!this.memberById(needle.id)) return this;
38677 for (var i = 0; i < this.members.length; i++) {
38678 var member = this.members[i];
38680 if (member.id !== needle.id) {
38681 members.push(member);
38682 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
38684 id: replacement.id,
38685 type: replacement.type,
38691 return this.update({
38695 asJXON: function asJXON(changeset_id) {
38698 '@id': this.osmId(),
38699 '@version': this.version || 0,
38700 member: this.members.map(function (member) {
38705 ref: osmEntity.id.toOSM(member.id)
38709 tag: Object.keys(this.tags).map(function (k) {
38720 if (changeset_id) {
38721 r.relation['@changeset'] = changeset_id;
38726 asGeoJSON: function asGeoJSON(resolver) {
38727 return resolver["transient"](this, 'GeoJSON', function () {
38728 if (this.isMultipolygon()) {
38730 type: 'MultiPolygon',
38731 coordinates: this.multipolygon(resolver)
38735 type: 'FeatureCollection',
38736 properties: this.tags,
38737 features: this.members.map(function (member) {
38738 return Object.assign({
38740 }, resolver.entity(member.id).asGeoJSON(resolver));
38746 area: function area(resolver) {
38747 return resolver["transient"](this, 'area', function () {
38748 return d3_geoArea(this.asGeoJSON(resolver));
38751 isMultipolygon: function isMultipolygon() {
38752 return this.tags.type === 'multipolygon';
38754 isComplete: function isComplete(resolver) {
38755 for (var i = 0; i < this.members.length; i++) {
38756 if (!resolver.hasEntity(this.members[i].id)) {
38763 hasFromViaTo: function hasFromViaTo() {
38764 return this.members.some(function (m) {
38765 return m.role === 'from';
38766 }) && this.members.some(function (m) {
38767 return m.role === 'via';
38768 }) && this.members.some(function (m) {
38769 return m.role === 'to';
38772 isRestriction: function isRestriction() {
38773 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
38775 isValidRestriction: function isValidRestriction() {
38776 if (!this.isRestriction()) return false;
38777 var froms = this.members.filter(function (m) {
38778 return m.role === 'from';
38780 var vias = this.members.filter(function (m) {
38781 return m.role === 'via';
38783 var tos = this.members.filter(function (m) {
38784 return m.role === 'to';
38786 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
38787 if (froms.some(function (m) {
38788 return m.type !== 'way';
38790 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
38791 if (tos.some(function (m) {
38792 return m.type !== 'way';
38794 if (vias.length === 0) return false;
38795 if (vias.length > 1 && vias.some(function (m) {
38796 return m.type !== 'way';
38800 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
38801 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
38803 // This corresponds to the structure needed for rendering a multipolygon path using a
38804 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
38806 // In the case of invalid geometries, this function will still return a result which
38807 // includes the nodes of all way members, but some Nds may be unclosed and some inner
38808 // rings not matched with the intended outer ring.
38810 multipolygon: function multipolygon(resolver) {
38811 var outers = this.members.filter(function (m) {
38812 return 'outer' === (m.role || 'outer');
38814 var inners = this.members.filter(function (m) {
38815 return 'inner' === m.role;
38817 outers = osmJoinWays(outers, resolver);
38818 inners = osmJoinWays(inners, resolver);
38820 var sequenceToLineString = function sequenceToLineString(sequence) {
38821 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
38822 // close unclosed parts to ensure correct area rendering - #2945
38823 sequence.nodes.push(sequence.nodes[0]);
38826 return sequence.nodes.map(function (node) {
38831 outers = outers.map(sequenceToLineString);
38832 inners = inners.map(sequenceToLineString);
38833 var result = outers.map(function (o) {
38834 // Heuristic for detecting counterclockwise winding order. Assumes
38835 // that OpenStreetMap polygons are not hemisphere-spanning.
38836 return [d3_geoArea({
38839 }) > 2 * Math.PI ? o.reverse() : o];
38842 function findOuter(inner) {
38845 for (o = 0; o < outers.length; o++) {
38848 if (geoPolygonContainsPolygon(outer, inner)) {
38853 for (o = 0; o < outers.length; o++) {
38856 if (geoPolygonIntersectsPolygon(outer, inner, false)) {
38862 for (var i = 0; i < inners.length; i++) {
38863 var inner = inners[i];
38867 coordinates: [inner]
38868 }) < 2 * Math.PI) {
38869 inner = inner.reverse();
38872 var o = findOuter(inners[i]);
38874 if (o !== undefined) {
38875 result[o].push(inners[i]);
38877 result.push([inners[i]]); // Invalid geometry
38885 var QAItem = /*#__PURE__*/function () {
38886 function QAItem(loc, service, itemType, id, props) {
38887 _classCallCheck$1(this, QAItem);
38889 // Store required properties
38891 this.service = service.title;
38892 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
38894 this.id = id ? id : "".concat(QAItem.id());
38895 this.update(props); // Some QA services have marker icons to differentiate issues
38897 if (service && typeof service.getIcon === 'function') {
38898 this.icon = service.getIcon(itemType);
38902 _createClass$1(QAItem, [{
38904 value: function update(props) {
38907 // You can't override this initial information
38908 var loc = this.loc,
38909 service = this.service,
38910 itemType = this.itemType,
38912 Object.keys(props).forEach(function (prop) {
38913 return _this[prop] = props[prop];
38916 this.service = service;
38917 this.itemType = itemType;
38920 } // Generic handling for newly created QAItems
38924 value: function id() {
38925 return this.nextId--;
38931 QAItem.nextId = -1;
38934 // Optionally, split only the given ways, if multiple ways share
38937 // This is the inverse of `iD.actionJoin`.
38939 // For testing convenience, accepts an ID to assign to the new way.
38940 // Normally, this will be undefined and the way will automatically
38941 // be assigned a new ID.
38944 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
38947 function actionSplit(nodeIds, newWayIds) {
38948 // accept single ID for backwards-compatiblity
38949 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
38951 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
38954 var _keepHistoryOn = 'longest'; // 'longest', 'first'
38955 // The IDs of the ways actually created by running this action
38957 var _createdWayIDs = [];
38959 function dist(graph, nA, nB) {
38960 var locA = graph.entity(nA).loc;
38961 var locB = graph.entity(nB).loc;
38962 var epsilon = 1e-6;
38963 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
38964 } // If the way is closed, we need to search for a partner node
38965 // to split the way at.
38967 // The following looks for a node that is both far away from
38968 // the initial node in terms of way segment length and nearby
38969 // in terms of beeline-distance. This assures that areas get
38970 // split on the most "natural" points (independent of the number
38972 // For example: bone-shaped areas get split across their waist
38973 // line, circles across the diameter.
38976 function splitArea(nodes, idxA, graph) {
38977 var lengths = new Array(nodes.length);
38983 function wrap(index) {
38984 return utilWrap(index, nodes.length);
38985 } // calculate lengths
38990 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
38991 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
38992 lengths[i] = length;
38997 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
38998 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
39000 if (length < lengths[i]) {
39001 lengths[i] = length;
39003 } // determine best opposite node to split
39006 for (i = 0; i < nodes.length; i++) {
39007 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
39018 function totalLengthBetweenNodes(graph, nodes) {
39019 var totalLength = 0;
39021 for (var i = 0; i < nodes.length - 1; i++) {
39022 totalLength += dist(graph, nodes[i], nodes[i + 1]);
39025 return totalLength;
39028 function split(graph, nodeId, wayA, newWayId) {
39029 var wayB = osmWay({
39032 }); // `wayB` is the NEW way
39034 var origNodes = wayA.nodes.slice();
39037 var isArea = wayA.isArea();
39038 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
39040 if (wayA.isClosed()) {
39041 var nodes = wayA.nodes.slice(0, -1);
39042 var idxA = nodes.indexOf(nodeId);
39043 var idxB = splitArea(nodes, idxA, graph);
39046 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
39047 nodesB = nodes.slice(idxB, idxA + 1);
39049 nodesA = nodes.slice(idxA, idxB + 1);
39050 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
39053 var idx = wayA.nodes.indexOf(nodeId, 1);
39054 nodesA = wayA.nodes.slice(0, idx + 1);
39055 nodesB = wayA.nodes.slice(idx);
39058 var lengthA = totalLengthBetweenNodes(graph, nodesA);
39059 var lengthB = totalLengthBetweenNodes(graph, nodesB);
39061 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
39062 // keep the history on the longer way, regardless of the node count
39063 wayA = wayA.update({
39066 wayB = wayB.update({
39069 var temp = lengthA;
39073 wayA = wayA.update({
39076 wayB = wayB.update({
39081 if (wayA.tags.step_count) {
39082 // divide up the the step count proportionally between the two ways
39083 var stepCount = parseFloat(wayA.tags.step_count);
39085 if (stepCount && // ensure a number
39086 isFinite(stepCount) && // ensure positive
39087 stepCount > 0 && // ensure integer
39088 Math.round(stepCount) === stepCount) {
39089 var tagsA = Object.assign({}, wayA.tags);
39090 var tagsB = Object.assign({}, wayB.tags);
39091 var ratioA = lengthA / (lengthA + lengthB);
39092 var countA = Math.round(stepCount * ratioA);
39093 tagsA.step_count = countA.toString();
39094 tagsB.step_count = (stepCount - countA).toString();
39095 wayA = wayA.update({
39098 wayB = wayB.update({
39104 graph = graph.replace(wayA);
39105 graph = graph.replace(wayB);
39106 graph.parentRelations(wayA).forEach(function (relation) {
39107 var member; // Turn restrictions - make sure:
39108 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
39109 // (whichever one is connected to the VIA node/ways)
39110 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
39112 if (relation.hasFromViaTo()) {
39113 var f = relation.memberByRole('from');
39114 var v = relation.membersByRole('via');
39115 var t = relation.memberByRole('to');
39116 var i; // 1. split a FROM/TO
39118 if (f.id === wayA.id || t.id === wayA.id) {
39121 if (v.length === 1 && v[0].type === 'node') {
39123 keepB = wayB.contains(v[0].id);
39125 // check via way(s)
39126 for (i = 0; i < v.length; i++) {
39127 if (v[i].type === 'way') {
39128 var wayVia = graph.hasEntity(v[i].id);
39130 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
39139 relation = relation.replaceMember(wayA, wayB);
39140 graph = graph.replace(relation);
39141 } // 2. split a VIA
39144 for (i = 0; i < v.length; i++) {
39145 if (v[i].type === 'way' && v[i].id === wayA.id) {
39151 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
39155 } // All other relations (Routes, Multipolygons, etc):
39156 // 1. Both `wayA` and `wayB` remain in the relation
39157 // 2. But must be inserted as a pair (see `actionAddMember` for details)
39160 if (relation === isOuter) {
39161 graph = graph.replace(relation.mergeTags(wayA.tags));
39162 graph = graph.replace(wayA.update({
39165 graph = graph.replace(wayB.update({
39173 role: relation.memberById(wayA.id).role
39176 originalID: wayA.id,
39177 insertedID: wayB.id,
39180 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
39184 if (!isOuter && isArea) {
39185 var multipolygon = osmRelation({
39186 tags: Object.assign({}, wayA.tags, {
39187 type: 'multipolygon'
39199 graph = graph.replace(multipolygon);
39200 graph = graph.replace(wayA.update({
39203 graph = graph.replace(wayB.update({
39208 _createdWayIDs.push(wayB.id);
39213 var action = function action(graph) {
39214 _createdWayIDs = [];
39215 var newWayIndex = 0;
39217 for (var i = 0; i < nodeIds.length; i++) {
39218 var nodeId = nodeIds[i];
39219 var candidates = action.waysForNode(nodeId, graph);
39221 for (var j = 0; j < candidates.length; j++) {
39222 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
39230 action.getCreatedWayIDs = function () {
39231 return _createdWayIDs;
39234 action.waysForNode = function (nodeId, graph) {
39235 var node = graph.entity(nodeId);
39236 var splittableParents = graph.parentWays(node).filter(isSplittable);
39239 // If the ways to split aren't specified, only split the lines.
39240 // If there are no lines to split, split the areas.
39241 var hasLine = splittableParents.some(function (parent) {
39242 return parent.geometry(graph) === 'line';
39246 return splittableParents.filter(function (parent) {
39247 return parent.geometry(graph) === 'line';
39252 return splittableParents;
39254 function isSplittable(parent) {
39255 // If the ways to split are specified, ignore everything else.
39256 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
39258 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
39260 for (var i = 1; i < parent.nodes.length - 1; i++) {
39261 if (parent.nodes[i] === nodeId) return true;
39268 action.ways = function (graph) {
39269 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
39270 return action.waysForNode(nodeId, graph);
39274 action.disabled = function (graph) {
39275 for (var i = 0; i < nodeIds.length; i++) {
39276 var nodeId = nodeIds[i];
39277 var candidates = action.waysForNode(nodeId, graph);
39279 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
39280 return 'not_eligible';
39285 action.limitWays = function (val) {
39286 if (!arguments.length) return _wayIDs;
39291 action.keepHistoryOn = function (val) {
39292 if (!arguments.length) return _keepHistoryOn;
39293 _keepHistoryOn = val;
39300 function coreGraph(other, mutable) {
39301 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
39303 if (other instanceof coreGraph) {
39304 var base = other.base();
39305 this.entities = Object.assign(Object.create(base.entities), other.entities);
39306 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
39307 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
39309 this.entities = Object.create({});
39310 this._parentWays = Object.create({});
39311 this._parentRels = Object.create({});
39312 this.rebase(other || [], [this]);
39315 this.transients = {};
39316 this._childNodes = {};
39317 this.frozen = !mutable;
39319 coreGraph.prototype = {
39320 hasEntity: function hasEntity(id) {
39321 return this.entities[id];
39323 entity: function entity(id) {
39324 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
39327 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
39331 throw new Error('entity ' + id + ' not found');
39336 geometry: function geometry(id) {
39337 return this.entity(id).geometry(this);
39339 "transient": function transient(entity, key, fn) {
39340 var id = entity.id;
39341 var transients = this.transients[id] || (this.transients[id] = {});
39343 if (transients[key] !== undefined) {
39344 return transients[key];
39347 transients[key] = fn.call(entity);
39348 return transients[key];
39350 parentWays: function parentWays(entity) {
39351 var parents = this._parentWays[entity.id];
39355 parents.forEach(function (id) {
39356 result.push(this.entity(id));
39362 isPoi: function isPoi(entity) {
39363 var parents = this._parentWays[entity.id];
39364 return !parents || parents.size === 0;
39366 isShared: function isShared(entity) {
39367 var parents = this._parentWays[entity.id];
39368 return parents && parents.size > 1;
39370 parentRelations: function parentRelations(entity) {
39371 var parents = this._parentRels[entity.id];
39375 parents.forEach(function (id) {
39376 result.push(this.entity(id));
39382 parentMultipolygons: function parentMultipolygons(entity) {
39383 return this.parentRelations(entity).filter(function (relation) {
39384 return relation.isMultipolygon();
39387 childNodes: function childNodes(entity) {
39388 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
39389 if (!entity.nodes) return [];
39392 for (var i = 0; i < entity.nodes.length; i++) {
39393 nodes[i] = this.entity(entity.nodes[i]);
39395 this._childNodes[entity.id] = nodes;
39396 return this._childNodes[entity.id];
39398 base: function base() {
39400 'entities': Object.getPrototypeOf(this.entities),
39401 'parentWays': Object.getPrototypeOf(this._parentWays),
39402 'parentRels': Object.getPrototypeOf(this._parentRels)
39405 // Unlike other graph methods, rebase mutates in place. This is because it
39406 // is used only during the history operation that merges newly downloaded
39407 // data into each state. To external consumers, it should appear as if the
39408 // graph always contained the newly downloaded data.
39409 rebase: function rebase(entities, stack, force) {
39410 var base = this.base();
39413 for (i = 0; i < entities.length; i++) {
39414 var entity = entities[i];
39415 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
39417 base.entities[entity.id] = entity;
39419 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
39422 if (entity.type === 'way') {
39423 for (j = 0; j < entity.nodes.length; j++) {
39424 id = entity.nodes[j];
39426 for (k = 1; k < stack.length; k++) {
39427 var ents = stack[k].entities;
39429 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
39437 for (i = 0; i < stack.length; i++) {
39438 stack[i]._updateRebased();
39441 _updateRebased: function _updateRebased() {
39442 var base = this.base();
39443 Object.keys(this._parentWays).forEach(function (child) {
39444 if (base.parentWays[child]) {
39445 base.parentWays[child].forEach(function (id) {
39446 if (!this.entities.hasOwnProperty(id)) {
39447 this._parentWays[child].add(id);
39452 Object.keys(this._parentRels).forEach(function (child) {
39453 if (base.parentRels[child]) {
39454 base.parentRels[child].forEach(function (id) {
39455 if (!this.entities.hasOwnProperty(id)) {
39456 this._parentRels[child].add(id);
39461 this.transients = {}; // this._childNodes is not updated, under the assumption that
39462 // ways are always downloaded with their child nodes.
39464 // Updates calculated properties (parentWays, parentRels) for the specified change
39465 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
39466 parentWays = parentWays || this._parentWays;
39467 parentRels = parentRels || this._parentRels;
39468 var type = entity && entity.type || oldentity && oldentity.type;
39469 var removed, added, i;
39471 if (type === 'way') {
39472 // Update parentWays
39473 if (oldentity && entity) {
39474 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
39475 added = utilArrayDifference(entity.nodes, oldentity.nodes);
39476 } else if (oldentity) {
39477 removed = oldentity.nodes;
39479 } else if (entity) {
39481 added = entity.nodes;
39484 for (i = 0; i < removed.length; i++) {
39485 // make a copy of prototype property, store as own property, and update..
39486 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
39487 parentWays[removed[i]]["delete"](oldentity.id);
39490 for (i = 0; i < added.length; i++) {
39491 // make a copy of prototype property, store as own property, and update..
39492 parentWays[added[i]] = new Set(parentWays[added[i]]);
39493 parentWays[added[i]].add(entity.id);
39495 } else if (type === 'relation') {
39496 // Update parentRels
39497 // diff only on the IDs since the same entity can be a member multiple times with different roles
39498 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
39501 var entityMemberIDs = entity ? entity.members.map(function (m) {
39505 if (oldentity && entity) {
39506 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
39507 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
39508 } else if (oldentity) {
39509 removed = oldentityMemberIDs;
39511 } else if (entity) {
39513 added = entityMemberIDs;
39516 for (i = 0; i < removed.length; i++) {
39517 // make a copy of prototype property, store as own property, and update..
39518 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
39519 parentRels[removed[i]]["delete"](oldentity.id);
39522 for (i = 0; i < added.length; i++) {
39523 // make a copy of prototype property, store as own property, and update..
39524 parentRels[added[i]] = new Set(parentRels[added[i]]);
39525 parentRels[added[i]].add(entity.id);
39529 replace: function replace(entity) {
39530 if (this.entities[entity.id] === entity) return this;
39531 return this.update(function () {
39532 this._updateCalculated(this.entities[entity.id], entity);
39534 this.entities[entity.id] = entity;
39537 remove: function remove(entity) {
39538 return this.update(function () {
39539 this._updateCalculated(entity, undefined);
39541 this.entities[entity.id] = undefined;
39544 revert: function revert(id) {
39545 var baseEntity = this.base().entities[id];
39546 var headEntity = this.entities[id];
39547 if (headEntity === baseEntity) return this;
39548 return this.update(function () {
39549 this._updateCalculated(headEntity, baseEntity);
39551 delete this.entities[id];
39554 update: function update() {
39555 var graph = this.frozen ? coreGraph(this, true) : this;
39557 for (var i = 0; i < arguments.length; i++) {
39558 arguments[i].call(graph, graph);
39561 if (this.frozen) graph.frozen = true;
39564 // Obliterates any existing entities
39565 load: function load(entities) {
39566 var base = this.base();
39567 this.entities = Object.create(base.entities);
39569 for (var i in entities) {
39570 this.entities[i] = entities[i];
39572 this._updateCalculated(base.entities[i], this.entities[i]);
39579 function osmTurn(turn) {
39580 if (!(this instanceof osmTurn)) {
39581 return new osmTurn(turn);
39584 Object.assign(this, turn);
39586 function osmIntersection(graph, startVertexId, maxDistance) {
39587 maxDistance = maxDistance || 30; // in meters
39589 var vgraph = coreGraph(); // virtual graph
39593 function memberOfRestriction(entity) {
39594 return graph.parentRelations(entity).some(function (r) {
39595 return r.isRestriction();
39599 function isRoad(way) {
39600 if (way.isArea() || way.isDegenerate()) return false;
39603 'motorway_link': true,
39605 'trunk_link': true,
39607 'primary_link': true,
39609 'secondary_link': true,
39611 'tertiary_link': true,
39612 'residential': true,
39613 'unclassified': true,
39614 'living_street': true,
39619 return roads[way.tags.highway];
39622 var startNode = graph.entity(startVertexId);
39623 var checkVertices = [startNode];
39626 var vertexIds = [];
39634 var parent; // `actions` will store whatever actions must be performed to satisfy
39635 // preconditions for adding a turn restriction to this intersection.
39636 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
39637 // - Reverse oneways so that they are drawn in the forward direction
39638 // - Split ways on key vertices
39640 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
39641 // for more key vertices and ways to include in the intersection..
39643 while (checkVertices.length) {
39644 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
39646 checkWays = graph.parentWays(vertex);
39647 var hasWays = false;
39649 for (i = 0; i < checkWays.length; i++) {
39650 way = checkWays[i];
39651 if (!isRoad(way) && !memberOfRestriction(way)) continue;
39652 ways.push(way); // it's a road, or it's already in a turn restriction
39654 hasWays = true; // check the way's children for more key vertices
39656 nodes = utilArrayUniq(graph.childNodes(way));
39658 for (j = 0; j < nodes.length; j++) {
39660 if (node === vertex) continue; // same thing
39662 if (vertices.indexOf(node) !== -1) continue; // seen it already
39664 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
39665 // a key vertex will have parents that are also roads
39667 var hasParents = false;
39668 parents = graph.parentWays(node);
39670 for (k = 0; k < parents.length; k++) {
39671 parent = parents[k];
39672 if (parent === way) continue; // same thing
39674 if (ways.indexOf(parent) !== -1) continue; // seen it already
39676 if (!isRoad(parent)) continue; // not a road
39683 checkVertices.push(node);
39689 vertices.push(vertex);
39693 vertices = utilArrayUniq(vertices);
39694 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
39695 // Everything done after this step should act on the virtual graph
39696 // Any actions that must be performed later to the main graph go in `actions` array
39698 ways.forEach(function (way) {
39699 graph.childNodes(way).forEach(function (node) {
39700 vgraph = vgraph.replace(node);
39702 vgraph = vgraph.replace(way);
39703 graph.parentRelations(way).forEach(function (relation) {
39704 if (relation.isRestriction()) {
39705 if (relation.isValidRestriction(graph)) {
39706 vgraph = vgraph.replace(relation);
39707 } else if (relation.isComplete(graph)) {
39708 actions.push(actionDeleteRelation(relation.id));
39712 }); // STEP 3: Force all oneways to be drawn in the forward direction
39714 ways.forEach(function (w) {
39715 var way = vgraph.entity(w.id);
39717 if (way.tags.oneway === '-1') {
39718 var action = actionReverse(way.id, {
39719 reverseOneway: true
39721 actions.push(action);
39722 vgraph = action(vgraph);
39724 }); // STEP 4: Split ways on key vertices
39726 var origCount = osmEntity.id.next.way;
39727 vertices.forEach(function (v) {
39728 // This is an odd way to do it, but we need to find all the ways that
39729 // will be split here, then split them one at a time to ensure that these
39730 // actions can be replayed on the main graph exactly in the same order.
39731 // (It is unintuitive, but the order of ways returned from graph.parentWays()
39732 // is arbitrary, depending on how the main graph and vgraph were built)
39733 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
39735 if (!splitAll.disabled(vgraph)) {
39736 splitAll.ways(vgraph).forEach(function (way) {
39737 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
39738 actions.push(splitOne);
39739 vgraph = splitOne(vgraph);
39742 }); // In here is where we should also split the intersection at nearby junction.
39743 // for https://github.com/mapbox/iD-internal/issues/31
39744 // nearbyVertices.forEach(function(v) {
39746 // Reasons why we reset the way id count here:
39747 // 1. Continuity with way ids created by the splits so that we can replay
39748 // these actions later if the user decides to create a turn restriction
39749 // 2. Avoids churning way ids just by hovering over a vertex
39750 // and displaying the turn restriction editor
39752 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
39754 vertexIds = vertices.map(function (v) {
39759 vertexIds.forEach(function (id) {
39760 var vertex = vgraph.entity(id);
39761 var parents = vgraph.parentWays(vertex);
39762 vertices.push(vertex);
39763 ways = ways.concat(parents);
39765 vertices = utilArrayUniq(vertices);
39766 ways = utilArrayUniq(ways);
39767 vertexIds = vertices.map(function (v) {
39770 wayIds = ways.map(function (w) {
39772 }); // STEP 6: Update the ways with some metadata that will be useful for
39773 // walking the intersection graph later and rendering turn arrows.
39775 function withMetadata(way, vertexIds) {
39776 var __oneWay = way.isOneWay(); // which affixes are key vertices?
39779 var __first = vertexIds.indexOf(way.first()) !== -1;
39781 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
39784 var __via = __first && __last;
39786 var __from = __first && !__oneWay || __last;
39788 var __to = __first || __last && !__oneWay;
39790 return way.update({
39801 wayIds.forEach(function (id) {
39802 var way = withMetadata(vgraph.entity(id), vertexIds);
39803 vgraph = vgraph.replace(way);
39805 }); // STEP 7: Simplify - This is an iterative process where we:
39806 // 1. Find trivial vertices with only 2 parents
39807 // 2. trim off the leaf way from those vertices and remove from vgraph
39810 var removeWayIds = [];
39811 var removeVertexIds = [];
39815 checkVertices = vertexIds.slice();
39817 for (i = 0; i < checkVertices.length; i++) {
39818 var vertexId = checkVertices[i];
39819 vertex = vgraph.hasEntity(vertexId);
39822 if (vertexIds.indexOf(vertexId) !== -1) {
39823 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39826 removeVertexIds.push(vertexId);
39830 parents = vgraph.parentWays(vertex);
39832 if (parents.length < 3) {
39833 if (vertexIds.indexOf(vertexId) !== -1) {
39834 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39838 if (parents.length === 2) {
39839 // vertex with 2 parents is trivial
39840 var a = parents[0];
39841 var b = parents[1];
39842 var aIsLeaf = a && !a.__via;
39843 var bIsLeaf = b && !b.__via;
39844 var leaf, survivor;
39846 if (aIsLeaf && !bIsLeaf) {
39849 } else if (!aIsLeaf && bIsLeaf) {
39854 if (leaf && survivor) {
39855 survivor = withMetadata(survivor, vertexIds); // update survivor way
39857 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
39859 removeWayIds.push(leaf.id);
39864 parents = vgraph.parentWays(vertex);
39866 if (parents.length < 2) {
39867 // vertex is no longer a key vertex
39868 if (vertexIds.indexOf(vertexId) !== -1) {
39869 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
39872 removeVertexIds.push(vertexId);
39876 if (parents.length < 1) {
39877 // vertex is no longer attached to anything
39878 vgraph = vgraph.remove(vertex);
39881 } while (keepGoing);
39883 vertices = vertices.filter(function (vertex) {
39884 return removeVertexIds.indexOf(vertex.id) === -1;
39885 }).map(function (vertex) {
39886 return vgraph.entity(vertex.id);
39888 ways = ways.filter(function (way) {
39889 return removeWayIds.indexOf(way.id) === -1;
39890 }).map(function (way) {
39891 return vgraph.entity(way.id);
39892 }); // OK! Here is our intersection..
39894 var intersection = {
39897 vertices: vertices,
39899 }; // Get all the valid turns through this intersection given a starting way id.
39900 // This operates on the virtual graph for everything.
39902 // Basically, walk through all possible paths from starting way,
39903 // honoring the existing turn restrictions as we go (watch out for loops!)
39905 // For each path found, generate and return a `osmTurn` datastructure.
39908 intersection.turns = function (fromWayId, maxViaWay) {
39909 if (!fromWayId) return [];
39910 if (!maxViaWay) maxViaWay = 0;
39911 var vgraph = intersection.graph;
39912 var keyVertexIds = intersection.vertices.map(function (v) {
39915 var start = vgraph.entity(fromWayId);
39916 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
39917 // maxViaWay=1 from-*-via-*-to (1 via max)
39918 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
39920 var maxPathLength = maxViaWay * 2 + 3;
39923 return turns; // traverse the intersection graph and find all the valid paths
39925 function step(entity, currPath, currRestrictions, matchedRestriction) {
39926 currPath = (currPath || []).slice(); // shallow copy
39928 if (currPath.length >= maxPathLength) return;
39929 currPath.push(entity.id);
39930 currRestrictions = (currRestrictions || []).slice(); // shallow copy
39934 if (entity.type === 'node') {
39935 var parents = vgraph.parentWays(entity);
39936 var nextWays = []; // which ways can we step into?
39938 for (i = 0; i < parents.length; i++) {
39939 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
39941 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
39943 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
39945 var restrict = null;
39947 for (j = 0; j < currRestrictions.length; j++) {
39948 var restriction = currRestrictions[j];
39949 var f = restriction.memberByRole('from');
39950 var v = restriction.membersByRole('via');
39951 var t = restriction.memberByRole('to');
39952 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
39954 var matchesFrom = f.id === fromWayId;
39955 var matchesViaTo = false;
39956 var isAlongOnlyPath = false;
39958 if (t.id === way.id) {
39960 if (v.length === 1 && v[0].type === 'node') {
39962 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
39964 // match all VIA ways
39967 for (k = 2; k < currPath.length; k += 2) {
39968 // k = 2 skips FROM
39969 pathVias.push(currPath[k]); // (path goes way-node-way...)
39972 var restrictionVias = [];
39974 for (k = 0; k < v.length; k++) {
39975 if (v[k].type === 'way') {
39976 restrictionVias.push(v[k].id);
39980 var diff = utilArrayDifference(pathVias, restrictionVias);
39981 matchesViaTo = !diff.length;
39983 } else if (isOnly) {
39984 for (k = 0; k < v.length; k++) {
39985 // way doesn't match TO, but is one of the via ways along the path of an "only"
39986 if (v[k].type === 'way' && v[k].id === way.id) {
39987 isAlongOnlyPath = true;
39993 if (matchesViaTo) {
39996 id: restriction.id,
39997 direct: matchesFrom,
40004 id: restriction.id,
40005 direct: matchesFrom,
40012 // indirect - caused by a different nearby restriction
40013 if (isAlongOnlyPath) {
40015 id: restriction.id,
40021 } else if (isOnly) {
40023 id: restriction.id,
40030 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
40033 if (restrict && restrict.direct) break;
40042 nextWays.forEach(function (nextWay) {
40043 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
40046 // entity.type === 'way'
40047 if (currPath.length >= 3) {
40048 // this is a "complete" path..
40049 var turnPath = currPath.slice(); // shallow copy
40050 // an indirect restriction - only include the partial path (starting at FROM)
40052 if (matchedRestriction && matchedRestriction.direct === false) {
40053 for (i = 0; i < turnPath.length; i++) {
40054 if (turnPath[i] === matchedRestriction.from) {
40055 turnPath = turnPath.slice(i);
40061 var turn = pathToTurn(turnPath);
40064 if (matchedRestriction) {
40065 turn.restrictionID = matchedRestriction.id;
40066 turn.no = matchedRestriction.no;
40067 turn.only = matchedRestriction.only;
40068 turn.direct = matchedRestriction.direct;
40071 turns.push(osmTurn(turn));
40074 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
40077 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
40078 // which nodes can we step into?
40080 var n1 = vgraph.entity(entity.first());
40081 var n2 = vgraph.entity(entity.last());
40082 var dist = geoSphericalDistance(n1.loc, n2.loc);
40083 var nextNodes = [];
40085 if (currPath.length > 1) {
40086 if (dist > maxDistance) return; // the next node is too far
40088 if (!entity.__via) return; // this way is a leaf / can't be a via
40091 if (!entity.__oneWay && // bidirectional..
40092 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
40093 currPath.indexOf(n1.id) === -1) {
40094 // haven't seen it yet..
40095 nextNodes.push(n1); // can advance to first node
40098 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
40099 currPath.indexOf(n2.id) === -1) {
40100 // haven't seen it yet..
40101 nextNodes.push(n2); // can advance to last node
40104 nextNodes.forEach(function (nextNode) {
40105 // gather restrictions FROM this way
40106 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
40107 if (!r.isRestriction()) return false;
40108 var f = r.memberByRole('from');
40109 if (!f || f.id !== entity.id) return false;
40110 var isOnly = /^only_/.test(r.tags.restriction);
40111 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
40113 var isOnlyVia = false;
40114 var v = r.membersByRole('via');
40116 if (v.length === 1 && v[0].type === 'node') {
40118 isOnlyVia = v[0].id === nextNode.id;
40121 for (var i = 0; i < v.length; i++) {
40122 if (v[i].type !== 'way') continue;
40123 var viaWay = vgraph.entity(v[i].id);
40125 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
40134 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
40137 } // assumes path is alternating way-node-way of odd length
40140 function pathToTurn(path) {
40141 if (path.length < 3) return;
40142 var fromWayId, fromNodeId, fromVertexId;
40143 var toWayId, toNodeId, toVertexId;
40144 var viaWayIds, viaNodeId, isUturn;
40145 fromWayId = path[0];
40146 toWayId = path[path.length - 1];
40148 if (path.length === 3 && fromWayId === toWayId) {
40150 var way = vgraph.entity(fromWayId);
40151 if (way.__oneWay) return null;
40153 viaNodeId = fromVertexId = toVertexId = path[1];
40154 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
40157 fromVertexId = path[1];
40158 fromNodeId = adjacentNode(fromWayId, fromVertexId);
40159 toVertexId = path[path.length - 2];
40160 toNodeId = adjacentNode(toWayId, toVertexId);
40162 if (path.length === 3) {
40163 viaNodeId = path[1];
40165 viaWayIds = path.filter(function (entityId) {
40166 return entityId[0] === 'w';
40168 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
40173 key: path.join('_'),
40178 vertex: fromVertexId
40192 function adjacentNode(wayId, affixId) {
40193 var nodes = vgraph.entity(wayId).nodes;
40194 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
40199 return intersection;
40201 function osmInferRestriction(graph, turn, projection) {
40202 var fromWay = graph.entity(turn.from.way);
40203 var fromNode = graph.entity(turn.from.node);
40204 var fromVertex = graph.entity(turn.from.vertex);
40205 var toWay = graph.entity(turn.to.way);
40206 var toNode = graph.entity(turn.to.node);
40207 var toVertex = graph.entity(turn.to.vertex);
40208 var fromOneWay = fromWay.tags.oneway === 'yes';
40209 var toOneWay = toWay.tags.oneway === 'yes';
40210 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
40212 while (angle < 0) {
40216 if (fromNode === toNode) {
40217 return 'no_u_turn';
40220 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) {
40221 return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
40224 if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) {
40225 return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to)
40229 return 'no_right_turn';
40233 return 'no_left_turn';
40236 return 'no_straight_on';
40239 function actionMergePolygon(ids, newRelationId) {
40240 function groupEntities(graph) {
40241 var entities = ids.map(function (id) {
40242 return graph.entity(id);
40244 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
40245 if (entity.type === 'way' && entity.isClosed()) {
40246 return 'closedWay';
40247 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
40248 return 'multipolygon';
40253 return Object.assign({
40257 }, geometryGroups);
40260 var action = function action(graph) {
40261 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
40263 // Each element is itself an array of objects with an id property, and has a
40264 // locs property which is an array of the locations forming the polygon.
40266 var polygons = entities.multipolygon.reduce(function (polygons, m) {
40267 return polygons.concat(osmJoinWays(m.members, graph));
40268 }, []).concat(entities.closedWay.map(function (d) {
40272 member.nodes = graph.childNodes(d);
40274 })); // contained is an array of arrays of boolean values,
40275 // where contained[j][k] is true iff the jth way is
40276 // contained by the kth way.
40278 var contained = polygons.map(function (w, i) {
40279 return polygons.map(function (d, n) {
40280 if (i === n) return null;
40281 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
40283 }), w.nodes.map(function (n) {
40287 }); // Sort all polygons as either outer or inner ways
40292 while (polygons.length) {
40293 extractUncontained(polygons);
40294 polygons = polygons.filter(isContained);
40295 contained = contained.filter(isContained).map(filterContained);
40298 function isContained(d, i) {
40299 return contained[i].some(function (val) {
40304 function filterContained(d) {
40305 return d.filter(isContained);
40308 function extractUncontained(polygons) {
40309 polygons.forEach(function (d, i) {
40310 if (!isContained(d, i)) {
40311 d.forEach(function (member) {
40315 role: outer ? 'outer' : 'inner'
40321 } // Move all tags to one relation
40324 var relation = entities.multipolygon[0] || osmRelation({
40327 type: 'multipolygon'
40330 entities.multipolygon.slice(1).forEach(function (m) {
40331 relation = relation.mergeTags(m.tags);
40332 graph = graph.remove(m);
40334 entities.closedWay.forEach(function (way) {
40335 function isThisOuter(m) {
40336 return m.id === way.id && m.role !== 'inner';
40339 if (members.some(isThisOuter)) {
40340 relation = relation.mergeTags(way.tags);
40341 graph = graph.replace(way.update({
40346 return graph.replace(relation.update({
40348 tags: utilObjectOmit(relation.tags, ['area'])
40352 action.disabled = function (graph) {
40353 var entities = groupEntities(graph);
40355 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
40356 return 'not_eligible';
40359 if (!entities.multipolygon.every(function (r) {
40360 return r.isComplete(graph);
40362 return 'incomplete_relation';
40365 if (!entities.multipolygon.length) {
40366 var sharedMultipolygons = [];
40367 entities.closedWay.forEach(function (way, i) {
40369 sharedMultipolygons = graph.parentMultipolygons(way);
40371 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
40374 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
40375 return relation.members.length === entities.closedWay.length;
40378 if (sharedMultipolygons.length) {
40379 // don't create a new multipolygon if it'd be redundant
40380 return 'not_eligible';
40382 } else if (entities.closedWay.some(function (way) {
40383 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
40385 // don't add a way to a multipolygon again if it's already a member
40386 return 'not_eligible';
40393 var FORCED$2 = descriptors && fails(function () {
40394 // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
40395 return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy';
40398 // `RegExp.prototype.flags` getter
40399 // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
40400 if (FORCED$2) objectDefineProperty.f(RegExp.prototype, 'flags', {
40401 configurable: true,
40405 var fastDeepEqual = function equal(a, b) {
40406 if (a === b) return true;
40408 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
40409 if (a.constructor !== b.constructor) return false;
40410 var length, i, keys;
40412 if (Array.isArray(a)) {
40414 if (length != b.length) return false;
40416 for (i = length; i-- !== 0;) {
40417 if (!equal(a[i], b[i])) return false;
40423 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
40424 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
40425 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
40426 keys = Object.keys(a);
40427 length = keys.length;
40428 if (length !== Object.keys(b).length) return false;
40430 for (i = length; i-- !== 0;) {
40431 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
40434 for (i = length; i-- !== 0;) {
40436 if (!equal(a[key], b[key])) return false;
40440 } // true if both NaN, false otherwise
40443 return a !== a && b !== b;
40446 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
40447 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
40448 // http://www.cs.dartmouth.edu/~doug/
40449 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
40451 // Expects two arrays, finds longest common sequence
40453 function LCS(buffer1, buffer2) {
40454 var equivalenceClasses = {};
40456 for (var j = 0; j < buffer2.length; j++) {
40457 var item = buffer2[j];
40459 if (equivalenceClasses[item]) {
40460 equivalenceClasses[item].push(j);
40462 equivalenceClasses[item] = [j];
40471 var candidates = [NULLRESULT];
40473 for (var i = 0; i < buffer1.length; i++) {
40474 var _item = buffer1[i];
40475 var buffer2indices = equivalenceClasses[_item] || [];
40477 var c = candidates[0];
40479 for (var jx = 0; jx < buffer2indices.length; jx++) {
40480 var _j = buffer2indices[jx];
40483 for (s = r; s < candidates.length; s++) {
40484 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
40489 if (s < candidates.length) {
40490 var newCandidate = {
40493 chain: candidates[s]
40496 if (r === candidates.length) {
40497 candidates.push(c);
40505 if (r === candidates.length) {
40506 break; // no point in examining further (j)s
40512 } // At this point, we know the LCS: it's in the reverse of the
40513 // linked-list through .chain of candidates[candidates.length - 1].
40516 return candidates[candidates.length - 1];
40517 } // We apply the LCS to build a 'comm'-style picture of the
40518 // offsets and lengths of mismatched chunks in the input
40519 // buffers. This is used by diff3MergeRegions.
40522 function diffIndices(buffer1, buffer2) {
40523 var lcs = LCS(buffer1, buffer2);
40525 var tail1 = buffer1.length;
40526 var tail2 = buffer2.length;
40528 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
40529 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
40530 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
40531 tail1 = candidate.buffer1index;
40532 tail2 = candidate.buffer2index;
40534 if (mismatchLength1 || mismatchLength2) {
40536 buffer1: [tail1 + 1, mismatchLength1],
40537 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
40538 buffer2: [tail2 + 1, mismatchLength2],
40539 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
40546 } // We apply the LCS to build a JSON representation of a
40547 // independently derived from O, returns a fairly complicated
40548 // internal representation of merge decisions it's taken. The
40549 // interested reader may wish to consult
40551 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
40552 // 'A Formal Investigation of ' In Arvind and Prasad,
40553 // editors, Foundations of Software Technology and Theoretical
40554 // Computer Science (FSTTCS), December 2007.
40556 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
40560 function diff3MergeRegions(a, o, b) {
40561 // "hunks" are array subsets where `a` or `b` are different from `o`
40562 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
40565 function addHunk(h, ab) {
40568 oStart: h.buffer1[0],
40569 oLength: h.buffer1[1],
40570 // length of o to remove
40571 abStart: h.buffer2[0],
40572 abLength: h.buffer2[1] // length of a/b to insert
40573 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
40578 diffIndices(o, a).forEach(function (item) {
40579 return addHunk(item, 'a');
40581 diffIndices(o, b).forEach(function (item) {
40582 return addHunk(item, 'b');
40584 hunks.sort(function (x, y) {
40585 return x.oStart - y.oStart;
40588 var currOffset = 0;
40590 function advanceTo(endOffset) {
40591 if (endOffset > currOffset) {
40595 bufferStart: currOffset,
40596 bufferLength: endOffset - currOffset,
40597 bufferContent: o.slice(currOffset, endOffset)
40599 currOffset = endOffset;
40603 while (hunks.length) {
40604 var hunk = hunks.shift();
40605 var regionStart = hunk.oStart;
40606 var regionEnd = hunk.oStart + hunk.oLength;
40607 var regionHunks = [hunk];
40608 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
40610 while (hunks.length) {
40611 var nextHunk = hunks[0];
40612 var nextHunkStart = nextHunk.oStart;
40613 if (nextHunkStart > regionEnd) break; // no overlap
40615 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
40616 regionHunks.push(hunks.shift());
40619 if (regionHunks.length === 1) {
40620 // Only one hunk touches this region, meaning that there is no conflict here.
40621 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
40622 if (hunk.abLength > 0) {
40623 var buffer = hunk.ab === 'a' ? a : b;
40627 bufferStart: hunk.abStart,
40628 bufferLength: hunk.abLength,
40629 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
40633 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
40634 // Effectively merge all the `a` hunks into one giant hunk, then do the
40635 // same for the `b` hunks; then, correct for skew in the regions of `o`
40636 // that each side changed, and report appropriate spans for the three sides.
40638 a: [a.length, -1, o.length, -1],
40639 b: [b.length, -1, o.length, -1]
40642 while (regionHunks.length) {
40643 hunk = regionHunks.shift();
40644 var oStart = hunk.oStart;
40645 var oEnd = oStart + hunk.oLength;
40646 var abStart = hunk.abStart;
40647 var abEnd = abStart + hunk.abLength;
40648 var _b = bounds[hunk.ab];
40649 _b[0] = Math.min(abStart, _b[0]);
40650 _b[1] = Math.max(abEnd, _b[1]);
40651 _b[2] = Math.min(oStart, _b[2]);
40652 _b[3] = Math.max(oEnd, _b[3]);
40655 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
40656 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
40657 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
40658 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
40662 aLength: aEnd - aStart,
40663 aContent: a.slice(aStart, aEnd),
40664 oStart: regionStart,
40665 oLength: regionEnd - regionStart,
40666 oContent: o.slice(regionStart, regionEnd),
40668 bLength: bEnd - bStart,
40669 bContent: b.slice(bStart, bEnd)
40671 results.push(result);
40674 currOffset = regionEnd;
40677 advanceTo(o.length);
40679 } // Applies the output of diff3MergeRegions to actually
40680 // construct the merged buffer; the returned result alternates
40681 // between 'ok' and 'conflict' blocks.
40682 // A "false conflict" is where `a` and `b` both change the same from `o`
40685 function diff3Merge(a, o, b, options) {
40687 excludeFalseConflicts: true,
40688 stringSeparator: /\s+/
40690 options = Object.assign(defaults, options);
40691 var aString = typeof a === 'string';
40692 var oString = typeof o === 'string';
40693 var bString = typeof b === 'string';
40694 if (aString) a = a.split(options.stringSeparator);
40695 if (oString) o = o.split(options.stringSeparator);
40696 if (bString) b = b.split(options.stringSeparator);
40698 var regions = diff3MergeRegions(a, o, b);
40701 function flushOk() {
40702 if (okBuffer.length) {
40711 function isFalseConflict(a, b) {
40712 if (a.length !== b.length) return false;
40714 for (var i = 0; i < a.length; i++) {
40715 if (a[i] !== b[i]) return false;
40721 regions.forEach(function (region) {
40722 if (region.stable) {
40725 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
40727 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
40730 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
40735 a: region.aContent,
40736 aIndex: region.aStart,
40737 o: region.oContent,
40738 oIndex: region.oStart,
40739 b: region.bContent,
40740 bIndex: region.bStart
40750 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
40751 discardTags = discardTags || {};
40752 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
40754 var _conflicts = [];
40757 return typeof formatUser === 'function' ? formatUser(d) : d;
40760 function mergeLocation(remote, target) {
40761 function pointEqual(a, b) {
40762 var epsilon = 1e-6;
40763 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
40766 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
40770 if (_option === 'force_remote') {
40771 return target.update({
40776 _conflicts.push(_t('merge_remote_changes.conflict.location', {
40777 user: user(remote.user)
40783 function mergeNodes(base, remote, target) {
40784 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
40788 if (_option === 'force_remote') {
40789 return target.update({
40790 nodes: remote.nodes
40794 var ccount = _conflicts.length;
40795 var o = base.nodes || [];
40796 var a = target.nodes || [];
40797 var b = remote.nodes || [];
40799 var hunks = diff3Merge(a, o, b, {
40800 excludeFalseConflicts: true
40803 for (var i = 0; i < hunks.length; i++) {
40804 var hunk = hunks[i];
40807 nodes.push.apply(nodes, hunk.ok);
40809 // for all conflicts, we can assume c.a !== c.b
40810 // because `diff3Merge` called with `true` option to exclude false conflicts..
40811 var c = hunk.conflict;
40813 if (fastDeepEqual(c.o, c.a)) {
40814 // only changed remotely
40815 nodes.push.apply(nodes, c.b);
40816 } else if (fastDeepEqual(c.o, c.b)) {
40817 // only changed locally
40818 nodes.push.apply(nodes, c.a);
40820 // changed both locally and remotely
40821 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
40822 user: user(remote.user)
40830 return _conflicts.length === ccount ? target.update({
40835 function mergeChildren(targetWay, children, updates, graph) {
40836 function isUsed(node, targetWay) {
40837 var hasInterestingParent = graph.parentWays(node).some(function (way) {
40838 return way.id !== targetWay.id;
40840 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
40843 var ccount = _conflicts.length;
40845 for (var i = 0; i < children.length; i++) {
40846 var id = children[i];
40847 var node = graph.hasEntity(id); // remove unused childNodes..
40849 if (targetWay.nodes.indexOf(id) === -1) {
40850 if (node && !isUsed(node, targetWay)) {
40851 updates.removeIds.push(id);
40855 } // restore used childNodes..
40858 var local = localGraph.hasEntity(id);
40859 var remote = remoteGraph.hasEntity(id);
40862 if (_option === 'force_remote' && remote && remote.visible) {
40863 updates.replacements.push(remote);
40864 } else if (_option === 'force_local' && local) {
40865 target = osmEntity(local);
40868 target = target.update({
40869 version: remote.version
40873 updates.replacements.push(target);
40874 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
40875 target = osmEntity(local, {
40876 version: remote.version
40879 if (remote.visible) {
40880 target = mergeLocation(remote, target);
40882 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
40883 user: user(remote.user)
40887 if (_conflicts.length !== ccount) break;
40888 updates.replacements.push(target);
40895 function updateChildren(updates, graph) {
40896 for (var i = 0; i < updates.replacements.length; i++) {
40897 graph = graph.replace(updates.replacements[i]);
40900 if (updates.removeIds.length) {
40901 graph = actionDeleteMultiple(updates.removeIds)(graph);
40907 function mergeMembers(remote, target) {
40908 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
40912 if (_option === 'force_remote') {
40913 return target.update({
40914 members: remote.members
40918 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
40919 user: user(remote.user)
40925 function mergeTags(base, remote, target) {
40926 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
40930 if (_option === 'force_remote') {
40931 return target.update({
40936 var ccount = _conflicts.length;
40937 var o = base.tags || {};
40938 var a = target.tags || {};
40939 var b = remote.tags || {};
40940 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
40941 return !discardTags[k];
40943 var tags = Object.assign({}, a); // shallow copy
40945 var changed = false;
40947 for (var i = 0; i < keys.length; i++) {
40950 if (o[k] !== b[k] && a[k] !== b[k]) {
40951 // changed remotely..
40952 if (o[k] !== a[k]) {
40953 // changed locally..
40954 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
40958 user: user(remote.user)
40961 // unchanged locally, accept remote change..
40962 if (b.hasOwnProperty(k)) {
40973 return changed && _conflicts.length === ccount ? target.update({
40976 } // `graph.base()` is the common ancestor of the two graphs.
40977 // `localGraph` contains user's edits up to saving
40978 // `remoteGraph` contains remote edits to modified nodes
40979 // `graph` must be a descendent of `localGraph` and may include
40980 // some conflict resolution actions performed on it.
40982 // --- ... --- `localGraph` -- ... -- `graph`
40984 // `graph.base()` --- ... --- `remoteGraph`
40988 var action = function action(graph) {
40993 var base = graph.base().entities[id];
40994 var local = localGraph.entity(id);
40995 var remote = remoteGraph.entity(id);
40996 var target = osmEntity(local, {
40997 version: remote.version
40998 }); // delete/undelete
41000 if (!remote.visible) {
41001 if (_option === 'force_remote') {
41002 return actionDeleteMultiple([id])(graph);
41003 } else if (_option === 'force_local') {
41004 if (target.type === 'way') {
41005 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
41006 graph = updateChildren(updates, graph);
41009 return graph.replace(target);
41011 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
41012 user: user(remote.user)
41015 return graph; // do nothing
41020 if (target.type === 'node') {
41021 target = mergeLocation(remote, target);
41022 } else if (target.type === 'way') {
41023 // pull in any child nodes that may not be present locally..
41024 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
41025 target = mergeNodes(base, remote, target);
41026 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
41027 } else if (target.type === 'relation') {
41028 target = mergeMembers(remote, target);
41031 target = mergeTags(base, remote, target);
41033 if (!_conflicts.length) {
41034 graph = updateChildren(updates, graph).replace(target);
41040 action.withOption = function (opt) {
41045 action.conflicts = function () {
41052 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
41054 function actionMove(moveIDs, tryDelta, projection, cache) {
41055 var _delta = tryDelta;
41057 function setupCache(graph) {
41058 function canMove(nodeID) {
41059 // Allow movement of any node that is in the selectedIDs list..
41060 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
41062 var parents = graph.parentWays(graph.entity(nodeID));
41063 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
41065 var parentsMoving = parents.every(function (way) {
41066 return cache.moving[way.id];
41068 if (!parentsMoving) delete cache.moving[nodeID];
41069 return parentsMoving;
41072 function cacheEntities(ids) {
41073 for (var i = 0; i < ids.length; i++) {
41075 if (cache.moving[id]) continue;
41076 cache.moving[id] = true;
41077 var entity = graph.hasEntity(id);
41078 if (!entity) continue;
41080 if (entity.type === 'node') {
41081 cache.nodes.push(id);
41082 cache.startLoc[id] = entity.loc;
41083 } else if (entity.type === 'way') {
41084 cache.ways.push(id);
41085 cacheEntities(entity.nodes);
41087 cacheEntities(entity.members.map(function (member) {
41094 function cacheIntersections(ids) {
41095 function isEndpoint(way, id) {
41096 return !way.isClosed() && !!way.affix(id);
41099 for (var i = 0; i < ids.length; i++) {
41100 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
41102 var childNodes = graph.childNodes(graph.entity(id));
41104 for (var j = 0; j < childNodes.length; j++) {
41105 var node = childNodes[j];
41106 var parents = graph.parentWays(node);
41107 if (parents.length !== 2) continue;
41108 var moved = graph.entity(id);
41109 var unmoved = null;
41111 for (var k = 0; k < parents.length; k++) {
41112 var way = parents[k];
41114 if (!cache.moving[way.id]) {
41120 if (!unmoved) continue; // exclude ways that are overly connected..
41122 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
41123 if (moved.isArea() || unmoved.isArea()) continue;
41124 cache.intersections.push({
41127 unmovedId: unmoved.id,
41128 movedIsEP: isEndpoint(moved, node.id),
41129 unmovedIsEP: isEndpoint(unmoved, node.id)
41141 cache.intersections = [];
41142 cache.replacedVertex = {};
41143 cache.startLoc = {};
41146 cacheEntities(moveIDs);
41147 cacheIntersections(cache.ways);
41148 cache.nodes = cache.nodes.filter(canMove);
41151 } // Place a vertex where the moved vertex used to be, to preserve way shape..
41160 // * node '*' added to preserve shape
41162 // / b ---- e way `b,e` moved here:
41169 function replaceMovedVertex(nodeId, wayId, graph, delta) {
41170 var way = graph.entity(wayId);
41171 var moved = graph.entity(nodeId);
41172 var movedIndex = way.nodes.indexOf(nodeId);
41173 var len, prevIndex, nextIndex;
41175 if (way.isClosed()) {
41176 len = way.nodes.length - 1;
41177 prevIndex = (movedIndex + len - 1) % len;
41178 nextIndex = (movedIndex + len + 1) % len;
41180 len = way.nodes.length;
41181 prevIndex = movedIndex - 1;
41182 nextIndex = movedIndex + 1;
41185 var prev = graph.hasEntity(way.nodes[prevIndex]);
41186 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
41188 if (!prev || !next) return graph;
41189 var key = wayId + '_' + nodeId;
41190 var orig = cache.replacedVertex[key];
41194 cache.replacedVertex[key] = orig;
41195 cache.startLoc[orig.id] = cache.startLoc[nodeId];
41201 start = projection(cache.startLoc[nodeId]);
41202 end = projection.invert(geoVecAdd(start, delta));
41204 end = cache.startLoc[nodeId];
41207 orig = orig.move(end);
41208 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..
41210 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
41212 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
41213 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
41214 var d1 = geoPathLength(p1);
41215 var d2 = geoPathLength(p2);
41216 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
41218 if (way.isClosed() && insertAt === 0) insertAt = len;
41219 way = way.addNode(orig.id, insertAt);
41220 return graph.replace(orig).replace(way);
41221 } // Remove duplicate vertex that might have been added by
41222 // replaceMovedVertex. This is done after the unzorro checks.
41225 function removeDuplicateVertices(wayId, graph) {
41226 var way = graph.entity(wayId);
41227 var epsilon = 1e-6;
41230 function isInteresting(node, graph) {
41231 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41234 for (var i = 0; i < way.nodes.length; i++) {
41235 curr = graph.entity(way.nodes[i]);
41237 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
41238 if (!isInteresting(prev, graph)) {
41239 way = way.removeNode(prev.id);
41240 graph = graph.replace(way).remove(prev);
41241 } else if (!isInteresting(curr, graph)) {
41242 way = way.removeNode(curr.id);
41243 graph = graph.replace(way).remove(curr);
41251 } // Reorder nodes around intersections that have moved..
41253 // Start: way1.nodes: b,e (moving)
41254 // a - b - c ----- d way2.nodes: a,b,c,d (static)
41256 // e isEP1: true, isEP2, false
41258 // way1 `b,e` moved here:
41259 // a ----- c = b - d
41263 // reorder nodes way1.nodes: b,e
41264 // a ----- c - b - d way2.nodes: a,c,b,d
41270 function unZorroIntersection(intersection, graph) {
41271 var vertex = graph.entity(intersection.nodeId);
41272 var way1 = graph.entity(intersection.movedId);
41273 var way2 = graph.entity(intersection.unmovedId);
41274 var isEP1 = intersection.movedIsEP;
41275 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
41277 if (isEP1 && isEP2) return graph;
41278 var nodes1 = graph.childNodes(way1).filter(function (n) {
41279 return n !== vertex;
41281 var nodes2 = graph.childNodes(way2).filter(function (n) {
41282 return n !== vertex;
41284 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
41285 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
41286 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
41287 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
41288 var loc; // snap vertex to nearest edge (or some point between them)..
41290 if (!isEP1 && !isEP2) {
41291 var epsilon = 1e-6,
41294 for (var i = 0; i < maxIter; i++) {
41295 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
41296 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
41297 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
41298 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
41300 } else if (!isEP1) {
41306 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
41308 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
41309 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
41310 graph = graph.replace(way1);
41313 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
41314 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
41315 graph = graph.replace(way2);
41321 function cleanupIntersections(graph) {
41322 for (var i = 0; i < cache.intersections.length; i++) {
41323 var obj = cache.intersections[i];
41324 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
41325 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
41326 graph = unZorroIntersection(obj, graph);
41327 graph = removeDuplicateVertices(obj.movedId, graph);
41328 graph = removeDuplicateVertices(obj.unmovedId, graph);
41332 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
41335 function limitDelta(graph) {
41336 function moveNode(loc) {
41337 return geoVecAdd(projection(loc), _delta);
41340 for (var i = 0; i < cache.intersections.length; i++) {
41341 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
41343 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
41345 if (!obj.movedIsEP) continue;
41346 var node = graph.entity(obj.nodeId);
41347 var start = projection(node.loc);
41348 var end = geoVecAdd(start, _delta);
41349 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
41350 var movedPath = movedNodes.map(function (n) {
41351 return moveNode(n.loc);
41353 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
41354 var unmovedPath = unmovedNodes.map(function (n) {
41355 return projection(n.loc);
41357 var hits = geoPathIntersections(movedPath, unmovedPath);
41359 for (var j = 0; i < hits.length; i++) {
41360 if (geoVecEqual(hits[j], end)) continue;
41361 var edge = geoChooseEdge(unmovedNodes, end, projection);
41362 _delta = geoVecSubtract(projection(edge.loc), start);
41367 var action = function action(graph) {
41368 if (_delta[0] === 0 && _delta[1] === 0) return graph;
41371 if (cache.intersections.length) {
41375 for (var i = 0; i < cache.nodes.length; i++) {
41376 var node = graph.entity(cache.nodes[i]);
41377 var start = projection(node.loc);
41378 var end = geoVecAdd(start, _delta);
41379 graph = graph.replace(node.move(projection.invert(end)));
41382 if (cache.intersections.length) {
41383 graph = cleanupIntersections(graph);
41389 action.delta = function () {
41396 function actionMoveMember(relationId, fromIndex, toIndex) {
41397 return function (graph) {
41398 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
41402 function actionMoveNode(nodeID, toLoc) {
41403 var action = function action(graph, t) {
41404 if (t === null || !isFinite(t)) t = 1;
41405 t = Math.min(Math.max(+t, 0), 1);
41406 var node = graph.entity(nodeID);
41407 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
41410 action.transitionable = true;
41414 function actionNoop() {
41415 return function (graph) {
41420 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
41421 var epsilon = ep || 1e-4;
41422 var threshold = degThresh || 13; // degrees within right or straight to alter
41423 // We test normalized dot products so we can compare as cos(angle)
41425 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
41426 var upperThreshold = Math.cos(threshold * Math.PI / 180);
41428 var action = function action(graph, t) {
41429 if (t === null || !isFinite(t)) t = 1;
41430 t = Math.min(Math.max(+t, 0), 1);
41431 var way = graph.entity(wayID);
41432 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41434 if (way.tags.nonsquare) {
41435 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
41437 delete tags.nonsquare;
41443 graph = graph.replace(way);
41444 var isClosed = way.isClosed();
41445 var nodes = graph.childNodes(way).slice(); // shallow copy
41447 if (isClosed) nodes.pop();
41449 if (vertexID !== undefined) {
41450 nodes = nodeSubset(nodes, vertexID, isClosed);
41451 if (nodes.length !== 3) return graph;
41452 } // note: all geometry functions here use the unclosed node/point/coord list
41455 var nodeCount = {};
41461 var node, point, loc, score, motions, i, j;
41463 for (i = 0; i < nodes.length; i++) {
41465 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
41468 coord: projection(node.loc)
41472 if (points.length === 3) {
41473 // move only one vertex for right triangle
41474 for (i = 0; i < 1000; i++) {
41475 motions = points.map(calcMotion);
41476 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
41477 score = corner.dotp;
41479 if (score < epsilon) {
41484 node = graph.entity(nodes[corner.i].id);
41485 loc = projection.invert(points[corner.i].coord);
41486 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41488 var straights = [];
41489 var simplified = []; // Remove points from nearly straight sections..
41490 // This produces a simplified shape to orthogonalize
41492 for (i = 0; i < points.length; i++) {
41496 if (isClosed || i > 0 && i < points.length - 1) {
41497 var a = points[(i - 1 + points.length) % points.length];
41498 var b = points[(i + 1) % points.length];
41499 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
41502 if (dotp > upperThreshold) {
41503 straights.push(point);
41505 simplified.push(point);
41507 } // Orthogonalize the simplified shape
41510 var bestPoints = clonePoints(simplified);
41511 var originalPoints = clonePoints(simplified);
41514 for (i = 0; i < 1000; i++) {
41515 motions = simplified.map(calcMotion);
41517 for (j = 0; j < motions.length; j++) {
41518 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
41521 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
41523 if (newScore < score) {
41524 bestPoints = clonePoints(simplified);
41528 if (score < epsilon) {
41533 var bestCoords = bestPoints.map(function (p) {
41536 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
41538 for (i = 0; i < bestPoints.length; i++) {
41539 point = bestPoints[i];
41541 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
41542 node = graph.entity(point.id);
41543 loc = projection.invert(point.coord);
41544 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41546 } // move the nodes along straight segments
41549 for (i = 0; i < straights.length; i++) {
41550 point = straights[i];
41551 if (nodeCount[point.id] > 1) continue; // skip self-intersections
41553 node = graph.entity(point.id);
41555 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
41556 // remove uninteresting points..
41557 graph = actionDeleteNode(node.id)(graph);
41559 // move interesting points to the nearest edge..
41560 var choice = geoVecProject(point.coord, bestCoords);
41563 loc = projection.invert(choice.target);
41564 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
41572 function clonePoints(array) {
41573 return array.map(function (p) {
41576 coord: [p.coord[0], p.coord[1]]
41581 function calcMotion(point, i, array) {
41582 // don't try to move the endpoints of a non-closed way.
41583 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)
41585 if (nodeCount[array[i].id] > 1) return [0, 0];
41586 var a = array[(i - 1 + array.length) % array.length].coord;
41587 var origin = point.coord;
41588 var b = array[(i + 1) % array.length].coord;
41589 var p = geoVecSubtract(a, origin);
41590 var q = geoVecSubtract(b, origin);
41591 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
41592 p = geoVecNormalize(p);
41593 q = geoVecNormalize(q);
41594 var dotp = p[0] * q[0] + p[1] * q[1];
41595 var val = Math.abs(dotp);
41597 if (val < lowerThreshold) {
41598 // nearly orthogonal
41601 var vec = geoVecNormalize(geoVecAdd(p, q));
41602 return geoVecScale(vec, 0.1 * dotp * scale);
41605 return [0, 0]; // do nothing
41607 }; // if we are only orthogonalizing one vertex,
41608 // get that vertex and the previous and next
41611 function nodeSubset(nodes, vertexID, isClosed) {
41612 var first = isClosed ? 0 : 1;
41613 var last = isClosed ? nodes.length : nodes.length - 1;
41615 for (var i = first; i < last; i++) {
41616 if (nodes[i].id === vertexID) {
41617 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
41624 action.disabled = function (graph) {
41625 var way = graph.entity(wayID);
41626 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
41628 graph = graph.replace(way);
41629 var isClosed = way.isClosed();
41630 var nodes = graph.childNodes(way).slice(); // shallow copy
41632 if (isClosed) nodes.pop();
41633 var allowStraightAngles = false;
41635 if (vertexID !== undefined) {
41636 allowStraightAngles = true;
41637 nodes = nodeSubset(nodes, vertexID, isClosed);
41638 if (nodes.length !== 3) return 'end_vertex';
41641 var coords = nodes.map(function (n) {
41642 return projection(n.loc);
41644 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
41646 if (score === null) {
41647 return 'not_squarish';
41648 } else if (score === 0) {
41649 return 'square_enough';
41655 action.transitionable = true;
41660 // `turn` must be an `osmTurn` object
41661 // see osm/intersection.js, pathToTurn()
41663 // This specifies a restriction of type `restriction` when traveling from
41664 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
41665 // (The action does not check that these entities form a valid intersection.)
41667 // From, to, and via ways should be split before calling this action.
41668 // (old versions of the code would split the ways here, but we no longer do it)
41670 // For testing convenience, accepts a restrictionID to assign to the new
41671 // relation. Normally, this will be undefined and the relation will
41672 // automatically be assigned a new ID.
41675 function actionRestrictTurn(turn, restrictionType, restrictionID) {
41676 return function (graph) {
41677 var fromWay = graph.entity(turn.from.way);
41678 var toWay = graph.entity(turn.to.way);
41679 var viaNode = turn.via.node && graph.entity(turn.via.node);
41680 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
41681 return graph.entity(id);
41696 } else if (viaWays) {
41697 viaWays.forEach(function (viaWay) {
41711 return graph.replace(osmRelation({
41714 type: 'restriction',
41715 restriction: restrictionType
41722 function actionRevert(id) {
41723 var action = function action(graph) {
41724 var entity = graph.hasEntity(id),
41725 base = graph.base().entities[id];
41727 if (entity && !base) {
41728 // entity will be removed..
41729 if (entity.type === 'node') {
41730 graph.parentWays(entity).forEach(function (parent) {
41731 parent = parent.removeNode(id);
41732 graph = graph.replace(parent);
41734 if (parent.isDegenerate()) {
41735 graph = actionDeleteWay(parent.id)(graph);
41740 graph.parentRelations(entity).forEach(function (parent) {
41741 parent = parent.removeMembersWithID(id);
41742 graph = graph.replace(parent);
41744 if (parent.isDegenerate()) {
41745 graph = actionDeleteRelation(parent.id)(graph);
41750 return graph.revert(id);
41756 function actionRotate(rotateIds, pivot, angle, projection) {
41757 var action = function action(graph) {
41758 return graph.update(function (graph) {
41759 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
41760 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
41761 graph = graph.replace(node.move(projection.invert(point)));
41769 function actionScale(ids, pivotLoc, scaleFactor, projection) {
41770 return function (graph) {
41771 return graph.update(function (graph) {
41773 utilGetAllNodes(ids, graph).forEach(function (node) {
41774 point = projection(node.loc);
41775 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
41776 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
41777 graph = graph.replace(node.move(projection.invert(point)));
41783 /* Align nodes along their common axis */
41785 function actionStraightenNodes(nodeIDs, projection) {
41786 function positionAlongWay(a, o, b) {
41787 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41788 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
41791 function getEndpoints(points) {
41792 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
41793 // The shape's surrounding rectangle has 2 axes of symmetry.
41794 // Snap points to the long axis
41796 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
41797 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
41798 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
41799 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
41800 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
41809 var action = function action(graph, t) {
41810 if (t === null || !isFinite(t)) t = 1;
41811 t = Math.min(Math.max(+t, 0), 1);
41812 var nodes = nodeIDs.map(function (id) {
41813 return graph.entity(id);
41815 var points = nodes.map(function (n) {
41816 return projection(n.loc);
41818 var endpoints = getEndpoints(points);
41819 var startPoint = endpoints[0];
41820 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
41822 for (var i = 0; i < points.length; i++) {
41823 var node = nodes[i];
41824 var point = points[i];
41825 var u = positionAlongWay(point, startPoint, endPoint);
41826 var point2 = geoVecInterp(startPoint, endPoint, u);
41827 var loc2 = projection.invert(point2);
41828 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41834 action.disabled = function (graph) {
41835 var nodes = nodeIDs.map(function (id) {
41836 return graph.entity(id);
41838 var points = nodes.map(function (n) {
41839 return projection(n.loc);
41841 var endpoints = getEndpoints(points);
41842 var startPoint = endpoints[0];
41843 var endPoint = endpoints[1];
41844 var maxDistance = 0;
41846 for (var i = 0; i < points.length; i++) {
41847 var point = points[i];
41848 var u = positionAlongWay(point, startPoint, endPoint);
41849 var p = geoVecInterp(startPoint, endPoint, u);
41850 var dist = geoVecLength(p, point);
41852 if (!isNaN(dist) && dist > maxDistance) {
41853 maxDistance = dist;
41857 if (maxDistance < 0.0001) {
41858 return 'straight_enough';
41862 action.transitionable = true;
41867 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
41870 function actionStraightenWay(selectedIDs, projection) {
41871 function positionAlongWay(a, o, b) {
41872 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
41873 } // Return all selected ways as a continuous, ordered array of nodes
41876 function allNodes(graph) {
41878 var startNodes = [];
41880 var remainingWays = [];
41881 var selectedWays = selectedIDs.filter(function (w) {
41882 return graph.entity(w).type === 'way';
41884 var selectedNodes = selectedIDs.filter(function (n) {
41885 return graph.entity(n).type === 'node';
41888 for (var i = 0; i < selectedWays.length; i++) {
41889 var way = graph.entity(selectedWays[i]);
41890 nodes = way.nodes.slice(0);
41891 remainingWays.push(nodes);
41892 startNodes.push(nodes[0]);
41893 endNodes.push(nodes[nodes.length - 1]);
41894 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
41895 // and need to be removed so currNode difference calculation below works)
41896 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
41899 startNodes = startNodes.filter(function (n) {
41900 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
41902 endNodes = endNodes.filter(function (n) {
41903 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
41904 }); // Choose the initial endpoint to start from
41906 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
41908 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
41910 var getNextWay = function getNextWay(currNode, remainingWays) {
41911 return remainingWays.filter(function (way) {
41912 return way[0] === currNode || way[way.length - 1] === currNode;
41914 }; // Add nodes to end of nodes array, until all ways are added
41917 while (remainingWays.length) {
41918 nextWay = getNextWay(currNode, remainingWays);
41919 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
41921 if (nextWay[0] !== currNode) {
41925 nodes = nodes.concat(nextWay);
41926 currNode = nodes[nodes.length - 1];
41927 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
41930 if (selectedNodes.length === 2) {
41931 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
41932 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
41933 var sortedStartEnd = [startNodeIdx, endNodeIdx];
41934 sortedStartEnd.sort(function (a, b) {
41937 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
41940 return nodes.map(function (n) {
41941 return graph.entity(n);
41945 function shouldKeepNode(node, graph) {
41946 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
41949 var action = function action(graph, t) {
41950 if (t === null || !isFinite(t)) t = 1;
41951 t = Math.min(Math.max(+t, 0), 1);
41952 var nodes = allNodes(graph);
41953 var points = nodes.map(function (n) {
41954 return projection(n.loc);
41956 var startPoint = points[0];
41957 var endPoint = points[points.length - 1];
41961 for (i = 1; i < points.length - 1; i++) {
41962 var node = nodes[i];
41963 var point = points[i];
41965 if (t < 1 || shouldKeepNode(node, graph)) {
41966 var u = positionAlongWay(point, startPoint, endPoint);
41967 var p = geoVecInterp(startPoint, endPoint, u);
41968 var loc2 = projection.invert(p);
41969 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
41972 if (toDelete.indexOf(node) === -1) {
41973 toDelete.push(node);
41978 for (i = 0; i < toDelete.length; i++) {
41979 graph = actionDeleteNode(toDelete[i].id)(graph);
41985 action.disabled = function (graph) {
41986 // check way isn't too bendy
41987 var nodes = allNodes(graph);
41988 var points = nodes.map(function (n) {
41989 return projection(n.loc);
41991 var startPoint = points[0];
41992 var endPoint = points[points.length - 1];
41993 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
41996 if (threshold === 0) {
41997 return 'too_bendy';
42000 var maxDistance = 0;
42002 for (i = 1; i < points.length - 1; i++) {
42003 var point = points[i];
42004 var u = positionAlongWay(point, startPoint, endPoint);
42005 var p = geoVecInterp(startPoint, endPoint, u);
42006 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
42008 if (isNaN(dist) || dist > threshold) {
42009 return 'too_bendy';
42010 } else if (dist > maxDistance) {
42011 maxDistance = dist;
42015 var keepingAllNodes = nodes.every(function (node, i) {
42016 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
42019 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
42021 return 'straight_enough';
42025 action.transitionable = true;
42030 // `turn` must be an `osmTurn` object with a `restrictionID` property.
42031 // see osm/intersection.js, pathToTurn()
42034 function actionUnrestrictTurn(turn) {
42035 return function (graph) {
42036 return actionDeleteRelation(turn.restrictionID)(graph);
42040 /* Reflect the given area around its axis of symmetry */
42042 function actionReflect(reflectIds, projection) {
42043 var _useLongAxis = true;
42045 var action = function action(graph, t) {
42046 if (t === null || !isFinite(t)) t = 1;
42047 t = Math.min(Math.max(+t, 0), 1);
42048 var nodes = utilGetAllNodes(reflectIds, graph);
42049 var points = nodes.map(function (n) {
42050 return projection(n.loc);
42052 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
42053 // The shape's surrounding rectangle has 2 axes of symmetry.
42054 // Reflect across the longer axis by default.
42056 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
42057 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
42058 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
42059 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
42061 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
42063 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
42069 } // reflect c across pq
42070 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
42073 var dx = q[0] - p[0];
42074 var dy = q[1] - p[1];
42075 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
42076 var b = 2 * dx * dy / (dx * dx + dy * dy);
42078 for (var i = 0; i < nodes.length; i++) {
42079 var node = nodes[i];
42080 var c = projection(node.loc);
42081 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]];
42082 var loc2 = projection.invert(c2);
42083 node = node.move(geoVecInterp(node.loc, loc2, t));
42084 graph = graph.replace(node);
42090 action.useLongAxis = function (val) {
42091 if (!arguments.length) return _useLongAxis;
42092 _useLongAxis = val;
42096 action.transitionable = true;
42100 function actionUpgradeTags(entityId, oldTags, replaceTags) {
42101 return function (graph) {
42102 var entity = graph.entity(entityId);
42103 var tags = Object.assign({}, entity.tags); // shallow copy
42108 for (var oldTagKey in oldTags) {
42109 if (!(oldTagKey in tags)) continue; // wildcard match
42111 if (oldTags[oldTagKey] === '*') {
42112 // note the value since we might need to transfer it
42113 transferValue = tags[oldTagKey];
42114 delete tags[oldTagKey]; // exact match
42115 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
42116 delete tags[oldTagKey]; // match is within semicolon-delimited values
42118 var vals = tags[oldTagKey].split(';').filter(Boolean);
42119 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
42121 if (vals.length === 1 || oldIndex === -1) {
42122 delete tags[oldTagKey];
42124 if (replaceTags && replaceTags[oldTagKey]) {
42125 // replacing a value within a semicolon-delimited value, note the index
42126 semiIndex = oldIndex;
42129 vals.splice(oldIndex, 1);
42130 tags[oldTagKey] = vals.join(';');
42136 for (var replaceKey in replaceTags) {
42137 var replaceValue = replaceTags[replaceKey];
42139 if (replaceValue === '*') {
42140 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
42141 // allow any pre-existing value except `no` (troll tag)
42144 // otherwise assume `yes` is okay
42145 tags[replaceKey] = 'yes';
42147 } else if (replaceValue === '$1') {
42148 tags[replaceKey] = transferValue;
42150 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
42151 // don't override preexisting values
42152 var existingVals = tags[replaceKey].split(';').filter(Boolean);
42154 if (existingVals.indexOf(replaceValue) === -1) {
42155 existingVals.splice(semiIndex, 0, replaceValue);
42156 tags[replaceKey] = existingVals.join(';');
42159 tags[replaceKey] = replaceValue;
42165 return graph.replace(entity.update({
42171 function behaviorEdit(context) {
42172 function behavior() {
42173 context.map().minzoom(context.minEditableZoom());
42176 behavior.off = function () {
42177 context.map().minzoom(0);
42184 The hover behavior adds the `.hover` class on pointerover to all elements to which
42185 the identical datum is bound, and removes it on pointerout.
42187 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
42188 representation may consist of several elements scattered throughout the DOM hierarchy.
42189 Only one of these elements can have the :hover pseudo-class, but all of them will
42190 have the .hover class.
42193 function behaviorHover(context) {
42194 var dispatch = dispatch$8('hover');
42196 var _selection = select(null);
42198 var _newNodeId = null;
42199 var _initialNodeID = null;
42205 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
42207 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42209 function keydown(d3_event) {
42210 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42211 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
42213 _selection.classed('hover-disabled', true);
42215 dispatch.call('hover', this, null);
42219 function keyup(d3_event) {
42220 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
42221 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
42223 _selection.classed('hover-disabled', false);
42225 dispatch.call('hover', this, _targets);
42229 function behavior(selection) {
42230 _selection = selection;
42233 if (_initialNodeID) {
42234 _newNodeId = _initialNodeID;
42235 _initialNodeID = null;
42240 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
42241 .on(_pointerPrefix + 'down.hover', pointerover);
42243 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
42245 function eventTarget(d3_event) {
42246 var datum = d3_event.target && d3_event.target.__data__;
42247 if (_typeof(datum) !== 'object') return null;
42249 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
42250 return datum.properties.entity;
42256 function pointerover(d3_event) {
42257 // ignore mouse hovers with buttons pressed unless dragging
42258 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
42259 var target = eventTarget(d3_event);
42261 if (target && _targets.indexOf(target) === -1) {
42262 _targets.push(target);
42264 updateHover(d3_event, _targets);
42268 function pointerout(d3_event) {
42269 var target = eventTarget(d3_event);
42271 var index = _targets.indexOf(target);
42273 if (index !== -1) {
42274 _targets.splice(index);
42276 updateHover(d3_event, _targets);
42280 function allowsVertex(d) {
42281 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42284 function modeAllowsHover(target) {
42285 var mode = context.mode();
42287 if (mode.id === 'add-point') {
42288 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
42294 function updateHover(d3_event, targets) {
42295 _selection.selectAll('.hover').classed('hover', false);
42297 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42299 var mode = context.mode();
42301 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
42302 var node = targets.find(function (target) {
42303 return target instanceof osmEntity && target.type === 'node';
42305 _newNodeId = node && node.id;
42308 targets = targets.filter(function (datum) {
42309 if (datum instanceof osmEntity) {
42310 // If drawing a way, don't hover on a node that was just placed. #3974
42311 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
42318 for (var i in targets) {
42319 var datum = targets[i]; // What are we hovering over?
42321 if (datum.__featurehash__) {
42322 // hovering custom data
42323 selector += ', .data' + datum.__featurehash__;
42324 } else if (datum instanceof QAItem) {
42325 selector += ', .' + datum.service + '.itemId-' + datum.id;
42326 } else if (datum instanceof osmNote) {
42327 selector += ', .note-' + datum.id;
42328 } else if (datum instanceof osmEntity) {
42329 selector += ', .' + datum.id;
42331 if (datum.type === 'relation') {
42332 for (var j in datum.members) {
42333 selector += ', .' + datum.members[j].id;
42339 var suppressed = _altDisables && d3_event && d3_event.altKey;
42341 if (selector.trim().length) {
42342 // remove the first comma
42343 selector = selector.slice(1);
42345 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
42348 dispatch.call('hover', this, !suppressed && targets);
42352 behavior.off = function (selection) {
42353 selection.selectAll('.hover').classed('hover', false);
42354 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
42355 selection.classed('hover-disabled', false);
42356 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
42357 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
42360 behavior.altDisables = function (val) {
42361 if (!arguments.length) return _altDisables;
42362 _altDisables = val;
42366 behavior.ignoreVertex = function (val) {
42367 if (!arguments.length) return _ignoreVertex;
42368 _ignoreVertex = val;
42372 behavior.initialNodeID = function (nodeId) {
42373 _initialNodeID = nodeId;
42377 return utilRebind(behavior, dispatch, 'on');
42380 var _disableSpace = false;
42381 var _lastSpace = null;
42382 function behaviorDraw(context) {
42383 var dispatch = dispatch$8('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
42384 var keybinding = utilKeybinding('draw');
42386 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
42388 var _edit = behaviorEdit(context);
42390 var _closeTolerance = 4;
42391 var _tolerance = 12;
42392 var _mouseLeave = false;
42393 var _lastMouse = null;
42395 var _lastPointerUpEvent;
42397 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
42400 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
42401 // - `mode/drag_node.js` `datum()`
42404 function datum(d3_event) {
42405 var mode = context.mode();
42406 var isNote = mode && mode.id.indexOf('note') !== -1;
42407 if (d3_event.altKey || isNote) return {};
42410 if (d3_event.type === 'keydown') {
42411 element = _lastMouse && _lastMouse.target;
42413 element = d3_event.target;
42414 } // When drawing, snap only to touch targets..
42415 // (this excludes area fills and active drawing elements)
42418 var d = element.__data__;
42419 return d && d.properties && d.properties.target ? d : {};
42422 function pointerdown(d3_event) {
42423 if (_downPointer) return;
42424 var pointerLocGetter = utilFastMouse(this);
42426 id: d3_event.pointerId || 'mouse',
42427 pointerLocGetter: pointerLocGetter,
42428 downTime: +new Date(),
42429 downLoc: pointerLocGetter(d3_event)
42431 dispatch.call('down', this, d3_event, datum(d3_event));
42434 function pointerup(d3_event) {
42435 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
42436 var downPointer = _downPointer;
42437 _downPointer = null;
42438 _lastPointerUpEvent = d3_event;
42439 if (downPointer.isCancelled) return;
42440 var t2 = +new Date();
42441 var p2 = downPointer.pointerLocGetter(d3_event);
42442 var dist = geoVecLength(downPointer.downLoc, p2);
42444 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
42445 // Prevent a quick second click
42446 select(window).on('click.draw-block', function () {
42447 d3_event.stopPropagation();
42449 context.map().dblclickZoomEnable(false);
42450 window.setTimeout(function () {
42451 context.map().dblclickZoomEnable(true);
42452 select(window).on('click.draw-block', null);
42454 click(d3_event, p2);
42458 function pointermove(d3_event) {
42459 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
42460 var p2 = _downPointer.pointerLocGetter(d3_event);
42462 var dist = geoVecLength(_downPointer.downLoc, p2);
42464 if (dist >= _closeTolerance) {
42465 _downPointer.isCancelled = true;
42466 dispatch.call('downcancel', this);
42470 if (d3_event.pointerType && d3_event.pointerType !== 'mouse' || d3_event.buttons || _downPointer) return; // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
42471 // events immediately after non-mouse pointerup events; detect and ignore them.
42473 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
42474 _lastMouse = d3_event;
42475 dispatch.call('move', this, d3_event, datum(d3_event));
42478 function pointercancel(d3_event) {
42479 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
42480 if (!_downPointer.isCancelled) {
42481 dispatch.call('downcancel', this);
42484 _downPointer = null;
42488 function mouseenter() {
42489 _mouseLeave = false;
42492 function mouseleave() {
42493 _mouseLeave = true;
42496 function allowsVertex(d) {
42497 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
42499 // - `mode/drag_node.js` `doMove()`
42500 // - `behavior/draw.js` `click()`
42501 // - `behavior/draw_way.js` `move()`
42504 function click(d3_event, loc) {
42505 var d = datum(d3_event);
42506 var target = d && d.properties && d.properties.entity;
42507 var mode = context.mode();
42509 if (target && target.type === 'node' && allowsVertex(target)) {
42511 dispatch.call('clickNode', this, target, d);
42513 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
42515 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
42518 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
42519 dispatch.call('clickWay', this, choice.loc, edge, d);
42522 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
42523 var locLatLng = context.projection.invert(loc);
42524 dispatch.call('click', this, locLatLng, d);
42526 } // treat a spacebar press like a click
42529 function space(d3_event) {
42530 d3_event.preventDefault();
42531 d3_event.stopPropagation();
42532 var currSpace = context.map().mouse();
42534 if (_disableSpace && _lastSpace) {
42535 var dist = geoVecLength(_lastSpace, currSpace);
42537 if (dist > _tolerance) {
42538 _disableSpace = false;
42542 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
42544 _lastSpace = currSpace;
42545 _disableSpace = true;
42546 select(window).on('keyup.space-block', function () {
42547 d3_event.preventDefault();
42548 d3_event.stopPropagation();
42549 _disableSpace = false;
42550 select(window).on('keyup.space-block', null);
42551 }); // get the current mouse position
42553 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
42554 context.projection(context.map().center());
42555 click(d3_event, loc);
42558 function backspace(d3_event) {
42559 d3_event.preventDefault();
42560 dispatch.call('undo');
42563 function del(d3_event) {
42564 d3_event.preventDefault();
42565 dispatch.call('cancel');
42568 function ret(d3_event) {
42569 d3_event.preventDefault();
42570 dispatch.call('finish');
42573 function behavior(selection) {
42574 context.install(_hover);
42575 context.install(_edit);
42576 _downPointer = null;
42577 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
42578 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
42579 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
42580 select(document).call(keybinding);
42584 behavior.off = function (selection) {
42585 context.ui().sidebar.hover.cancel();
42586 context.uninstall(_hover);
42587 context.uninstall(_edit);
42588 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
42589 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
42591 select(document).call(keybinding.unbind);
42594 behavior.hover = function () {
42598 return utilRebind(behavior, dispatch, 'on');
42601 function initRange(domain, range) {
42602 switch (arguments.length) {
42607 this.range(domain);
42611 this.range(range).domain(domain);
42618 function constants(x) {
42619 return function () {
42624 function number(x) {
42629 function identity$1(x) {
42633 function normalize(a, b) {
42634 return (b -= a = +a) ? function (x) {
42635 return (x - a) / b;
42636 } : constants(isNaN(b) ? NaN : 0.5);
42639 function clamper(a, b) {
42641 if (a > b) t = a, a = b, b = t;
42642 return function (x) {
42643 return Math.max(a, Math.min(b, x));
42645 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
42646 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
42649 function bimap(domain, range, interpolate) {
42650 var d0 = domain[0],
42654 if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
42655 return function (x) {
42660 function polymap(domain, range, interpolate) {
42661 var j = Math.min(domain.length, range.length) - 1,
42664 i = -1; // Reverse descending domains.
42666 if (domain[j] < domain[0]) {
42667 domain = domain.slice().reverse();
42668 range = range.slice().reverse();
42672 d[i] = normalize(domain[i], domain[i + 1]);
42673 r[i] = interpolate(range[i], range[i + 1]);
42676 return function (x) {
42677 var i = bisectRight(domain, x, 1, j) - 1;
42678 return r[i](d[i](x));
42682 function copy(source, target) {
42683 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
42685 function transformer() {
42688 interpolate = interpolate$1,
42692 clamp = identity$1,
42697 function rescale() {
42698 var n = Math.min(domain.length, range.length);
42699 if (clamp !== identity$1) clamp = clamper(domain[0], domain[n - 1]);
42700 piecewise = n > 2 ? polymap : bimap;
42701 output = input = null;
42705 function scale(x) {
42706 return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
42709 scale.invert = function (y) {
42710 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
42713 scale.domain = function (_) {
42714 return arguments.length ? (domain = Array.from(_, number), rescale()) : domain.slice();
42717 scale.range = function (_) {
42718 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
42721 scale.rangeRound = function (_) {
42722 return range = Array.from(_), interpolate = interpolateRound, rescale();
42725 scale.clamp = function (_) {
42726 return arguments.length ? (clamp = _ ? true : identity$1, rescale()) : clamp !== identity$1;
42729 scale.interpolate = function (_) {
42730 return arguments.length ? (interpolate = _, rescale()) : interpolate;
42733 scale.unknown = function (_) {
42734 return arguments.length ? (unknown = _, scale) : unknown;
42737 return function (t, u) {
42738 transform = t, untransform = u;
42742 function continuous() {
42743 return transformer()(identity$1, identity$1);
42746 function formatDecimal (x) {
42747 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
42748 } // Computes the decimal coefficient and exponent of the specified number x with
42749 // significant digits p, where x is positive and p is in [1, 21] or undefined.
42750 // For example, formatDecimalParts(1.23) returns ["123", 0].
42752 function formatDecimalParts(x, p) {
42753 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
42756 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
42757 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
42759 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
42762 function exponent (x) {
42763 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
42766 function formatGroup (grouping, thousands) {
42767 return function (value, width) {
42768 var i = value.length,
42774 while (i > 0 && g > 0) {
42775 if (length + g + 1 > width) g = Math.max(1, width - length);
42776 t.push(value.substring(i -= g, i + g));
42777 if ((length += g + 1) > width) break;
42778 g = grouping[j = (j + 1) % grouping.length];
42781 return t.reverse().join(thousands);
42785 function formatNumerals (numerals) {
42786 return function (value) {
42787 return value.replace(/[0-9]/g, function (i) {
42788 return numerals[+i];
42793 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
42794 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
42795 function formatSpecifier(specifier) {
42796 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
42798 return new FormatSpecifier({
42806 precision: match[8] && match[8].slice(1),
42811 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
42813 function FormatSpecifier(specifier) {
42814 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
42815 this.align = specifier.align === undefined ? ">" : specifier.align + "";
42816 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
42817 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
42818 this.zero = !!specifier.zero;
42819 this.width = specifier.width === undefined ? undefined : +specifier.width;
42820 this.comma = !!specifier.comma;
42821 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
42822 this.trim = !!specifier.trim;
42823 this.type = specifier.type === undefined ? "" : specifier.type + "";
42826 FormatSpecifier.prototype.toString = function () {
42827 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;
42830 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
42831 function formatTrim (s) {
42832 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
42839 if (i0 === 0) i0 = i;
42844 if (!+s[i]) break out;
42845 if (i0 > 0) i0 = 0;
42850 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
42853 var nativeToPrecision = 1.0.toPrecision;
42855 var FORCED$1 = fails(function () {
42857 return nativeToPrecision.call(1, undefined) !== '1';
42858 }) || !fails(function () {
42859 // V8 ~ Android 4.3-
42860 nativeToPrecision.call({});
42863 // `Number.prototype.toPrecision` method
42864 // https://tc39.es/ecma262/#sec-number.prototype.toprecision
42865 _export({ target: 'Number', proto: true, forced: FORCED$1 }, {
42866 toPrecision: function toPrecision(precision) {
42867 return precision === undefined
42868 ? nativeToPrecision.call(thisNumberValue(this))
42869 : nativeToPrecision.call(thisNumberValue(this), precision);
42873 var prefixExponent;
42874 function formatPrefixAuto (x, p) {
42875 var d = formatDecimalParts(x, p);
42876 if (!d) return x + "";
42877 var coefficient = d[0],
42879 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
42880 n = coefficient.length;
42881 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") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
42884 function formatRounded (x, p) {
42885 var d = formatDecimalParts(x, p);
42886 if (!d) return x + "";
42887 var coefficient = d[0],
42889 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");
42892 var formatTypes = {
42893 "%": function _(x, p) {
42894 return (x * 100).toFixed(p);
42896 "b": function b(x) {
42897 return Math.round(x).toString(2);
42899 "c": function c(x) {
42902 "d": formatDecimal,
42903 "e": function e(x, p) {
42904 return x.toExponential(p);
42906 "f": function f(x, p) {
42907 return x.toFixed(p);
42909 "g": function g(x, p) {
42910 return x.toPrecision(p);
42912 "o": function o(x) {
42913 return Math.round(x).toString(8);
42915 "p": function p(x, _p) {
42916 return formatRounded(x * 100, _p);
42918 "r": formatRounded,
42919 "s": formatPrefixAuto,
42920 "X": function X(x) {
42921 return Math.round(x).toString(16).toUpperCase();
42923 "x": function x(_x) {
42924 return Math.round(_x).toString(16);
42928 function identity (x) {
42932 var map$1 = Array.prototype.map,
42933 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
42934 function formatLocale (locale) {
42935 var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map$1.call(locale.grouping, Number), locale.thousands + ""),
42936 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
42937 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
42938 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
42939 numerals = locale.numerals === undefined ? identity : formatNumerals(map$1.call(locale.numerals, String)),
42940 percent = locale.percent === undefined ? "%" : locale.percent + "",
42941 minus = locale.minus === undefined ? "−" : locale.minus + "",
42942 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
42944 function newFormat(specifier) {
42945 specifier = formatSpecifier(specifier);
42946 var fill = specifier.fill,
42947 align = specifier.align,
42948 sign = specifier.sign,
42949 symbol = specifier.symbol,
42950 zero = specifier.zero,
42951 width = specifier.width,
42952 comma = specifier.comma,
42953 precision = specifier.precision,
42954 trim = specifier.trim,
42955 type = specifier.type; // The "n" type is an alias for ",g".
42957 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
42958 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
42960 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
42961 // For SI-prefix, the suffix is lazily computed.
42963 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
42964 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
42965 // Is this an integer type?
42966 // Can this type generate exponential notation?
42968 var formatType = formatTypes[type],
42969 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
42970 // or clamp the specified precision to the supported range.
42971 // For significant precision, it must be in [1, 21].
42972 // For fixed precision, it must be in [0, 20].
42974 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
42976 function format(value) {
42977 var valuePrefix = prefix,
42978 valueSuffix = suffix,
42983 if (type === "c") {
42984 valueSuffix = formatType(value) + valueSuffix;
42987 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
42989 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
42991 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
42993 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
42995 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
42997 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
42998 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
42999 // grouped, and fractional or exponential “suffix” part that is not.
43002 i = -1, n = value.length;
43005 if (c = value.charCodeAt(i), 48 > c || c > 57) {
43006 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
43007 value = value.slice(0, i);
43012 } // If the fill character is not "0", grouping is applied before padding.
43015 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
43017 var length = valuePrefix.length + value.length + valueSuffix.length,
43018 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
43020 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
43024 value = valuePrefix + value + valueSuffix + padding;
43028 value = valuePrefix + padding + value + valueSuffix;
43032 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
43036 value = padding + valuePrefix + value + valueSuffix;
43040 return numerals(value);
43043 format.toString = function () {
43044 return specifier + "";
43050 function formatPrefix(specifier, value) {
43051 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
43052 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
43053 k = Math.pow(10, -e),
43054 prefix = prefixes[8 + e / 3];
43055 return function (value) {
43056 return f(k * value) + prefix;
43062 formatPrefix: formatPrefix
43072 currency: ["$", ""]
43074 function defaultLocale(definition) {
43075 locale = formatLocale(definition);
43076 format = locale.format;
43077 formatPrefix = locale.formatPrefix;
43081 function precisionFixed (step) {
43082 return Math.max(0, -exponent(Math.abs(step)));
43085 function precisionPrefix (step, value) {
43086 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
43089 function precisionRound (step, max) {
43090 step = Math.abs(step), max = Math.abs(max) - step;
43091 return Math.max(0, exponent(max) - exponent(step)) + 1;
43094 function tickFormat(start, stop, count, specifier) {
43095 var step = tickStep(start, stop, count),
43097 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
43099 switch (specifier.type) {
43102 var value = Math.max(Math.abs(start), Math.abs(stop));
43103 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
43104 return formatPrefix(specifier, value);
43113 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
43120 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
43125 return format(specifier);
43128 function linearish(scale) {
43129 var domain = scale.domain;
43131 scale.ticks = function (count) {
43133 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
43136 scale.tickFormat = function (count, specifier) {
43138 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
43141 scale.nice = function (count) {
43142 if (count == null) count = 10;
43145 var i1 = d.length - 1;
43152 if (stop < start) {
43153 step = start, start = stop, stop = step;
43154 step = i0, i0 = i1, i1 = step;
43157 while (maxIter-- > 0) {
43158 step = tickIncrement(start, stop, count);
43160 if (step === prestep) {
43164 } else if (step > 0) {
43165 start = Math.floor(start / step) * step;
43166 stop = Math.ceil(stop / step) * step;
43167 } else if (step < 0) {
43168 start = Math.ceil(start * step) / step;
43169 stop = Math.floor(stop * step) / step;
43182 function linear() {
43183 var scale = continuous();
43185 scale.copy = function () {
43186 return copy(scale, linear());
43189 initRange.apply(scale, arguments);
43190 return linearish(scale);
43193 // eslint-disable-next-line es/no-math-expm1 -- safe
43194 var $expm1 = Math.expm1;
43195 var exp$1 = Math.exp;
43197 // `Math.expm1` method implementation
43198 // https://tc39.es/ecma262/#sec-math.expm1
43199 var mathExpm1 = (!$expm1
43201 || $expm1(10) > 22025.465794806719 || $expm1(10) < 22025.4657948067165168
43203 || $expm1(-2e-17) != -2e-17
43204 ) ? function expm1(x) {
43205 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
43208 function quantize() {
43216 function scale(x) {
43217 return x != null && x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
43220 function rescale() {
43222 domain = new Array(n);
43225 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
43231 scale.domain = function (_) {
43234 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
43237 scale.range = function (_) {
43238 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
43241 scale.invertExtent = function (y) {
43242 var i = range.indexOf(y);
43243 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
43246 scale.unknown = function (_) {
43247 return arguments.length ? (unknown = _, scale) : scale;
43250 scale.thresholds = function () {
43251 return domain.slice();
43254 scale.copy = function () {
43255 return quantize().domain([x0, x1]).range(range).unknown(unknown);
43258 return initRange.apply(linearish(scale), arguments);
43261 // https://github.com/tc39/proposal-string-pad-start-end
43266 var ceil = Math.ceil;
43268 // `String.prototype.{ padStart, padEnd }` methods implementation
43269 var createMethod = function (IS_END) {
43270 return function ($this, maxLength, fillString) {
43271 var S = String(requireObjectCoercible($this));
43272 var stringLength = S.length;
43273 var fillStr = fillString === undefined ? ' ' : String(fillString);
43274 var intMaxLength = toLength(maxLength);
43275 var fillLen, stringFiller;
43276 if (intMaxLength <= stringLength || fillStr == '') return S;
43277 fillLen = intMaxLength - stringLength;
43278 stringFiller = stringRepeat.call(fillStr, ceil(fillLen / fillStr.length));
43279 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
43280 return IS_END ? S + stringFiller : stringFiller + S;
43285 // `String.prototype.padStart` method
43286 // https://tc39.es/ecma262/#sec-string.prototype.padstart
43287 start: createMethod(false),
43288 // `String.prototype.padEnd` method
43289 // https://tc39.es/ecma262/#sec-string.prototype.padend
43290 end: createMethod(true)
43293 var padStart = stringPad.start;
43295 var abs$1 = Math.abs;
43296 var DatePrototype = Date.prototype;
43297 var getTime = DatePrototype.getTime;
43298 var nativeDateToISOString = DatePrototype.toISOString;
43300 // `Date.prototype.toISOString` method implementation
43301 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43302 // PhantomJS / old WebKit fails here:
43303 var dateToIsoString = (fails(function () {
43304 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
43305 }) || !fails(function () {
43306 nativeDateToISOString.call(new Date(NaN));
43307 })) ? function toISOString() {
43308 if (!isFinite(getTime.call(this))) throw RangeError('Invalid time value');
43310 var year = date.getUTCFullYear();
43311 var milliseconds = date.getUTCMilliseconds();
43312 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
43313 return sign + padStart(abs$1(year), sign ? 6 : 4, 0) +
43314 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
43315 '-' + padStart(date.getUTCDate(), 2, 0) +
43316 'T' + padStart(date.getUTCHours(), 2, 0) +
43317 ':' + padStart(date.getUTCMinutes(), 2, 0) +
43318 ':' + padStart(date.getUTCSeconds(), 2, 0) +
43319 '.' + padStart(milliseconds, 3, 0) +
43321 } : nativeDateToISOString;
43323 // `Date.prototype.toISOString` method
43324 // https://tc39.es/ecma262/#sec-date.prototype.toisostring
43325 // PhantomJS / old WebKit has a broken implementations
43326 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
43327 toISOString: dateToIsoString
43330 function behaviorBreathe() {
43331 var duration = 800;
43333 var selector = '.selected.shadow, .selected .shadow';
43335 var _selected = select(null);
43343 function ratchetyInterpolator(a, b, steps, units) {
43346 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
43347 return function (t) {
43348 return String(sample(t)) + (units || '');
43352 function reset(selection) {
43353 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
43356 function setAnimationParams(transition, fromTo) {
43357 var toFrom = fromTo === 'from' ? 'to' : 'from';
43358 transition.styleTween('stroke-opacity', function (d) {
43359 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43360 }).styleTween('stroke-width', function (d) {
43361 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43362 }).styleTween('fill-opacity', function (d) {
43363 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
43364 }).styleTween('r', function (d) {
43365 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
43369 function calcAnimationParams(selection) {
43370 selection.call(reset).each(function (d) {
43371 var s = select(this);
43372 var tag = s.node().tagName;
43378 var width; // determine base opacity and width
43380 if (tag === 'circle') {
43381 opacity = parseFloat(s.style('fill-opacity') || 0.5);
43382 width = parseFloat(s.style('r') || 15.5);
43384 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
43385 width = parseFloat(s.style('stroke-width') || 10);
43386 } // calculate from/to interpolation params..
43390 p.from.opacity = opacity * 0.6;
43391 p.to.opacity = opacity * 1.25;
43392 p.from.width = width * 0.7;
43393 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
43398 function run(surface, fromTo) {
43399 var toFrom = fromTo === 'from' ? 'to' : 'from';
43400 var currSelected = surface.selectAll(selector);
43401 var currClassed = surface.attr('class');
43403 if (_done || currSelected.empty()) {
43404 _selected.call(reset);
43406 _selected = select(null);
43410 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
43411 _selected.call(reset);
43413 _classed = currClassed;
43414 _selected = currSelected.call(calcAnimationParams);
43417 var didCallNextRun = false;
43419 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
43420 // `end` event is called for each selected element, but we want
43421 // it to run only once
43422 if (!didCallNextRun) {
43423 surface.call(run, toFrom);
43424 didCallNextRun = true;
43425 } // if entity was deselected, remove breathe styling
43428 if (!select(this).classed('selected')) {
43429 reset(select(this));
43434 function behavior(surface) {
43436 _timer = timer(function () {
43437 // wait for elements to actually become selected
43438 if (surface.selectAll(selector).empty()) {
43442 surface.call(run, 'from');
43450 behavior.restartIfNeeded = function (surface) {
43451 if (_selected.empty()) {
43452 surface.call(run, 'from');
43460 behavior.off = function () {
43467 _selected.interrupt().call(reset);
43473 /* Creates a keybinding behavior for an operation */
43474 function behaviorOperation(context) {
43477 function keypress(d3_event) {
43478 // prevent operations during low zoom selection
43479 if (!context.map().withinEditableZoom()) return;
43480 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
43481 d3_event.preventDefault();
43483 var disabled = _operation.disabled();
43486 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
43488 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
43489 if (_operation.point) _operation.point(null);
43495 function behavior() {
43496 if (_operation && _operation.available()) {
43497 context.keybinding().on(_operation.keys, keypress);
43503 behavior.off = function () {
43504 context.keybinding().off(_operation.keys);
43507 behavior.which = function (_) {
43508 if (!arguments.length) return _operation;
43516 function operationCircularize(context, selectedIDs) {
43519 var _actions = selectedIDs.map(getAction).filter(Boolean);
43521 var _amount = _actions.length === 1 ? 'single' : 'multiple';
43523 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43527 function getAction(entityID) {
43528 var entity = context.entity(entityID);
43529 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
43532 _extent = entity.extent(context.graph());
43534 _extent = _extent.extend(entity.extent(context.graph()));
43537 return actionCircularize(entityID, context.projection);
43540 var operation = function operation() {
43541 if (!_actions.length) return;
43543 var combinedAction = function combinedAction(graph, t) {
43544 _actions.forEach(function (action) {
43545 if (!action.disabled(graph)) {
43546 graph = action(graph, t);
43553 combinedAction.transitionable = true;
43554 context.perform(combinedAction, operation.annotation());
43555 window.setTimeout(function () {
43556 context.validator().validate();
43557 }, 300); // after any transition
43560 operation.available = function () {
43561 return _actions.length && selectedIDs.length === _actions.length;
43562 }; // don't cache this because the visible extent could change
43565 operation.disabled = function () {
43566 if (!_actions.length) return '';
43568 var actionDisableds = _actions.map(function (action) {
43569 return action.disabled(context.graph());
43570 }).filter(Boolean);
43572 if (actionDisableds.length === _actions.length) {
43573 // none of the features can be circularized
43574 if (new Set(actionDisableds).size > 1) {
43575 return 'multiple_blockers';
43578 return actionDisableds[0];
43579 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
43580 return 'too_large';
43581 } else if (someMissing()) {
43582 return 'not_downloaded';
43583 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43584 return 'connected_to_hidden';
43589 function someMissing() {
43590 if (context.inIntro()) return false;
43591 var osm = context.connection();
43594 var missing = _coords.filter(function (loc) {
43595 return !osm.isDataLoaded(loc);
43598 if (missing.length) {
43599 missing.forEach(function (loc) {
43600 context.loadTileAtLoc(loc);
43610 operation.tooltip = function () {
43611 var disable = operation.disabled();
43612 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
43615 operation.annotation = function () {
43616 return _t('operations.circularize.annotation.feature', {
43621 operation.id = 'circularize';
43622 operation.keys = [_t('operations.circularize.key')];
43623 operation.title = _t('operations.circularize.title');
43624 operation.behavior = behaviorOperation(context).which(operation);
43628 // For example, ⌘Z -> Ctrl+Z
43630 var uiCmd = function uiCmd(code) {
43631 var detected = utilDetect();
43633 if (detected.os === 'mac') {
43637 if (detected.os === 'win') {
43638 if (code === '⌘⇧Z') return 'Ctrl+Y';
43650 for (var i = 0; i < code.length; i++) {
43651 if (code[i] in replacements) {
43652 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
43659 }; // return a display-focused string for a given keyboard code
43661 uiCmd.display = function (code) {
43662 if (code.length !== 1) return code;
43663 var detected = utilDetect();
43664 var mac = detected.os === 'mac';
43665 var replacements = {
43666 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
43667 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
43668 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
43669 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
43670 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
43671 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
43672 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
43673 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
43674 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
43675 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
43676 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
43677 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
43678 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
43680 return replacements[code] || code;
43683 function operationDelete(context, selectedIDs) {
43684 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43685 var action = actionDeleteMultiple(selectedIDs);
43686 var nodes = utilGetAllNodes(selectedIDs, context.graph());
43687 var coords = nodes.map(function (n) {
43690 var extent = utilTotalExtent(selectedIDs, context.graph());
43692 var operation = function operation() {
43693 var nextSelectedID;
43694 var nextSelectedLoc;
43696 if (selectedIDs.length === 1) {
43697 var id = selectedIDs[0];
43698 var entity = context.entity(id);
43699 var geometry = entity.geometry(context.graph());
43700 var parents = context.graph().parentWays(entity);
43701 var parent = parents[0]; // Select the next closest node in the way.
43703 if (geometry === 'vertex') {
43704 var nodes = parent.nodes;
43705 var i = nodes.indexOf(id);
43709 } else if (i === nodes.length - 1) {
43712 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
43713 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
43714 i = a < b ? i - 1 : i + 1;
43717 nextSelectedID = nodes[i];
43718 nextSelectedLoc = context.entity(nextSelectedID).loc;
43722 context.perform(action, operation.annotation());
43723 context.validator().validate();
43725 if (nextSelectedID && nextSelectedLoc) {
43726 if (context.hasEntity(nextSelectedID)) {
43727 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
43729 context.map().centerEase(nextSelectedLoc);
43730 context.enter(modeBrowse(context));
43733 context.enter(modeBrowse(context));
43737 operation.available = function () {
43741 operation.disabled = function () {
43742 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43743 return 'too_large';
43744 } else if (someMissing()) {
43745 return 'not_downloaded';
43746 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43747 return 'connected_to_hidden';
43748 } else if (selectedIDs.some(protectedMember)) {
43749 return 'part_of_relation';
43750 } else if (selectedIDs.some(incompleteRelation)) {
43751 return 'incomplete_relation';
43752 } else if (selectedIDs.some(hasWikidataTag)) {
43753 return 'has_wikidata_tag';
43758 function someMissing() {
43759 if (context.inIntro()) return false;
43760 var osm = context.connection();
43763 var missing = coords.filter(function (loc) {
43764 return !osm.isDataLoaded(loc);
43767 if (missing.length) {
43768 missing.forEach(function (loc) {
43769 context.loadTileAtLoc(loc);
43778 function hasWikidataTag(id) {
43779 var entity = context.entity(id);
43780 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
43783 function incompleteRelation(id) {
43784 var entity = context.entity(id);
43785 return entity.type === 'relation' && !entity.isComplete(context.graph());
43788 function protectedMember(id) {
43789 var entity = context.entity(id);
43790 if (entity.type !== 'way') return false;
43791 var parents = context.graph().parentRelations(entity);
43793 for (var i = 0; i < parents.length; i++) {
43794 var parent = parents[i];
43795 var type = parent.tags.type;
43796 var role = parent.memberById(id).role || 'outer';
43798 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
43807 operation.tooltip = function () {
43808 var disable = operation.disabled();
43809 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
43812 operation.annotation = function () {
43813 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
43814 n: selectedIDs.length
43818 operation.id = 'delete';
43819 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
43820 operation.title = _t('operations.delete.title');
43821 operation.behavior = behaviorOperation(context).which(operation);
43825 function operationOrthogonalize(context, selectedIDs) {
43830 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
43832 var _amount = _actions.length === 1 ? 'single' : 'multiple';
43834 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
43838 function chooseAction(entityID) {
43839 var entity = context.entity(entityID);
43840 var geometry = entity.geometry(context.graph());
43843 _extent = entity.extent(context.graph());
43845 _extent = _extent.extend(entity.extent(context.graph()));
43846 } // square a line/area
43849 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
43850 if (_type && _type !== 'feature') return null;
43852 return actionOrthogonalize(entityID, context.projection); // square a single vertex
43853 } else if (geometry === 'vertex') {
43854 if (_type && _type !== 'corner') return null;
43856 var graph = context.graph();
43857 var parents = graph.parentWays(entity);
43859 if (parents.length === 1) {
43860 var way = parents[0];
43862 if (way.nodes.indexOf(entityID) !== -1) {
43863 return actionOrthogonalize(way.id, context.projection, entityID);
43871 var operation = function operation() {
43872 if (!_actions.length) return;
43874 var combinedAction = function combinedAction(graph, t) {
43875 _actions.forEach(function (action) {
43876 if (!action.disabled(graph)) {
43877 graph = action(graph, t);
43884 combinedAction.transitionable = true;
43885 context.perform(combinedAction, operation.annotation());
43886 window.setTimeout(function () {
43887 context.validator().validate();
43888 }, 300); // after any transition
43891 operation.available = function () {
43892 return _actions.length && selectedIDs.length === _actions.length;
43893 }; // don't cache this because the visible extent could change
43896 operation.disabled = function () {
43897 if (!_actions.length) return '';
43899 var actionDisableds = _actions.map(function (action) {
43900 return action.disabled(context.graph());
43901 }).filter(Boolean);
43903 if (actionDisableds.length === _actions.length) {
43904 // none of the features can be squared
43905 if (new Set(actionDisableds).size > 1) {
43906 return 'multiple_blockers';
43909 return actionDisableds[0];
43910 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
43911 return 'too_large';
43912 } else if (someMissing()) {
43913 return 'not_downloaded';
43914 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43915 return 'connected_to_hidden';
43920 function someMissing() {
43921 if (context.inIntro()) return false;
43922 var osm = context.connection();
43925 var missing = _coords.filter(function (loc) {
43926 return !osm.isDataLoaded(loc);
43929 if (missing.length) {
43930 missing.forEach(function (loc) {
43931 context.loadTileAtLoc(loc);
43941 operation.tooltip = function () {
43942 var disable = operation.disabled();
43943 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
43946 operation.annotation = function () {
43947 return _t('operations.orthogonalize.annotation.' + _type, {
43952 operation.id = 'orthogonalize';
43953 operation.keys = [_t('operations.orthogonalize.key')];
43954 operation.title = _t('operations.orthogonalize.title');
43955 operation.behavior = behaviorOperation(context).which(operation);
43959 function operationReflectShort(context, selectedIDs) {
43960 return operationReflect(context, selectedIDs, 'short');
43962 function operationReflectLong(context, selectedIDs) {
43963 return operationReflect(context, selectedIDs, 'long');
43965 function operationReflect(context, selectedIDs, axis) {
43966 axis = axis || 'long';
43967 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
43968 var nodes = utilGetAllNodes(selectedIDs, context.graph());
43969 var coords = nodes.map(function (n) {
43972 var extent = utilTotalExtent(selectedIDs, context.graph());
43974 var operation = function operation() {
43975 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
43976 context.perform(action, operation.annotation());
43977 window.setTimeout(function () {
43978 context.validator().validate();
43979 }, 300); // after any transition
43982 operation.available = function () {
43983 return nodes.length >= 3;
43984 }; // don't cache this because the visible extent could change
43987 operation.disabled = function () {
43988 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
43989 return 'too_large';
43990 } else if (someMissing()) {
43991 return 'not_downloaded';
43992 } else if (selectedIDs.some(context.hasHiddenConnections)) {
43993 return 'connected_to_hidden';
43994 } else if (selectedIDs.some(incompleteRelation)) {
43995 return 'incomplete_relation';
44000 function someMissing() {
44001 if (context.inIntro()) return false;
44002 var osm = context.connection();
44005 var missing = coords.filter(function (loc) {
44006 return !osm.isDataLoaded(loc);
44009 if (missing.length) {
44010 missing.forEach(function (loc) {
44011 context.loadTileAtLoc(loc);
44020 function incompleteRelation(id) {
44021 var entity = context.entity(id);
44022 return entity.type === 'relation' && !entity.isComplete(context.graph());
44026 operation.tooltip = function () {
44027 var disable = operation.disabled();
44028 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
44031 operation.annotation = function () {
44032 return _t('operations.reflect.annotation.' + axis + '.feature', {
44033 n: selectedIDs.length
44037 operation.id = 'reflect-' + axis;
44038 operation.keys = [_t('operations.reflect.key.' + axis)];
44039 operation.title = _t('operations.reflect.title.' + axis);
44040 operation.behavior = behaviorOperation(context).which(operation);
44044 function operationMove(context, selectedIDs) {
44045 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44046 var nodes = utilGetAllNodes(selectedIDs, context.graph());
44047 var coords = nodes.map(function (n) {
44050 var extent = utilTotalExtent(selectedIDs, context.graph());
44052 var operation = function operation() {
44053 context.enter(modeMove(context, selectedIDs));
44056 operation.available = function () {
44057 return selectedIDs.length > 0;
44060 operation.disabled = function () {
44061 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44062 return 'too_large';
44063 } else if (someMissing()) {
44064 return 'not_downloaded';
44065 } else if (selectedIDs.some(context.hasHiddenConnections)) {
44066 return 'connected_to_hidden';
44067 } else if (selectedIDs.some(incompleteRelation)) {
44068 return 'incomplete_relation';
44073 function someMissing() {
44074 if (context.inIntro()) return false;
44075 var osm = context.connection();
44078 var missing = coords.filter(function (loc) {
44079 return !osm.isDataLoaded(loc);
44082 if (missing.length) {
44083 missing.forEach(function (loc) {
44084 context.loadTileAtLoc(loc);
44093 function incompleteRelation(id) {
44094 var entity = context.entity(id);
44095 return entity.type === 'relation' && !entity.isComplete(context.graph());
44099 operation.tooltip = function () {
44100 var disable = operation.disabled();
44101 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
44104 operation.annotation = function () {
44105 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
44106 n: selectedIDs.length
44110 operation.id = 'move';
44111 operation.keys = [_t('operations.move.key')];
44112 operation.title = _t('operations.move.title');
44113 operation.behavior = behaviorOperation(context).which(operation);
44114 operation.mouseOnly = true;
44118 function modeRotate(context, entityIDs) {
44119 var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeMove
44125 var keybinding = utilKeybinding('rotate');
44126 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];
44127 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
44128 n: entityIDs.length
44135 var _prevTransform;
44137 var _pivot; // use pointer events on supported platforms; fallback to mouse events
44140 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44142 function doRotate(d3_event) {
44145 if (context.graph() !== _prevGraph) {
44146 fn = context.perform;
44148 fn = context.replace;
44149 } // projection changed, recalculate _pivot
44152 var projection = context.projection;
44153 var currTransform = projection.transform();
44155 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
44156 var nodes = utilGetAllNodes(entityIDs, context.graph());
44157 var points = nodes.map(function (n) {
44158 return projection(n.loc);
44160 _pivot = getPivot(points);
44161 _prevAngle = undefined;
44164 var currMouse = context.map().mouse(d3_event);
44165 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
44166 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
44167 var delta = currAngle - _prevAngle;
44168 fn(actionRotate(entityIDs, _pivot, delta, projection));
44169 _prevTransform = currTransform;
44170 _prevAngle = currAngle;
44171 _prevGraph = context.graph();
44174 function getPivot(points) {
44177 if (points.length === 1) {
44178 _pivot = points[0];
44179 } else if (points.length === 2) {
44180 _pivot = geoVecInterp(points[0], points[1], 0.5);
44182 var polygonHull = d3_polygonHull(points);
44184 if (polygonHull.length === 2) {
44185 _pivot = geoVecInterp(points[0], points[1], 0.5);
44187 _pivot = d3_polygonCentroid(d3_polygonHull(points));
44194 function finish(d3_event) {
44195 d3_event.stopPropagation();
44196 context.replace(actionNoop(), annotation);
44197 context.enter(modeSelect(context, entityIDs));
44200 function cancel() {
44201 if (_prevGraph) context.pop(); // remove the rotate
44203 context.enter(modeSelect(context, entityIDs));
44206 function undone() {
44207 context.enter(modeBrowse(context));
44210 mode.enter = function () {
44212 context.features().forceVisible(entityIDs);
44213 behaviors.forEach(context.install);
44215 context.surface().on(_pointerPrefix + 'down.modeRotate', function (d3_event) {
44216 downEvent = d3_event;
44218 select(window).on(_pointerPrefix + 'move.modeRotate', doRotate, true).on(_pointerPrefix + 'up.modeRotate', function (d3_event) {
44219 if (!downEvent) return;
44220 var mapNode = context.container().select('.main-map').node();
44221 var pointGetter = utilFastMouse(mapNode);
44222 var p1 = pointGetter(downEvent);
44223 var p2 = pointGetter(d3_event);
44224 var dist = geoVecLength(p1, p2);
44225 if (dist <= _tolerancePx) finish(d3_event);
44228 context.history().on('undone.modeRotate', undone);
44229 keybinding.on('⎋', cancel).on('↩', finish);
44230 select(document).call(keybinding);
44233 mode.exit = function () {
44234 behaviors.forEach(context.uninstall);
44235 context.surface().on(_pointerPrefix + 'down.modeRotate', null);
44236 select(window).on(_pointerPrefix + 'move.modeRotate', null, true).on(_pointerPrefix + 'up.modeRotate', null, true);
44237 context.history().on('undone.modeRotate', null);
44238 select(document).call(keybinding.unbind);
44239 context.features().forceVisible([]);
44242 mode.selectedIDs = function () {
44243 if (!arguments.length) return entityIDs; // no assign
44251 function operationRotate(context, selectedIDs) {
44252 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
44253 var nodes = utilGetAllNodes(selectedIDs, context.graph());
44254 var coords = nodes.map(function (n) {
44257 var extent = utilTotalExtent(selectedIDs, context.graph());
44259 var operation = function operation() {
44260 context.enter(modeRotate(context, selectedIDs));
44263 operation.available = function () {
44264 return nodes.length >= 2;
44267 operation.disabled = function () {
44268 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
44269 return 'too_large';
44270 } else if (someMissing()) {
44271 return 'not_downloaded';
44272 } else if (selectedIDs.some(context.hasHiddenConnections)) {
44273 return 'connected_to_hidden';
44274 } else if (selectedIDs.some(incompleteRelation)) {
44275 return 'incomplete_relation';
44280 function someMissing() {
44281 if (context.inIntro()) return false;
44282 var osm = context.connection();
44285 var missing = coords.filter(function (loc) {
44286 return !osm.isDataLoaded(loc);
44289 if (missing.length) {
44290 missing.forEach(function (loc) {
44291 context.loadTileAtLoc(loc);
44300 function incompleteRelation(id) {
44301 var entity = context.entity(id);
44302 return entity.type === 'relation' && !entity.isComplete(context.graph());
44306 operation.tooltip = function () {
44307 var disable = operation.disabled();
44308 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
44311 operation.annotation = function () {
44312 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
44313 n: selectedIDs.length
44317 operation.id = 'rotate';
44318 operation.keys = [_t('operations.rotate.key')];
44319 operation.title = _t('operations.rotate.title');
44320 operation.behavior = behaviorOperation(context).which(operation);
44321 operation.mouseOnly = true;
44325 function modeMove(context, entityIDs, baseGraph) {
44326 var _tolerancePx = 4; // see also behaviorDrag, behaviorSelect, modeRotate
44332 var keybinding = utilKeybinding('move');
44333 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];
44334 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
44335 n: entityIDs.length
44344 var _nudgeInterval; // use pointer events on supported platforms; fallback to mouse events
44347 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44349 function doMove(nudge) {
44350 nudge = nudge || [0, 0];
44353 if (_prevGraph !== context.graph()) {
44355 _origin = context.map().mouseCoordinates();
44356 fn = context.perform;
44358 fn = context.overwrite;
44361 var currMouse = context.map().mouse();
44362 var origMouse = context.projection(_origin);
44363 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
44364 fn(actionMove(entityIDs, delta, context.projection, _cache));
44365 _prevGraph = context.graph();
44368 function startNudge(nudge) {
44369 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44370 _nudgeInterval = window.setInterval(function () {
44371 context.map().pan(nudge);
44376 function stopNudge() {
44377 if (_nudgeInterval) {
44378 window.clearInterval(_nudgeInterval);
44379 _nudgeInterval = null;
44385 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
44394 function finish(d3_event) {
44395 d3_event.stopPropagation();
44396 context.replace(actionNoop(), annotation);
44397 context.enter(modeSelect(context, entityIDs));
44401 function cancel() {
44403 while (context.graph() !== baseGraph) {
44405 } // reset to baseGraph
44408 context.enter(modeBrowse(context));
44410 if (_prevGraph) context.pop(); // remove the move
44412 context.enter(modeSelect(context, entityIDs));
44418 function undone() {
44419 context.enter(modeBrowse(context));
44422 mode.enter = function () {
44423 _origin = context.map().mouseCoordinates();
44426 context.features().forceVisible(entityIDs);
44427 behaviors.forEach(context.install);
44429 context.surface().on(_pointerPrefix + 'down.modeMove', function (d3_event) {
44430 downEvent = d3_event;
44432 select(window).on(_pointerPrefix + 'move.modeMove', move, true).on(_pointerPrefix + 'up.modeMove', function (d3_event) {
44433 if (!downEvent) return;
44434 var mapNode = context.container().select('.main-map').node();
44435 var pointGetter = utilFastMouse(mapNode);
44436 var p1 = pointGetter(downEvent);
44437 var p2 = pointGetter(d3_event);
44438 var dist = geoVecLength(p1, p2);
44439 if (dist <= _tolerancePx) finish(d3_event);
44442 context.history().on('undone.modeMove', undone);
44443 keybinding.on('⎋', cancel).on('↩', finish);
44444 select(document).call(keybinding);
44447 mode.exit = function () {
44449 behaviors.forEach(function (behavior) {
44450 context.uninstall(behavior);
44452 context.surface().on(_pointerPrefix + 'down.modeMove', null);
44453 select(window).on(_pointerPrefix + 'move.modeMove', null, true).on(_pointerPrefix + 'up.modeMove', null, true);
44454 context.history().on('undone.modeMove', null);
44455 select(document).call(keybinding.unbind);
44456 context.features().forceVisible([]);
44459 mode.selectedIDs = function () {
44460 if (!arguments.length) return entityIDs; // no assign
44468 function behaviorPaste(context) {
44469 function doPaste(d3_event) {
44470 // prevent paste during low zoom selection
44471 if (!context.map().withinEditableZoom()) return;
44472 d3_event.preventDefault();
44473 var baseGraph = context.graph();
44474 var mouse = context.map().mouse();
44475 var projection = context.projection;
44476 var viewport = geoExtent(projection.clipExtent()).polygon();
44477 if (!geoPointInPolygon(mouse, viewport)) return;
44478 var oldIDs = context.copyIDs();
44479 if (!oldIDs.length) return;
44480 var extent = geoExtent();
44481 var oldGraph = context.copyGraph();
44483 var action = actionCopyEntities(oldIDs, oldGraph);
44484 context.perform(action);
44485 var copies = action.copies();
44486 var originals = new Set();
44487 Object.values(copies).forEach(function (entity) {
44488 originals.add(entity.id);
44491 for (var id in copies) {
44492 var oldEntity = oldGraph.entity(id);
44493 var newEntity = copies[id];
44495 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
44498 var parents = context.graph().parentWays(newEntity);
44499 var parentCopied = parents.some(function (parent) {
44500 return originals.has(parent.id);
44503 if (!parentCopied) {
44504 newIDs.push(newEntity.id);
44506 } // Put pasted objects where mouse pointer is..
44509 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
44510 var delta = geoVecSubtract(mouse, copyPoint);
44511 context.perform(actionMove(newIDs, delta, projection));
44512 context.enter(modeMove(context, newIDs, baseGraph));
44515 function behavior() {
44516 context.keybinding().on(uiCmd('⌘V'), doPaste);
44520 behavior.off = function () {
44521 context.keybinding().off(uiCmd('⌘V'));
44527 // `String.prototype.repeat` method
44528 // https://tc39.es/ecma262/#sec-string.prototype.repeat
44529 _export({ target: 'String', proto: true }, {
44530 repeat: stringRepeat
44534 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
44536 * The `origin` function is expected to return an [x, y] tuple rather than an
44538 * The events are `start`, `move`, and `end`.
44539 (https://github.com/mbostock/d3/issues/563)
44540 * The `start` event is not dispatched until the first cursor movement occurs.
44541 (https://github.com/mbostock/d3/pull/368)
44542 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
44543 than `x`, `y`, `dx`, and `dy` properties.
44544 * The `end` event is not dispatched if no movement occurs.
44545 * An `off` function is available that unbinds the drag's internal event handlers.
44548 function behaviorDrag() {
44549 var dispatch = dispatch$8('start', 'move', 'end'); // see also behaviorSelect
44551 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
44553 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
44555 var _origin = null;
44556 var _selector = '';
44564 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
44567 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
44569 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
44571 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
44572 var selection$1 = selection();
44573 var select = selection$1.style(d3_event_userSelectProperty);
44574 selection$1.style(d3_event_userSelectProperty, 'none');
44575 return function () {
44576 selection$1.style(d3_event_userSelectProperty, select);
44580 function pointerdown(d3_event) {
44581 if (_pointerId) return;
44582 _pointerId = d3_event.pointerId || 'mouse';
44583 _targetNode = this; // only force reflow once per drag
44585 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
44587 var startOrigin = pointerLocGetter(d3_event);
44588 var started = false;
44589 var selectEnable = d3_event_userSelectSuppress();
44590 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
44593 offset = _origin.call(_targetNode, _targetEntity);
44594 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
44599 d3_event.stopPropagation();
44601 function pointermove(d3_event) {
44602 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44603 var p = pointerLocGetter(d3_event);
44606 var dist = geoVecLength(startOrigin, p);
44607 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
44609 if (dist < tolerance) return;
44611 dispatch.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
44612 // a midpoint will convert the target to a node.
44615 d3_event.stopPropagation();
44616 d3_event.preventDefault();
44617 var dx = p[0] - startOrigin[0];
44618 var dy = p[1] - startOrigin[1];
44619 dispatch.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
44623 function pointerup(d3_event) {
44624 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
44628 dispatch.call('end', this, d3_event, _targetEntity);
44629 d3_event.preventDefault();
44632 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44637 function behavior(selection) {
44638 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
44639 var delegate = pointerdown;
44642 delegate = function delegate(d3_event) {
44644 var target = d3_event.target;
44646 for (; target && target !== root; target = target.parentNode) {
44647 var datum = target.__data__;
44648 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
44650 if (_targetEntity && target[matchesSelector](_selector)) {
44651 return pointerdown.call(target, d3_event);
44657 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
44660 behavior.off = function (selection) {
44661 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
44664 behavior.selector = function (_) {
44665 if (!arguments.length) return _selector;
44670 behavior.origin = function (_) {
44671 if (!arguments.length) return _origin;
44676 behavior.cancel = function () {
44677 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
44681 behavior.targetNode = function (_) {
44682 if (!arguments.length) return _targetNode;
44687 behavior.targetEntity = function (_) {
44688 if (!arguments.length) return _targetEntity;
44693 behavior.surface = function (_) {
44694 if (!arguments.length) return _surface;
44699 return utilRebind(behavior, dispatch, 'on');
44702 function modeDragNode(context) {
44707 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
44708 var edit = behaviorEdit(context);
44710 var _nudgeInterval;
44712 var _restoreSelectedIDs = [];
44713 var _wasMidpoint = false;
44714 var _isCancelled = false;
44722 function startNudge(d3_event, entity, nudge) {
44723 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
44724 _nudgeInterval = window.setInterval(function () {
44725 context.map().pan(nudge);
44726 doMove(d3_event, entity, nudge);
44730 function stopNudge() {
44731 if (_nudgeInterval) {
44732 window.clearInterval(_nudgeInterval);
44733 _nudgeInterval = null;
44737 function moveAnnotation(entity) {
44738 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
44741 function connectAnnotation(nodeEntity, targetEntity) {
44742 var nodeGeometry = nodeEntity.geometry(context.graph());
44743 var targetGeometry = targetEntity.geometry(context.graph());
44745 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
44746 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
44747 var targetParentWayIDs = context.graph().parentWays(targetEntity);
44748 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
44750 if (sharedParentWays.length !== 0) {
44751 // if the nodes are next to each other, they are merged
44752 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
44753 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
44756 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
44760 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
44763 function shouldSnapToNode(target) {
44764 if (!_activeEntity) return false;
44765 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
44768 function origin(entity) {
44769 return context.projection(entity.loc);
44772 function keydown(d3_event) {
44773 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44774 if (context.surface().classed('nope')) {
44775 context.surface().classed('nope-suppressed', true);
44778 context.surface().classed('nope', false).classed('nope-disabled', true);
44782 function keyup(d3_event) {
44783 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
44784 if (context.surface().classed('nope-suppressed')) {
44785 context.surface().classed('nope', true);
44788 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
44792 function start(d3_event, entity) {
44793 _wasMidpoint = entity.type === 'midpoint';
44794 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
44795 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
44797 if (_isCancelled) {
44799 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
44802 return drag.cancel();
44805 if (_wasMidpoint) {
44806 var midpoint = entity;
44807 entity = osmNode();
44808 context.perform(actionAddMidpoint(midpoint, entity));
44809 entity = context.entity(entity.id); // get post-action entity
44811 var vertex = context.surface().selectAll('.' + entity.id);
44812 drag.targetNode(vertex.node()).targetEntity(entity);
44814 context.perform(actionNoop());
44817 _activeEntity = entity;
44818 _startLoc = entity.loc;
44819 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
44820 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
44821 context.enter(mode);
44823 // - `behavior/draw.js` `datum()`
44826 function datum(d3_event) {
44827 if (!d3_event || d3_event.altKey) {
44830 // When dragging, snap only to touch targets..
44831 // (this excludes area fills and active drawing elements)
44832 var d = d3_event.target.__data__;
44833 return d && d.properties && d.properties.target ? d : {};
44837 function doMove(d3_event, entity, nudge) {
44838 nudge = nudge || [0, 0];
44839 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
44840 var currMouse = geoVecSubtract(currPoint, nudge);
44841 var loc = context.projection.invert(currMouse);
44844 if (!_nudgeInterval) {
44845 // If not nudging at the edge of the viewport, try to snap..
44847 // - `mode/drag_node.js` `doMove()`
44848 // - `behavior/draw.js` `click()`
44849 // - `behavior/draw_way.js` `move()`
44850 var d = datum(d3_event);
44851 target = d && d.properties && d.properties.entity;
44852 var targetLoc = target && target.loc;
44853 var targetNodes = d && d.properties && d.properties.nodes;
44856 // snap to node/vertex - a point target with `.loc`
44857 if (shouldSnapToNode(target)) {
44860 } else if (targetNodes) {
44861 // snap to way - a line target with `.nodes`
44862 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
44870 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
44872 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
44875 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
44876 } // Check if this drag causes the geometry to break..
44880 isInvalid = hasInvalidGeometry(entity, context.graph());
44883 var nope = context.surface().classed('nope');
44885 if (isInvalid === 'relation' || isInvalid === 'restriction') {
44887 // about to nope - show hint
44888 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
44889 relation: _mainPresetIndex.item('type/restriction').name()
44892 } else if (isInvalid) {
44893 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
44894 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
44897 // about to un-nope, remove hint
44898 context.ui().flash.duration(1).label('')();
44902 var nopeDisabled = context.surface().classed('nope-disabled');
44904 if (nopeDisabled) {
44905 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
44907 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
44911 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
44914 function hasRelationConflict(entity, target, edge, graph) {
44915 var testGraph = graph.update(); // copy
44916 // if snapping to way - add midpoint there and consider that the target..
44919 var midpoint = osmNode();
44920 var action = actionAddMidpoint({
44922 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
44924 testGraph = action(testGraph);
44926 } // can we connect to it?
44929 var ids = [entity.id, target.id];
44930 return actionConnect(ids).disabled(testGraph);
44933 function hasInvalidGeometry(entity, graph) {
44934 var parents = graph.parentWays(entity);
44937 for (i = 0; i < parents.length; i++) {
44938 var parent = parents[i];
44940 var activeIndex = null; // which multipolygon ring contains node being dragged
44941 // test any parent multipolygons for valid geometry
44943 var relations = graph.parentRelations(parent);
44945 for (j = 0; j < relations.length; j++) {
44946 if (!relations[j].isMultipolygon()) continue;
44947 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
44949 for (k = 0; k < rings.length; k++) {
44950 nodes = rings[k].nodes;
44952 if (nodes.find(function (n) {
44953 return n.id === entity.id;
44957 if (geoHasSelfIntersections(nodes, entity.id)) {
44958 return 'multipolygonMember';
44962 rings[k].coords = nodes.map(function (n) {
44965 } // test active ring for intersections with other rings in the multipolygon
44968 for (k = 0; k < rings.length; k++) {
44969 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
44971 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
44972 return 'multipolygonRing';
44975 } // If we still haven't tested this node's parent way for self-intersections.
44976 // (because it's not a member of a multipolygon), test it now.
44979 if (activeIndex === null) {
44980 nodes = parent.nodes.map(function (nodeID) {
44981 return graph.entity(nodeID);
44984 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
44985 return parent.geometry(graph);
44993 function move(d3_event, entity, point) {
44994 if (_isCancelled) return;
44995 d3_event.stopPropagation();
44996 context.surface().classed('nope-disabled', d3_event.altKey);
44997 _lastLoc = context.projection.invert(point);
44998 doMove(d3_event, entity);
44999 var nudge = geoViewportEdge(point, context.map().dimensions());
45002 startNudge(d3_event, entity, nudge);
45008 function end(d3_event, entity) {
45009 if (_isCancelled) return;
45010 var wasPoint = entity.geometry(context.graph()) === 'point';
45011 var d = datum(d3_event);
45012 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
45013 var target = d && d.properties && d.properties.entity; // entity to snap to
45017 context.perform(_actionBounceBack(entity.id, _startLoc));
45018 } else if (target && target.type === 'way') {
45019 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
45020 context.replace(actionAddMidpoint({
45022 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
45023 }, entity), connectAnnotation(entity, target));
45024 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
45025 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
45026 } else if (_wasMidpoint) {
45027 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
45029 context.replace(actionNoop(), moveAnnotation(entity));
45033 context.enter(modeSelect(context, [entity.id]));
45035 var reselection = _restoreSelectedIDs.filter(function (id) {
45036 return context.graph().hasEntity(id);
45039 if (reselection.length) {
45040 context.enter(modeSelect(context, reselection));
45042 context.enter(modeBrowse(context));
45047 function _actionBounceBack(nodeID, toLoc) {
45048 var moveNode = actionMoveNode(nodeID, toLoc);
45050 var action = function action(graph, t) {
45051 // last time through, pop off the bounceback perform.
45052 // it will then overwrite the initial perform with a moveNode that does nothing
45053 if (t === 1) context.pop();
45054 return moveNode(graph, t);
45057 action.transitionable = true;
45061 function cancel() {
45063 context.enter(modeBrowse(context));
45066 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);
45068 mode.enter = function () {
45069 context.install(hover);
45070 context.install(edit);
45071 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
45072 context.history().on('undone.drag-node', cancel);
45075 mode.exit = function () {
45076 context.ui().sidebar.hover.cancel();
45077 context.uninstall(hover);
45078 context.uninstall(edit);
45079 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
45080 context.history().on('undone.drag-node', null);
45081 _activeEntity = null;
45082 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
45086 mode.selectedIDs = function () {
45087 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
45092 mode.activeID = function () {
45093 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
45098 mode.restoreSelectedIDs = function (_) {
45099 if (!arguments.length) return _restoreSelectedIDs;
45100 _restoreSelectedIDs = _;
45104 mode.behavior = drag;
45108 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
45109 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
45110 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
45113 // `Promise.prototype.finally` method
45114 // https://tc39.es/ecma262/#sec-promise.prototype.finally
45115 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
45116 'finally': function (onFinally) {
45117 var C = speciesConstructor(this, getBuiltIn('Promise'));
45118 var isFunction = typeof onFinally == 'function';
45120 isFunction ? function (x) {
45121 return promiseResolve(C, onFinally()).then(function () { return x; });
45123 isFunction ? function (e) {
45124 return promiseResolve(C, onFinally()).then(function () { throw e; });
45130 // makes sure that native promise-based APIs `Promise#finally` properly works with patched `Promise#then`
45131 if (typeof nativePromiseConstructor == 'function') {
45132 var method = getBuiltIn('Promise').prototype['finally'];
45133 if (nativePromiseConstructor.prototype['finally'] !== method) {
45134 redefine(nativePromiseConstructor.prototype, 'finally', method, { unsafe: true });
45138 function quickselect(arr, k, left, right, compare) {
45139 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
45142 function quickselectStep(arr, k, left, right, compare) {
45143 while (right > left) {
45144 if (right - left > 600) {
45145 var n = right - left + 1;
45146 var m = k - left + 1;
45147 var z = Math.log(n);
45148 var s = 0.5 * Math.exp(2 * z / 3);
45149 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
45150 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
45151 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
45152 quickselectStep(arr, k, newLeft, newRight, compare);
45158 swap(arr, left, k);
45159 if (compare(arr[right], t) > 0) swap(arr, left, right);
45166 while (compare(arr[i], t) < 0) {
45170 while (compare(arr[j], t) > 0) {
45175 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
45177 swap(arr, j, right);
45179 if (j <= k) left = j + 1;
45180 if (k <= j) right = j - 1;
45184 function swap(arr, i, j) {
45190 function defaultCompare(a, b) {
45191 return a < b ? -1 : a > b ? 1 : 0;
45194 var RBush = /*#__PURE__*/function () {
45196 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
45198 _classCallCheck$1(this, RBush);
45200 // max entries in a node is 9 by default; min node fill is 40% for best performance
45201 this._maxEntries = Math.max(4, maxEntries);
45202 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
45206 _createClass$1(RBush, [{
45208 value: function all() {
45209 return this._all(this.data, []);
45213 value: function search(bbox) {
45214 var node = this.data;
45216 if (!intersects(bbox, node)) return result;
45217 var toBBox = this.toBBox;
45218 var nodesToSearch = [];
45221 for (var i = 0; i < node.children.length; i++) {
45222 var child = node.children[i];
45223 var childBBox = node.leaf ? toBBox(child) : child;
45225 if (intersects(bbox, childBBox)) {
45226 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
45230 node = nodesToSearch.pop();
45237 value: function collides(bbox) {
45238 var node = this.data;
45239 if (!intersects(bbox, node)) return false;
45240 var nodesToSearch = [];
45243 for (var i = 0; i < node.children.length; i++) {
45244 var child = node.children[i];
45245 var childBBox = node.leaf ? this.toBBox(child) : child;
45247 if (intersects(bbox, childBBox)) {
45248 if (node.leaf || contains(bbox, childBBox)) return true;
45249 nodesToSearch.push(child);
45253 node = nodesToSearch.pop();
45260 value: function load(data) {
45261 if (!(data && data.length)) return this;
45263 if (data.length < this._minEntries) {
45264 for (var i = 0; i < data.length; i++) {
45265 this.insert(data[i]);
45269 } // recursively build the tree with the given data from scratch using OMT algorithm
45272 var node = this._build(data.slice(), 0, data.length - 1, 0);
45274 if (!this.data.children.length) {
45275 // save as is if tree is empty
45277 } else if (this.data.height === node.height) {
45278 // split root if trees have the same height
45279 this._splitRoot(this.data, node);
45281 if (this.data.height < node.height) {
45282 // swap trees if inserted one is bigger
45283 var tmpNode = this.data;
45286 } // insert the small tree into the large tree at appropriate level
45289 this._insert(node, this.data.height - node.height - 1, true);
45296 value: function insert(item) {
45297 if (item) this._insert(item, this.data.height - 1);
45302 value: function clear() {
45303 this.data = createNode([]);
45308 value: function remove(item, equalsFn) {
45309 if (!item) return this;
45310 var node = this.data;
45311 var bbox = this.toBBox(item);
45314 var i, parent, goingUp; // depth-first iterative tree traversal
45316 while (node || path.length) {
45320 parent = path[path.length - 1];
45326 // check current node
45327 var index = findItem(item, node.children, equalsFn);
45329 if (index !== -1) {
45330 // item found, remove the item and condense tree upwards
45331 node.children.splice(index, 1);
45334 this._condense(path);
45340 if (!goingUp && !node.leaf && contains(node, bbox)) {
45346 node = node.children[0];
45347 } else if (parent) {
45350 node = parent.children[i];
45352 } else node = null; // nothing found
45360 value: function toBBox(item) {
45364 key: "compareMinX",
45365 value: function compareMinX(a, b) {
45366 return a.minX - b.minX;
45369 key: "compareMinY",
45370 value: function compareMinY(a, b) {
45371 return a.minY - b.minY;
45375 value: function toJSON() {
45380 value: function fromJSON(data) {
45386 value: function _all(node, result) {
45387 var nodesToSearch = [];
45390 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
45391 node = nodesToSearch.pop();
45398 value: function _build(items, left, right, height) {
45399 var N = right - left + 1;
45400 var M = this._maxEntries;
45404 // reached leaf level; return leaf
45405 node = createNode(items.slice(left, right + 1));
45406 calcBBox(node, this.toBBox);
45411 // target height of the bulk-loaded tree
45412 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
45414 M = Math.ceil(N / Math.pow(M, height - 1));
45417 node = createNode([]);
45419 node.height = height; // split the items into M mostly square tiles
45421 var N2 = Math.ceil(N / M);
45422 var N1 = N2 * Math.ceil(Math.sqrt(M));
45423 multiSelect(items, left, right, N1, this.compareMinX);
45425 for (var i = left; i <= right; i += N1) {
45426 var right2 = Math.min(i + N1 - 1, right);
45427 multiSelect(items, i, right2, N2, this.compareMinY);
45429 for (var j = i; j <= right2; j += N2) {
45430 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
45432 node.children.push(this._build(items, j, right3, height - 1));
45436 calcBBox(node, this.toBBox);
45440 key: "_chooseSubtree",
45441 value: function _chooseSubtree(bbox, node, level, path) {
45444 if (node.leaf || path.length - 1 === level) break;
45445 var minArea = Infinity;
45446 var minEnlargement = Infinity;
45447 var targetNode = void 0;
45449 for (var i = 0; i < node.children.length; i++) {
45450 var child = node.children[i];
45451 var area = bboxArea(child);
45452 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
45454 if (enlargement < minEnlargement) {
45455 minEnlargement = enlargement;
45456 minArea = area < minArea ? area : minArea;
45457 targetNode = child;
45458 } else if (enlargement === minEnlargement) {
45459 // otherwise choose one with the smallest area
45460 if (area < minArea) {
45462 targetNode = child;
45467 node = targetNode || node.children[0];
45474 value: function _insert(item, level, isNode) {
45475 var bbox = isNode ? item : this.toBBox(item);
45476 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
45478 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
45481 node.children.push(item);
45482 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
45484 while (level >= 0) {
45485 if (insertPath[level].children.length > this._maxEntries) {
45486 this._split(insertPath, level);
45490 } // adjust bboxes along the insertion path
45493 this._adjustParentBBoxes(bbox, insertPath, level);
45494 } // split overflowed node into two
45498 value: function _split(insertPath, level) {
45499 var node = insertPath[level];
45500 var M = node.children.length;
45501 var m = this._minEntries;
45503 this._chooseSplitAxis(node, m, M);
45505 var splitIndex = this._chooseSplitIndex(node, m, M);
45507 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
45508 newNode.height = node.height;
45509 newNode.leaf = node.leaf;
45510 calcBBox(node, this.toBBox);
45511 calcBBox(newNode, this.toBBox);
45512 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
45516 value: function _splitRoot(node, newNode) {
45518 this.data = createNode([node, newNode]);
45519 this.data.height = node.height + 1;
45520 this.data.leaf = false;
45521 calcBBox(this.data, this.toBBox);
45524 key: "_chooseSplitIndex",
45525 value: function _chooseSplitIndex(node, m, M) {
45527 var minOverlap = Infinity;
45528 var minArea = Infinity;
45530 for (var i = m; i <= M - m; i++) {
45531 var bbox1 = distBBox(node, 0, i, this.toBBox);
45532 var bbox2 = distBBox(node, i, M, this.toBBox);
45533 var overlap = intersectionArea(bbox1, bbox2);
45534 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
45536 if (overlap < minOverlap) {
45537 minOverlap = overlap;
45539 minArea = area < minArea ? area : minArea;
45540 } else if (overlap === minOverlap) {
45541 // otherwise choose distribution with minimum area
45542 if (area < minArea) {
45549 return index || M - m;
45550 } // sorts node children by the best axis for split
45553 key: "_chooseSplitAxis",
45554 value: function _chooseSplitAxis(node, m, M) {
45555 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
45556 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
45558 var xMargin = this._allDistMargin(node, m, M, compareMinX);
45560 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
45561 // otherwise it's already sorted by minY
45564 if (xMargin < yMargin) node.children.sort(compareMinX);
45565 } // total margin of all possible split distributions where each node is at least m full
45568 key: "_allDistMargin",
45569 value: function _allDistMargin(node, m, M, compare) {
45570 node.children.sort(compare);
45571 var toBBox = this.toBBox;
45572 var leftBBox = distBBox(node, 0, m, toBBox);
45573 var rightBBox = distBBox(node, M - m, M, toBBox);
45574 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
45576 for (var i = m; i < M - m; i++) {
45577 var child = node.children[i];
45578 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
45579 margin += bboxMargin(leftBBox);
45582 for (var _i = M - m - 1; _i >= m; _i--) {
45583 var _child = node.children[_i];
45584 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
45585 margin += bboxMargin(rightBBox);
45591 key: "_adjustParentBBoxes",
45592 value: function _adjustParentBBoxes(bbox, path, level) {
45593 // adjust bboxes along the given tree path
45594 for (var i = level; i >= 0; i--) {
45595 extend$1(path[i], bbox);
45600 value: function _condense(path) {
45601 // go through the path, removing empty nodes and updating bboxes
45602 for (var i = path.length - 1, siblings; i >= 0; i--) {
45603 if (path[i].children.length === 0) {
45605 siblings = path[i - 1].children;
45606 siblings.splice(siblings.indexOf(path[i]), 1);
45607 } else this.clear();
45608 } else calcBBox(path[i], this.toBBox);
45616 function findItem(item, items, equalsFn) {
45617 if (!equalsFn) return items.indexOf(item);
45619 for (var i = 0; i < items.length; i++) {
45620 if (equalsFn(item, items[i])) return i;
45624 } // calculate node's bbox from bboxes of its children
45627 function calcBBox(node, toBBox) {
45628 distBBox(node, 0, node.children.length, toBBox, node);
45629 } // min bounding rectangle of node children from k to p-1
45632 function distBBox(node, k, p, toBBox, destNode) {
45633 if (!destNode) destNode = createNode(null);
45634 destNode.minX = Infinity;
45635 destNode.minY = Infinity;
45636 destNode.maxX = -Infinity;
45637 destNode.maxY = -Infinity;
45639 for (var i = k; i < p; i++) {
45640 var child = node.children[i];
45641 extend$1(destNode, node.leaf ? toBBox(child) : child);
45647 function extend$1(a, b) {
45648 a.minX = Math.min(a.minX, b.minX);
45649 a.minY = Math.min(a.minY, b.minY);
45650 a.maxX = Math.max(a.maxX, b.maxX);
45651 a.maxY = Math.max(a.maxY, b.maxY);
45655 function compareNodeMinX(a, b) {
45656 return a.minX - b.minX;
45659 function compareNodeMinY(a, b) {
45660 return a.minY - b.minY;
45663 function bboxArea(a) {
45664 return (a.maxX - a.minX) * (a.maxY - a.minY);
45667 function bboxMargin(a) {
45668 return a.maxX - a.minX + (a.maxY - a.minY);
45671 function enlargedArea(a, b) {
45672 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));
45675 function intersectionArea(a, b) {
45676 var minX = Math.max(a.minX, b.minX);
45677 var minY = Math.max(a.minY, b.minY);
45678 var maxX = Math.min(a.maxX, b.maxX);
45679 var maxY = Math.min(a.maxY, b.maxY);
45680 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
45683 function contains(a, b) {
45684 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
45687 function intersects(a, b) {
45688 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
45691 function createNode(children) {
45693 children: children,
45701 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
45702 // combines selection algorithm with binary divide & conquer approach
45705 function multiSelect(arr, left, right, n, compare) {
45706 var stack = [left, right];
45708 while (stack.length) {
45709 right = stack.pop();
45710 left = stack.pop();
45711 if (right - left <= n) continue;
45712 var mid = left + Math.ceil((right - left) / n / 2) * n;
45713 quickselect(arr, mid, left, right, compare);
45714 stack.push(left, mid, mid, right);
45718 function responseText(response) {
45719 if (!response.ok) throw new Error(response.status + " " + response.statusText);
45720 return response.text();
45723 function d3_text (input, init) {
45724 return fetch(input, init).then(responseText);
45727 function responseJson(response) {
45728 if (!response.ok) throw new Error(response.status + " " + response.statusText);
45729 if (response.status === 204 || response.status === 205) return;
45730 return response.json();
45733 function d3_json (input, init) {
45734 return fetch(input, init).then(responseJson);
45737 function parser(type) {
45738 return function (input, init) {
45739 return d3_text(input, init).then(function (text) {
45740 return new DOMParser().parseFromString(text, type);
45745 var d3_xml = parser("application/xml");
45746 var svg = parser("image/svg+xml");
45748 var tiler$6 = utilTiler();
45749 var dispatch$7 = dispatch$8('loaded');
45750 var _tileZoom$3 = 14;
45751 var _krUrlRoot = 'https://www.keepright.at';
45754 localizeStrings: {}
45755 }; // This gets reassigned if reset
45759 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
45760 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];
45762 function abortRequest$6(controller) {
45764 controller.abort();
45768 function abortUnwantedRequests$3(cache, tiles) {
45769 Object.keys(cache.inflightTile).forEach(function (k) {
45770 var wanted = tiles.find(function (tile) {
45771 return k === tile.id;
45775 abortRequest$6(cache.inflightTile[k]);
45776 delete cache.inflightTile[k];
45781 function encodeIssueRtree$2(d) {
45789 } // Replace or remove QAItem from rtree
45792 function updateRtree$3(item, replace) {
45793 _cache$2.rtree.remove(item, function (a, b) {
45794 return a.data.id === b.data.id;
45798 _cache$2.rtree.insert(item);
45802 function tokenReplacements(d) {
45803 if (!(d instanceof QAItem)) return;
45804 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
45805 var replacements = {};
45806 var issueTemplate = _krData.errorTypes[d.whichType];
45808 if (!issueTemplate) {
45809 /* eslint-disable no-console */
45810 console.log('No Template: ', d.whichType);
45811 console.log(' ', d.description);
45812 /* eslint-enable no-console */
45815 } // some descriptions are just fixed text
45818 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
45820 var errorRegex = new RegExp(issueTemplate.regex, 'i');
45821 var errorMatch = errorRegex.exec(d.description);
45824 /* eslint-disable no-console */
45825 console.log('Unmatched: ', d.whichType);
45826 console.log(' ', d.description);
45827 console.log(' ', errorRegex);
45828 /* eslint-enable no-console */
45833 for (var i = 1; i < errorMatch.length; i++) {
45835 var capture = errorMatch[i];
45836 var idType = void 0;
45837 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
45839 if (idType && capture) {
45840 // link IDs if present in the capture
45841 capture = parseError(capture, idType);
45842 } else if (htmlRegex.test(capture)) {
45843 // escape any html in non-IDs
45844 capture = '\\' + capture + '\\';
45846 var compare = capture.toLowerCase();
45848 if (_krData.localizeStrings[compare]) {
45849 // some replacement strings can be localized
45850 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45854 replacements['var' + i] = capture;
45857 return replacements;
45860 function parseError(capture, idType) {
45861 var compare = capture.toLowerCase();
45863 if (_krData.localizeStrings[compare]) {
45864 // some replacement strings can be localized
45865 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
45869 // link a string like "this node"
45871 capture = linkErrorObject(capture);
45875 capture = linkURL(capture);
45877 // link an entity ID
45882 capture = linkEntity(idType + capture);
45884 // some errors have more complex ID lists/variance
45887 capture = parse20(capture);
45891 capture = parse211(capture);
45895 capture = parse231(capture);
45899 capture = parse294(capture);
45903 capture = parse370(capture);
45909 function linkErrorObject(d) {
45910 return "<a class=\"error_object_link\">".concat(d, "</a>");
45913 function linkEntity(d) {
45914 return "<a class=\"error_entity_link\">".concat(d, "</a>");
45917 function linkURL(d) {
45918 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
45919 } // arbitrary node list of form: #ID, #ID, #ID...
45922 function parse211(capture) {
45924 var items = capture.split(', ');
45925 items.forEach(function (item) {
45926 // ID has # at the front
45927 var id = linkEntity('n' + item.slice(1));
45930 return newList.join(', ');
45931 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
45934 function parse231(capture) {
45935 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
45937 var items = capture.split('),');
45938 items.forEach(function (item) {
45939 var match = item.match(/\#(\d+)\((.+)\)?/);
45941 if (match !== null && match.length > 2) {
45942 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
45947 return newList.join(', ');
45948 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
45951 function parse294(capture) {
45953 var items = capture.split(',');
45954 items.forEach(function (item) {
45955 // item of form "from/to node/relation #ID"
45956 item = item.split(' '); // to/from role is more clear in quotes
45958 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
45960 var idType = item[1].slice(0, 1); // ID has # at the front
45962 var id = item[2].slice(1);
45963 id = linkEntity(idType + id);
45964 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
45966 return newList.join(', ');
45967 } // may or may not include the string "(including the name 'name')"
45970 function parse370(capture) {
45971 if (!capture) return '';
45972 var match = capture.match(/\(including the name (\'.+\')\)/);
45974 if (match && match.length) {
45975 return _t('QA.keepRight.errorTypes.370.including_the_name', {
45981 } // arbitrary node list of form: #ID,#ID,#ID...
45984 function parse20(capture) {
45986 var items = capture.split(',');
45987 items.forEach(function (item) {
45988 // ID has # at the front
45989 var id = linkEntity('n' + item.slice(1));
45992 return newList.join(', ');
45996 var serviceKeepRight = {
45997 title: 'keepRight',
45998 init: function init() {
45999 _mainFileFetcher.get('keepRight').then(function (d) {
46000 return _krData = d;
46007 this.event = utilRebind(this, dispatch$7, 'on');
46009 reset: function reset() {
46011 Object.values(_cache$2.inflightTile).forEach(abortRequest$6);
46023 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
46024 loadIssues: function loadIssues(projection) {
46030 }; // determine the needed tiles to cover the view
46032 var tiles = tiler$6.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
46034 abortUnwantedRequests$3(_cache$2, tiles); // issue new requests..
46036 tiles.forEach(function (tile) {
46037 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
46039 var _tile$extent$rectangl = tile.extent.rectangle(),
46040 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46041 left = _tile$extent$rectangl2[0],
46042 top = _tile$extent$rectangl2[1],
46043 right = _tile$extent$rectangl2[2],
46044 bottom = _tile$extent$rectangl2[3];
46046 var params = Object.assign({}, options, {
46052 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
46053 var controller = new AbortController();
46054 _cache$2.inflightTile[tile.id] = controller;
46056 signal: controller.signal
46057 }).then(function (data) {
46058 delete _cache$2.inflightTile[tile.id];
46059 _cache$2.loadedTile[tile.id] = true;
46061 if (!data || !data.features || !data.features.length) {
46062 throw new Error('No Data');
46065 data.features.forEach(function (feature) {
46066 var _feature$properties = feature.properties,
46067 itemType = _feature$properties.error_type,
46068 id = _feature$properties.error_id,
46069 _feature$properties$c = _feature$properties.comment,
46070 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
46071 objectId = _feature$properties.object_id,
46072 objectType = _feature$properties.object_type,
46073 schema = _feature$properties.schema,
46074 title = _feature$properties.title;
46075 var loc = feature.geometry.coordinates,
46076 _feature$properties$d = feature.properties.description,
46077 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
46078 // Error 191 = "highway-highway"
46079 // Error 190 = "intersections without junctions" (parent)
46081 var issueTemplate = _krData.errorTypes[itemType];
46082 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
46084 var whichType = issueTemplate ? itemType : parentIssueType;
46085 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
46086 // This is done to make them easier to linkify and translate.
46088 switch (whichType) {
46090 description = "This feature has a FIXME tag: ".concat(description);
46095 description = description.replace('A turn-', 'This turn-');
46103 description = "This turn-restriction~".concat(description);
46107 description = 'This highway is missing a maxspeed tag';
46113 description = "This feature~".concat(description);
46115 } // move markers slightly so it doesn't obscure the geometry,
46116 // then move markers away from other coincident markers
46119 var coincident = false;
46122 // first time, move marker up. after that, move marker right.
46123 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
46124 loc = geoVecAdd(loc, delta);
46125 var bbox = geoExtent(loc).bbox();
46126 coincident = _cache$2.rtree.search(bbox).length;
46127 } while (coincident);
46129 var d = new QAItem(loc, _this, itemType, id, {
46131 description: description,
46132 whichType: whichType,
46133 parentIssueType: parentIssueType,
46134 severity: whichTemplate.severity || 'error',
46135 objectId: objectId,
46136 objectType: objectType,
46140 d.replacements = tokenReplacements(d);
46141 _cache$2.data[id] = d;
46143 _cache$2.rtree.insert(encodeIssueRtree$2(d));
46145 dispatch$7.call('loaded');
46146 })["catch"](function () {
46147 delete _cache$2.inflightTile[tile.id];
46148 _cache$2.loadedTile[tile.id] = true;
46152 postUpdate: function postUpdate(d, callback) {
46155 if (_cache$2.inflightPost[d.id]) {
46157 message: 'Error update already inflight',
46168 params.st = d.newStatus;
46171 if (d.newComment !== undefined) {
46172 params.co = d.newComment;
46173 } // NOTE: This throws a CORS err, but it seems successful.
46174 // We don't care too much about the response, so this is fine.
46177 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
46178 var controller = new AbortController();
46179 _cache$2.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
46180 // (worst case scenario the request truly fails and issue will show up if iD restarts)
46183 signal: controller.signal
46184 })["finally"](function () {
46185 delete _cache$2.inflightPost[d.id];
46187 if (d.newStatus === 'ignore') {
46188 // ignore permanently (false positive)
46189 _this2.removeItem(d);
46190 } else if (d.newStatus === 'ignore_t') {
46191 // ignore temporarily (error fixed)
46192 _this2.removeItem(d);
46194 _cache$2.closed["".concat(d.schema, ":").concat(d.id)] = true;
46196 d = _this2.replaceItem(d.update({
46197 comment: d.newComment,
46198 newComment: undefined,
46199 newState: undefined
46203 if (callback) callback(null, d);
46206 // Get all cached QAItems covering the viewport
46207 getItems: function getItems(projection) {
46208 var viewport = projection.clipExtent();
46209 var min = [viewport[0][0], viewport[1][1]];
46210 var max = [viewport[1][0], viewport[0][1]];
46211 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46212 return _cache$2.rtree.search(bbox).map(function (d) {
46216 // Get a QAItem from cache
46217 // NOTE: Don't change method name until UI v3 is merged
46218 getError: function getError(id) {
46219 return _cache$2.data[id];
46221 // Replace a single QAItem in the cache
46222 replaceItem: function replaceItem(item) {
46223 if (!(item instanceof QAItem) || !item.id) return;
46224 _cache$2.data[item.id] = item;
46225 updateRtree$3(encodeIssueRtree$2(item), true); // true = replace
46229 // Remove a single QAItem from the cache
46230 removeItem: function removeItem(item) {
46231 if (!(item instanceof QAItem) || !item.id) return;
46232 delete _cache$2.data[item.id];
46233 updateRtree$3(encodeIssueRtree$2(item), false); // false = remove
46235 issueURL: function issueURL(item) {
46236 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
46238 // Get an array of issues closed during this session.
46239 // Used to populate `closed:keepright` changeset tag
46240 getClosedIDs: function getClosedIDs() {
46241 return Object.keys(_cache$2.closed).sort();
46245 var tiler$5 = utilTiler();
46246 var dispatch$6 = dispatch$8('loaded');
46247 var _tileZoom$2 = 14;
46248 var _impOsmUrls = {
46249 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
46250 mr: 'https://grab.community.improve-osm.org/missingGeoService',
46251 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
46253 var _impOsmData = {
46255 }; // This gets reassigned if reset
46259 function abortRequest$5(i) {
46260 Object.values(i).forEach(function (controller) {
46262 controller.abort();
46267 function abortUnwantedRequests$2(cache, tiles) {
46268 Object.keys(cache.inflightTile).forEach(function (k) {
46269 var wanted = tiles.find(function (tile) {
46270 return k === tile.id;
46274 abortRequest$5(cache.inflightTile[k]);
46275 delete cache.inflightTile[k];
46280 function encodeIssueRtree$1(d) {
46288 } // Replace or remove QAItem from rtree
46291 function updateRtree$2(item, replace) {
46292 _cache$1.rtree.remove(item, function (a, b) {
46293 return a.data.id === b.data.id;
46297 _cache$1.rtree.insert(item);
46301 function linkErrorObject(d) {
46302 return "<a class=\"error_object_link\">".concat(d, "</a>");
46305 function linkEntity(d) {
46306 return "<a class=\"error_entity_link\">".concat(d, "</a>");
46309 function pointAverage(points) {
46310 if (points.length) {
46311 var sum = points.reduce(function (acc, point) {
46312 return geoVecAdd(acc, [point.lon, point.lat]);
46314 return geoVecScale(sum, 1 / points.length);
46320 function relativeBearing(p1, p2) {
46321 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
46324 angle += 2 * Math.PI;
46325 } // Return degrees
46328 return angle * 180 / Math.PI;
46329 } // Assuming range [0,360)
46332 function cardinalDirection(bearing) {
46333 var dir = 45 * Math.round(bearing / 45);
46345 return _t("QA.improveOSM.directions.".concat(compass[dir]));
46346 } // Errors shouldn't obscure each other
46349 function preventCoincident$1(loc, bumpUp) {
46350 var coincident = false;
46353 // first time, move marker up. after that, move marker right.
46354 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
46355 loc = geoVecAdd(loc, delta);
46356 var bbox = geoExtent(loc).bbox();
46357 coincident = _cache$1.rtree.search(bbox).length;
46358 } while (coincident);
46363 var serviceImproveOSM = {
46364 title: 'improveOSM',
46365 init: function init() {
46366 _mainFileFetcher.get('qa_data').then(function (d) {
46367 return _impOsmData = d.improveOSM;
46374 this.event = utilRebind(this, dispatch$6, 'on');
46376 reset: function reset() {
46378 Object.values(_cache$1.inflightTile).forEach(abortRequest$5);
46390 loadIssues: function loadIssues(projection) {
46396 zoom: '19' // Use a high zoom so that clusters aren't returned
46398 }; // determine the needed tiles to cover the view
46400 var tiles = tiler$5.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
46402 abortUnwantedRequests$2(_cache$1, tiles); // issue new requests..
46404 tiles.forEach(function (tile) {
46405 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
46407 var _tile$extent$rectangl = tile.extent.rectangle(),
46408 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
46409 east = _tile$extent$rectangl2[0],
46410 north = _tile$extent$rectangl2[1],
46411 west = _tile$extent$rectangl2[2],
46412 south = _tile$extent$rectangl2[3];
46414 var params = Object.assign({}, options, {
46419 }); // 3 separate requests to store for each tile
46422 Object.keys(_impOsmUrls).forEach(function (k) {
46423 // We exclude WATER from missing geometry as it doesn't seem useful
46424 // We use most confident one-way and turn restrictions only, still have false positives
46425 var kParams = Object.assign({}, params, k === 'mr' ? {
46426 type: 'PARKING,ROAD,BOTH,PATH'
46428 confidenceLevel: 'C1'
46430 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
46431 var controller = new AbortController();
46432 requests[k] = controller;
46434 signal: controller.signal
46435 }).then(function (data) {
46436 delete _cache$1.inflightTile[tile.id][k];
46438 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46439 delete _cache$1.inflightTile[tile.id];
46440 _cache$1.loadedTile[tile.id] = true;
46441 } // Road segments at high zoom == oneways
46444 if (data.roadSegments) {
46445 data.roadSegments.forEach(function (feature) {
46446 // Position error at the approximate middle of the segment
46447 var points = feature.points,
46448 wayId = feature.wayId,
46449 fromNodeId = feature.fromNodeId,
46450 toNodeId = feature.toNodeId;
46451 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
46452 var mid = points.length / 2;
46453 var loc; // Even number of points, find midpoint of the middle two
46454 // Odd number of points, use position of very middle point
46456 if (mid % 1 === 0) {
46457 loc = pointAverage([points[mid - 1], points[mid]]);
46459 mid = points[Math.floor(mid)];
46460 loc = [mid.lon, mid.lat];
46461 } // One-ways can land on same segment in opposite direction
46464 loc = preventCoincident$1(loc, false);
46465 var d = new QAItem(loc, _this, k, itemId, {
46467 // used as a category
46469 // used to post changes
46471 fromNodeId: fromNodeId,
46476 }); // Variables used in the description
46479 percentage: feature.percentOfTrips,
46480 num_trips: feature.numberOfTrips,
46481 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
46482 from_node: linkEntity('n' + feature.fromNodeId),
46483 to_node: linkEntity('n' + feature.toNodeId)
46485 _cache$1.data[d.id] = d;
46487 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46489 } // Tiles at high zoom == missing roads
46493 data.tiles.forEach(function (feature) {
46494 var type = feature.type,
46497 numberOfTrips = feature.numberOfTrips;
46498 var geoType = type.toLowerCase();
46499 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
46500 // Missing geometry could happen to land on another error
46502 var loc = pointAverage(feature.points);
46503 loc = preventCoincident$1(loc, false);
46504 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
46512 num_trips: numberOfTrips,
46513 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
46514 }; // -1 trips indicates data came from a 3rd party
46516 if (numberOfTrips === -1) {
46517 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
46520 _cache$1.data[d.id] = d;
46522 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46524 } // Entities at high zoom == turn restrictions
46527 if (data.entities) {
46528 data.entities.forEach(function (feature) {
46529 var point = feature.point,
46531 segments = feature.segments,
46532 numberOfPasses = feature.numberOfPasses,
46533 turnType = feature.turnType;
46534 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
46535 // We also want to bump the error up so node is accessible
46537 var loc = preventCoincident$1([point.lon, point.lat], true); // Elements are presented in a strange way
46539 var ids = id.split(',');
46540 var from_way = ids[0];
46541 var via_node = ids[3];
46542 var to_way = ids[2].split(':')[1];
46543 var d = new QAItem(loc, _this, k, itemId, {
46546 objectId: via_node,
46548 }); // Travel direction along from_way clarifies the turn restriction
46550 var _segments$0$points = _slicedToArray(segments[0].points, 2),
46551 p1 = _segments$0$points[0],
46552 p2 = _segments$0$points[1];
46554 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
46557 num_passed: numberOfPasses,
46558 num_trips: segments[0].numberOfTrips,
46559 turn_restriction: turnType.toLowerCase(),
46560 from_way: linkEntity('w' + from_way),
46561 to_way: linkEntity('w' + to_way),
46562 travel_direction: dir_of_travel,
46563 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
46565 _cache$1.data[d.id] = d;
46567 _cache$1.rtree.insert(encodeIssueRtree$1(d));
46569 dispatch$6.call('loaded');
46572 })["catch"](function () {
46573 delete _cache$1.inflightTile[tile.id][k];
46575 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
46576 delete _cache$1.inflightTile[tile.id];
46577 _cache$1.loadedTile[tile.id] = true;
46581 _cache$1.inflightTile[tile.id] = requests;
46584 getComments: function getComments(item) {
46587 // If comments already retrieved no need to do so again
46588 if (item.comments) {
46589 return Promise.resolve(item);
46592 var key = item.issueKey;
46595 if (key === 'ow') {
46596 qParams = item.identifier;
46597 } else if (key === 'mr') {
46598 qParams.tileX = item.identifier.x;
46599 qParams.tileY = item.identifier.y;
46600 } else if (key === 'tr') {
46601 qParams.targetId = item.identifier;
46604 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
46606 var cacheComments = function cacheComments(data) {
46607 // Assign directly for immediate use afterwards
46608 // comments are served newest to oldest
46609 item.comments = data.comments ? data.comments.reverse() : [];
46611 _this2.replaceItem(item);
46614 return d3_json(url).then(cacheComments).then(function () {
46618 postUpdate: function postUpdate(d, callback) {
46619 if (!serviceOsm.authenticated()) {
46620 // Username required in payload
46622 message: 'Not Authenticated',
46627 if (_cache$1.inflightPost[d.id]) {
46629 message: 'Error update already inflight',
46632 } // Payload can only be sent once username is established
46635 serviceOsm.userDetails(sendPayload.bind(this));
46637 function sendPayload(err, user) {
46641 return callback(err, d);
46644 var key = d.issueKey;
46645 var url = "".concat(_impOsmUrls[key], "/comment");
46647 username: user.display_name,
46648 targetIds: [d.identifier]
46652 payload.status = d.newStatus;
46653 payload.text = 'status changed';
46654 } // Comment take place of default text
46657 if (d.newComment) {
46658 payload.text = d.newComment;
46661 var controller = new AbortController();
46662 _cache$1.inflightPost[d.id] = controller;
46665 signal: controller.signal,
46666 body: JSON.stringify(payload)
46668 d3_json(url, options).then(function () {
46669 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
46671 if (!d.newStatus) {
46672 var now = new Date();
46673 var comments = d.comments ? d.comments : [];
46675 username: payload.username,
46676 text: payload.text,
46677 timestamp: now.getTime() / 1000
46680 _this3.replaceItem(d.update({
46681 comments: comments,
46682 newComment: undefined
46685 _this3.removeItem(d);
46687 if (d.newStatus === 'SOLVED') {
46688 // Keep track of the number of issues closed per type to tag the changeset
46689 if (!(d.issueKey in _cache$1.closed)) {
46690 _cache$1.closed[d.issueKey] = 0;
46693 _cache$1.closed[d.issueKey] += 1;
46697 if (callback) callback(null, d);
46698 })["catch"](function (err) {
46699 delete _cache$1.inflightPost[d.id];
46700 if (callback) callback(err.message);
46704 // Get all cached QAItems covering the viewport
46705 getItems: function getItems(projection) {
46706 var viewport = projection.clipExtent();
46707 var min = [viewport[0][0], viewport[1][1]];
46708 var max = [viewport[1][0], viewport[0][1]];
46709 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46710 return _cache$1.rtree.search(bbox).map(function (d) {
46714 // Get a QAItem from cache
46715 // NOTE: Don't change method name until UI v3 is merged
46716 getError: function getError(id) {
46717 return _cache$1.data[id];
46719 // get the name of the icon to display for this item
46720 getIcon: function getIcon(itemType) {
46721 return _impOsmData.icons[itemType];
46723 // Replace a single QAItem in the cache
46724 replaceItem: function replaceItem(issue) {
46725 if (!(issue instanceof QAItem) || !issue.id) return;
46726 _cache$1.data[issue.id] = issue;
46727 updateRtree$2(encodeIssueRtree$1(issue), true); // true = replace
46731 // Remove a single QAItem from the cache
46732 removeItem: function removeItem(issue) {
46733 if (!(issue instanceof QAItem) || !issue.id) return;
46734 delete _cache$1.data[issue.id];
46735 updateRtree$2(encodeIssueRtree$1(issue), false); // false = remove
46737 // Used to populate `closed:improveosm:*` changeset tags
46738 getClosedCounts: function getClosedCounts() {
46739 return _cache$1.closed;
46743 var defaults$5 = createCommonjsModule(function (module) {
46744 function getDefaults() {
46752 langPrefix: 'language-',
46760 smartypants: false,
46767 function changeDefaults(newDefaults) {
46768 module.exports.defaults = newDefaults;
46772 defaults: getDefaults(),
46773 getDefaults: getDefaults,
46774 changeDefaults: changeDefaults
46781 var escapeTest = /[&<>"']/;
46782 var escapeReplace = /[&<>"']/g;
46783 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
46784 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
46785 var escapeReplacements = {
46793 var getEscapeReplacement = function getEscapeReplacement(ch) {
46794 return escapeReplacements[ch];
46797 function escape$3(html, encode) {
46799 if (escapeTest.test(html)) {
46800 return html.replace(escapeReplace, getEscapeReplacement);
46803 if (escapeTestNoEncode.test(html)) {
46804 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
46811 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
46813 function unescape$2(html) {
46814 // explicitly match decimal, hex, and named HTML entities
46815 return html.replace(unescapeTest, function (_, n) {
46816 n = n.toLowerCase();
46817 if (n === 'colon') return ':';
46819 if (n.charAt(0) === '#') {
46820 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
46827 var caret = /(^|[^\[])\^/g;
46829 function edit$1(regex, opt) {
46830 regex = regex.source || regex;
46833 replace: function replace(name, val) {
46834 val = val.source || val;
46835 val = val.replace(caret, '$1');
46836 regex = regex.replace(name, val);
46839 getRegex: function getRegex() {
46840 return new RegExp(regex, opt);
46846 var nonWordAndColonTest = /[^\w:]/g;
46847 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
46849 function cleanUrl$1(sanitize, base, href) {
46854 prot = decodeURIComponent(unescape$2(href)).replace(nonWordAndColonTest, '').toLowerCase();
46859 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
46864 if (base && !originIndependentUrl.test(href)) {
46865 href = resolveUrl$1(base, href);
46869 href = encodeURI(href).replace(/%25/g, '%');
46878 var justDomain = /^[^:]+:\/*[^/]*$/;
46879 var protocol = /^([^:]+:)[\s\S]*$/;
46880 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
46882 function resolveUrl$1(base, href) {
46883 if (!baseUrls[' ' + base]) {
46884 // we can ignore everything in base after the last slash of its path component,
46885 // but we might need to add _that_
46886 // https://tools.ietf.org/html/rfc3986#section-3
46887 if (justDomain.test(base)) {
46888 baseUrls[' ' + base] = base + '/';
46890 baseUrls[' ' + base] = rtrim$1(base, '/', true);
46894 base = baseUrls[' ' + base];
46895 var relativeBase = base.indexOf(':') === -1;
46897 if (href.substring(0, 2) === '//') {
46898 if (relativeBase) {
46902 return base.replace(protocol, '$1') + href;
46903 } else if (href.charAt(0) === '/') {
46904 if (relativeBase) {
46908 return base.replace(domain, '$1') + href;
46910 return base + href;
46915 exec: function noopTest() {}
46918 function merge$2(obj) {
46923 for (; i < arguments.length; i++) {
46924 target = arguments[i];
46926 for (key in target) {
46927 if (Object.prototype.hasOwnProperty.call(target, key)) {
46928 obj[key] = target[key];
46936 function splitCells$1(tableRow, count) {
46937 // ensure that every cell-delimiting pipe has a space
46938 // before it to distinguish it from an escaped pipe
46939 var row = tableRow.replace(/\|/g, function (match, offset, str) {
46940 var escaped = false,
46943 while (--curr >= 0 && str[curr] === '\\') {
46944 escaped = !escaped;
46948 // odd number of slashes means | is escaped
46949 // so we leave it alone
46952 // add space before unescaped |
46956 cells = row.split(/ \|/);
46959 if (cells.length > count) {
46960 cells.splice(count);
46962 while (cells.length < count) {
46967 for (; i < cells.length; i++) {
46968 // leading or trailing whitespace is ignored per the gfm spec
46969 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
46973 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
46974 // /c*$/ is vulnerable to REDOS.
46975 // invert: Remove suffix of non-c chars instead. Default falsey.
46978 function rtrim$1(str, c, invert) {
46979 var l = str.length;
46983 } // Length of suffix matching the invert condition.
46986 var suffLen = 0; // Step left until we fail to match the invert condition.
46988 while (suffLen < l) {
46989 var currChar = str.charAt(l - suffLen - 1);
46991 if (currChar === c && !invert) {
46993 } else if (currChar !== c && invert) {
47000 return str.substr(0, l - suffLen);
47003 function findClosingBracket$1(str, b) {
47004 if (str.indexOf(b[1]) === -1) {
47008 var l = str.length;
47012 for (; i < l; i++) {
47013 if (str[i] === '\\') {
47015 } else if (str[i] === b[0]) {
47017 } else if (str[i] === b[1]) {
47029 function checkSanitizeDeprecation$1(opt) {
47030 if (opt && opt.sanitize && !opt.silent) {
47031 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');
47033 } // copied from https://stackoverflow.com/a/5450113/806777
47036 function repeatString$1(pattern, count) {
47043 while (count > 1) {
47049 pattern += pattern;
47052 return result + pattern;
47057 unescape: unescape$2,
47059 cleanUrl: cleanUrl$1,
47060 resolveUrl: resolveUrl$1,
47061 noopTest: noopTest$1,
47063 splitCells: splitCells$1,
47065 findClosingBracket: findClosingBracket$1,
47066 checkSanitizeDeprecation: checkSanitizeDeprecation$1,
47067 repeatString: repeatString$1
47070 var defaults$4 = defaults$5.defaults;
47071 var rtrim = helpers.rtrim,
47072 splitCells = helpers.splitCells,
47073 _escape = helpers.escape,
47074 findClosingBracket = helpers.findClosingBracket;
47076 function outputLink(cap, link, raw) {
47077 var href = link.href;
47078 var title = link.title ? _escape(link.title) : null;
47079 var text = cap[1].replace(/\\([\[\]])/g, '$1');
47081 if (cap[0].charAt(0) !== '!') {
47095 text: _escape(text)
47100 function indentCodeCompensation(raw, text) {
47101 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
47103 if (matchIndentToCode === null) {
47107 var indentToCode = matchIndentToCode[1];
47108 return text.split('\n').map(function (node) {
47109 var matchIndentInNode = node.match(/^\s+/);
47111 if (matchIndentInNode === null) {
47115 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
47116 indentInNode = _matchIndentInNode[0];
47118 if (indentInNode.length >= indentToCode.length) {
47119 return node.slice(indentToCode.length);
47130 var Tokenizer_1 = /*#__PURE__*/function () {
47131 function Tokenizer(options) {
47132 _classCallCheck$1(this, Tokenizer);
47134 this.options = options || defaults$4;
47137 _createClass$1(Tokenizer, [{
47139 value: function space(src) {
47140 var cap = this.rules.block.newline.exec(src);
47143 if (cap[0].length > 1) {
47157 value: function code(src) {
47158 var cap = this.rules.block.code.exec(src);
47161 var text = cap[0].replace(/^ {1,4}/gm, '');
47165 codeBlockStyle: 'indented',
47166 text: !this.options.pedantic ? rtrim(text, '\n') : text
47172 value: function fences(src) {
47173 var cap = this.rules.block.fences.exec(src);
47177 var text = indentCodeCompensation(raw, cap[3] || '');
47181 lang: cap[2] ? cap[2].trim() : cap[2],
47188 value: function heading(src) {
47189 var cap = this.rules.block.heading.exec(src);
47192 var text = cap[2].trim(); // remove trailing #s
47194 if (/#$/.test(text)) {
47195 var trimmed = rtrim(text, '#');
47197 if (this.options.pedantic) {
47198 text = trimmed.trim();
47199 } else if (!trimmed || / $/.test(trimmed)) {
47200 // CommonMark requires space before trailing #s
47201 text = trimmed.trim();
47208 depth: cap[1].length,
47215 value: function nptable(src) {
47216 var cap = this.rules.block.nptable.exec(src);
47221 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47222 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47223 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
47227 if (item.header.length === item.align.length) {
47228 var l = item.align.length;
47231 for (i = 0; i < l; i++) {
47232 if (/^ *-+: *$/.test(item.align[i])) {
47233 item.align[i] = 'right';
47234 } else if (/^ *:-+: *$/.test(item.align[i])) {
47235 item.align[i] = 'center';
47236 } else if (/^ *:-+ *$/.test(item.align[i])) {
47237 item.align[i] = 'left';
47239 item.align[i] = null;
47243 l = item.cells.length;
47245 for (i = 0; i < l; i++) {
47246 item.cells[i] = splitCells(item.cells[i], item.header.length);
47255 value: function hr(src) {
47256 var cap = this.rules.block.hr.exec(src);
47267 value: function blockquote(src) {
47268 var cap = this.rules.block.blockquote.exec(src);
47271 var text = cap[0].replace(/^ *> ?/gm, '');
47273 type: 'blockquote',
47281 value: function list(src) {
47282 var cap = this.rules.block.list.exec(src);
47287 var isordered = bull.length > 1;
47291 ordered: isordered,
47292 start: isordered ? +bull.slice(0, -1) : '',
47295 }; // Get each top-level item.
47297 var itemMatch = cap[0].match(this.rules.block.item);
47308 var l = itemMatch.length;
47309 bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
47311 for (var i = 0; i < l; i++) {
47312 item = itemMatch[i];
47315 if (!this.options.pedantic) {
47316 // Determine if current item contains the end of the list
47317 endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S'));
47320 addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length;
47321 list.raw = list.raw.substring(0, list.raw.length - addBack);
47322 item = item.substring(0, endMatch.index);
47326 } // Determine whether the next list item belongs here.
47327 // Backpedal if it does not belong in this list.
47331 bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
47333 if (!this.options.pedantic ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3 : bnext[1].length > bcurr[1].length) {
47334 // nested list or continuation
47335 itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]);
47339 } else if ( // different bullet style
47340 !this.options.pedantic || this.options.smartLists ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1] : isordered === (bnext[2].length === 1)) {
47341 addBack = itemMatch.slice(i + 1).join('\n').length;
47342 list.raw = list.raw.substring(0, list.raw.length - addBack);
47347 } // Remove the list item's bullet
47348 // so it is seen as the next token.
47351 space = item.length;
47352 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
47353 // list item contains. Hacky.
47355 if (~item.indexOf('\n ')) {
47356 space -= item.length;
47357 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
47358 } // trim item newlines at end
47361 item = rtrim(item, '\n');
47365 } // Determine whether item is loose or not.
47366 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
47367 // for discount behavior.
47370 loose = next || /\n\n(?!\s*$)/.test(raw);
47373 next = raw.slice(-2) === '\n\n';
47374 if (!loose) loose = next;
47379 } // Check for task list items
47382 if (this.options.gfm) {
47383 istask = /^\[[ xX]\] /.test(item);
47384 ischecked = undefined;
47387 ischecked = item[1] !== ' ';
47388 item = item.replace(/^\[[ xX]\] +/, '');
47396 checked: ischecked,
47407 value: function html(src) {
47408 var cap = this.rules.block.html.exec(src);
47412 type: this.options.sanitize ? 'paragraph' : 'html',
47414 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
47415 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47421 value: function def(src) {
47422 var cap = this.rules.block.def.exec(src);
47425 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
47426 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
47438 value: function table(src) {
47439 var cap = this.rules.block.table.exec(src);
47444 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
47445 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
47446 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
47449 if (item.header.length === item.align.length) {
47451 var l = item.align.length;
47454 for (i = 0; i < l; i++) {
47455 if (/^ *-+: *$/.test(item.align[i])) {
47456 item.align[i] = 'right';
47457 } else if (/^ *:-+: *$/.test(item.align[i])) {
47458 item.align[i] = 'center';
47459 } else if (/^ *:-+ *$/.test(item.align[i])) {
47460 item.align[i] = 'left';
47462 item.align[i] = null;
47466 l = item.cells.length;
47468 for (i = 0; i < l; i++) {
47469 item.cells[i] = splitCells(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
47478 value: function lheading(src) {
47479 var cap = this.rules.block.lheading.exec(src);
47485 depth: cap[2].charAt(0) === '=' ? 1 : 2,
47492 value: function paragraph(src) {
47493 var cap = this.rules.block.paragraph.exec(src);
47499 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
47505 value: function text(src) {
47506 var cap = this.rules.block.text.exec(src);
47518 value: function escape(src) {
47519 var cap = this.rules.inline.escape.exec(src);
47525 text: _escape(cap[1])
47531 value: function tag(src, inLink, inRawBlock) {
47532 var cap = this.rules.inline.tag.exec(src);
47535 if (!inLink && /^<a /i.test(cap[0])) {
47537 } else if (inLink && /^<\/a>/i.test(cap[0])) {
47541 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47543 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
47544 inRawBlock = false;
47548 type: this.options.sanitize ? 'text' : 'html',
47551 inRawBlock: inRawBlock,
47552 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
47558 value: function link(src) {
47559 var cap = this.rules.inline.link.exec(src);
47562 var trimmedUrl = cap[2].trim();
47564 if (!this.options.pedantic && /^</.test(trimmedUrl)) {
47565 // commonmark requires matching angle brackets
47566 if (!/>$/.test(trimmedUrl)) {
47568 } // ending angle bracket cannot be escaped
47571 var rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
47573 if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
47577 // find closing parenthesis
47578 var lastParenIndex = findClosingBracket(cap[2], '()');
47580 if (lastParenIndex > -1) {
47581 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
47582 var linkLen = start + cap[1].length + lastParenIndex;
47583 cap[2] = cap[2].substring(0, lastParenIndex);
47584 cap[0] = cap[0].substring(0, linkLen).trim();
47592 if (this.options.pedantic) {
47593 // split pedantic href and title
47594 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
47601 title = cap[3] ? cap[3].slice(1, -1) : '';
47604 href = href.trim();
47606 if (/^</.test(href)) {
47607 if (this.options.pedantic && !/>$/.test(trimmedUrl)) {
47608 // pedantic allows starting angle bracket without ending angle bracket
47609 href = href.slice(1);
47611 href = href.slice(1, -1);
47615 return outputLink(cap, {
47616 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
47617 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
47623 value: function reflink(src, links) {
47626 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
47627 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
47628 link = links[link.toLowerCase()];
47630 if (!link || !link.href) {
47631 var text = cap[0].charAt(0);
47639 return outputLink(cap, link, cap[0]);
47644 value: function emStrong(src, maskedSrc) {
47645 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
47646 var match = this.rules.inline.emStrong.lDelim.exec(src);
47647 if (!match) return; // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
47649 if (match[3] && prevChar.match(/(?:[0-9A-Za-z\xAA\xB2\xB3\xB5\xB9\xBA\xBC-\xBE\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u0660-\u0669\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08C7\u0904-\u0939\u093D\u0950\u0958-\u0961\u0966-\u096F\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09F9\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AE6-\u0AEF\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0BE6-\u0BF2\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C78-\u0C7E\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D58-\u0D61\u0D66-\u0D78\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DE6-\u0DEF\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F20-\u0F33\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F-\u1049\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u1090-\u1099\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1369-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A20-\u1A54\u1A80-\u1A89\u1A90-\u1A99\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B50-\u1B59\u1B83-\u1BA0\u1BAE-\u1BE5\u1C00-\u1C23\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2150-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2CFD\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u3192-\u3195\u31A0-\u31BF\u31F0-\u31FF\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\u3400-\u4DBF\u4E00-\u9FFC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7BF\uA7C2-\uA7CA\uA7F5-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA830-\uA835\uA840-\uA873\uA882-\uA8B3\uA8D0-\uA8D9\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA900-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF-\uA9D9\uA9E0-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDE80-\uDE9C\uDEA0-\uDED0\uDEE1-\uDEFB\uDF00-\uDF23\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC58-\uDC76\uDC79-\uDC9E\uDCA7-\uDCAF\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDD1B\uDD20-\uDD39\uDD80-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE40-\uDE48\uDE60-\uDE7E\uDE80-\uDE9F\uDEC0-\uDEC7\uDEC9-\uDEE4\uDEEB-\uDEEF\uDF00-\uDF35\uDF40-\uDF55\uDF58-\uDF72\uDF78-\uDF91\uDFA9-\uDFAF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDD23\uDD30-\uDD39\uDE60-\uDE7E\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF27\uDF30-\uDF45\uDF51-\uDF54\uDFB0-\uDFCB\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC52-\uDC6F\uDC83-\uDCAF\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD03-\uDD26\uDD36-\uDD3F\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDD0-\uDDDA\uDDDC\uDDE1-\uDDF4\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDEF0-\uDEF9\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC50-\uDC59\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE50-\uDE59\uDE80-\uDEAA\uDEB8\uDEC0-\uDEC9\uDF00-\uDF1A\uDF30-\uDF3B]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCF2\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDD50-\uDD59\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC50-\uDC6C\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD50-\uDD59\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDA0-\uDDA9\uDEE0-\uDEF2\uDFB0\uDFC0-\uDFD4]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE96\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82C[\uDC00-\uDD1E\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD834[\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD40-\uDD49\uDD4E\uDEC0-\uDEEB\uDEF0-\uDEF9]|\uD83A[\uDC00-\uDCC4\uDCC7-\uDCCF\uDD00-\uDD43\uDD4B\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9]|\uD869[\uDC00-\uDEDD\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])/)) return;
47650 var nextChar = match[1] || match[2] || '';
47652 if (!nextChar || nextChar && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar))) {
47653 var lLength = match[0].length - 1;
47656 delimTotal = lLength,
47658 var endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
47659 endReg.lastIndex = 0; // Clip maskedSrc to same section of string as src (move to lexer?)
47661 maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
47663 while ((match = endReg.exec(maskedSrc)) != null) {
47664 rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
47665 if (!rDelim) continue; // skip single * in __abc*abc__
47667 rLength = rDelim.length;
47669 if (match[3] || match[4]) {
47670 // found another Left Delim
47671 delimTotal += rLength;
47673 } else if (match[5] || match[6]) {
47674 // either Left or Right Delim
47675 if (lLength % 3 && !((lLength + rLength) % 3)) {
47676 midDelimTotal += rLength;
47677 continue; // CommonMark Emphasis Rules 9-10
47681 delimTotal -= rLength;
47682 if (delimTotal > 0) continue; // Haven't found enough closing delimiters
47683 // Remove extra characters. *a*** -> *a*
47685 rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); // Create `em` if smallest delimiter has odd char count. *a***
47687 if (Math.min(lLength, rLength) % 2) {
47690 raw: src.slice(0, lLength + match.index + rLength + 1),
47691 text: src.slice(1, lLength + match.index + rLength)
47693 } // Create 'strong' if smallest delimiter has even char count. **a***
47698 raw: src.slice(0, lLength + match.index + rLength + 1),
47699 text: src.slice(2, lLength + match.index + rLength - 1)
47706 value: function codespan(src) {
47707 var cap = this.rules.inline.code.exec(src);
47710 var text = cap[2].replace(/\n/g, ' ');
47711 var hasNonSpaceChars = /[^ ]/.test(text);
47712 var hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
47714 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
47715 text = text.substring(1, text.length - 1);
47718 text = _escape(text, true);
47728 value: function br(src) {
47729 var cap = this.rules.inline.br.exec(src);
47740 value: function del(src) {
47741 var cap = this.rules.inline.del.exec(src);
47753 value: function autolink(src, mangle) {
47754 var cap = this.rules.inline.autolink.exec(src);
47759 if (cap[2] === '@') {
47760 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
47761 href = 'mailto:' + text;
47763 text = _escape(cap[1]);
47782 value: function url(src, mangle) {
47785 if (cap = this.rules.inline.url.exec(src)) {
47788 if (cap[2] === '@') {
47789 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
47790 href = 'mailto:' + text;
47792 // do extended autolink path validation
47796 prevCapZero = cap[0];
47797 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
47798 } while (prevCapZero !== cap[0]);
47800 text = _escape(cap[0]);
47802 if (cap[1] === 'www.') {
47803 href = 'http://' + text;
47824 value: function inlineText(src, inRawBlock, smartypants) {
47825 var cap = this.rules.inline.text.exec(src);
47831 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
47833 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
47848 var noopTest = helpers.noopTest,
47849 edit = helpers.edit,
47850 merge$1 = helpers.merge;
47852 * Block-Level Grammar
47856 newline: /^(?: *(?:\n|$))+/,
47857 code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,
47858 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
47859 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
47860 heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,
47861 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
47862 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,
47863 html: '^ {0,3}(?:' // optional indentation
47864 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
47865 + '|comment[^\\n]*(\\n+|$)' // (2)
47866 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
47867 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
47868 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
47869 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (6)
47870 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) open tag
47871 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)' // (7) closing tag
47873 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
47876 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
47877 // regex template, placeholders will be replaced according to different paragraph
47878 // interruption rules of commonmark and the original markdown spec:
47879 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,
47882 block$1._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
47883 block$1._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
47884 block$1.def = edit(block$1.def).replace('label', block$1._label).replace('title', block$1._title).getRegex();
47885 block$1.bullet = /(?:[*+-]|\d{1,9}[.)])/;
47886 block$1.item = /^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/;
47887 block$1.item = edit(block$1.item, 'gm').replace(/bull/g, block$1.bullet).getRegex();
47888 block$1.listItemStart = edit(/^( *)(bull) */).replace('bull', block$1.bullet).getRegex();
47889 block$1.list = edit(block$1.list).replace(/bull/g, block$1.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block$1.def.source + ')').getRegex();
47890 block$1._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';
47891 block$1._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
47892 block$1.html = edit(block$1.html, 'i').replace('comment', block$1._comment).replace('tag', block$1._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
47893 block$1.paragraph = edit(block$1._paragraph).replace('hr', block$1.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
47894 .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
47895 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // pars can be interrupted by type (6) html blocks
47897 block$1.blockquote = edit(block$1.blockquote).replace('paragraph', block$1.paragraph).getRegex();
47899 * Normal Block Grammar
47902 block$1.normal = merge$1({}, block$1);
47904 * GFM Block Grammar
47907 block$1.gfm = merge$1({}, block$1.normal, {
47908 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
47909 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
47910 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
47912 table: '^ *\\|(.+)\\n' // Header
47913 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
47914 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
47917 block$1.gfm.nptable = edit(block$1.gfm.nptable).replace('hr', block$1.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
47918 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47920 block$1.gfm.table = edit(block$1.gfm.table).replace('hr', block$1.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
47921 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block$1._tag) // tables can be interrupted by type (6) html blocks
47924 * Pedantic grammar (original John Gruber's loose markdown specification)
47927 block$1.pedantic = merge$1({}, block$1.normal, {
47928 html: edit('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
47929 + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block$1._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(),
47930 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
47931 heading: /^(#{1,6})(.*)(?:\n+|$)/,
47933 // fences not supported
47934 paragraph: edit(block$1.normal._paragraph).replace('hr', block$1.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block$1.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex()
47937 * Inline-Level Grammar
47941 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
47942 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
47944 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
47945 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
47946 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
47947 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
47948 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
47950 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
47951 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
47952 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
47953 reflinkSearch: 'reflink|nolink(?!\\()',
47955 lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,
47956 // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right.
47957 // () Skip other delimiter (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a
47958 rDelimAst: /\_\_[^_*]*?\*[^_*]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
47959 rDelimUnd: /\*\*[^_*]*?\_[^_*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _
47962 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
47963 br: /^( {2,}|\\)\n(?!\s*$)/,
47965 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,
47966 punctuation: /^([\spunctuation])/
47967 }; // list of punctuation marks from CommonMark spec
47968 // without * and _ to handle the different emphasis markers * and _
47970 inline$1._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
47971 inline$1.punctuation = edit(inline$1.punctuation).replace(/punctuation/g, inline$1._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
47973 inline$1.blockSkip = /\[[^\]]*?\]\([^\)]*?\)|`[^`]*?`|<[^>]*?>/g;
47974 inline$1.escapedEmSt = /\\\*|\\_/g;
47975 inline$1._comment = edit(block$1._comment).replace('(?:-->|$)', '-->').getRegex();
47976 inline$1.emStrong.lDelim = edit(inline$1.emStrong.lDelim).replace(/punct/g, inline$1._punctuation).getRegex();
47977 inline$1.emStrong.rDelimAst = edit(inline$1.emStrong.rDelimAst, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47978 inline$1.emStrong.rDelimUnd = edit(inline$1.emStrong.rDelimUnd, 'g').replace(/punct/g, inline$1._punctuation).getRegex();
47979 inline$1._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
47980 inline$1._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
47981 inline$1._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])?)+(?![-_])/;
47982 inline$1.autolink = edit(inline$1.autolink).replace('scheme', inline$1._scheme).replace('email', inline$1._email).getRegex();
47983 inline$1._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
47984 inline$1.tag = edit(inline$1.tag).replace('comment', inline$1._comment).replace('attribute', inline$1._attribute).getRegex();
47985 inline$1._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
47986 inline$1._href = /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/;
47987 inline$1._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
47988 inline$1.link = edit(inline$1.link).replace('label', inline$1._label).replace('href', inline$1._href).replace('title', inline$1._title).getRegex();
47989 inline$1.reflink = edit(inline$1.reflink).replace('label', inline$1._label).getRegex();
47990 inline$1.reflinkSearch = edit(inline$1.reflinkSearch, 'g').replace('reflink', inline$1.reflink).replace('nolink', inline$1.nolink).getRegex();
47992 * Normal Inline Grammar
47995 inline$1.normal = merge$1({}, inline$1);
47997 * Pedantic Inline Grammar
48000 inline$1.pedantic = merge$1({}, inline$1.normal, {
48003 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
48004 endAst: /\*\*(?!\*)/g,
48009 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
48010 endAst: /\*(?!\*)/g,
48013 link: edit(/^!?\[(label)\]\((.*?)\)/).replace('label', inline$1._label).getRegex(),
48014 reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline$1._label).getRegex()
48017 * GFM Inline Grammar
48020 inline$1.gfm = merge$1({}, inline$1.normal, {
48021 escape: edit(inline$1.escape).replace('])', '~|])').getRegex(),
48022 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
48023 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
48024 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
48025 del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
48026 text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
48028 inline$1.gfm.url = edit(inline$1.gfm.url, 'i').replace('email', inline$1.gfm._extended_email).getRegex();
48030 * GFM + Line Breaks Inline Grammar
48033 inline$1.breaks = merge$1({}, inline$1.gfm, {
48034 br: edit(inline$1.br).replace('{2,}', '*').getRegex(),
48035 text: edit(inline$1.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
48042 var defaults$3 = defaults$5.defaults;
48043 var block = rules.block,
48044 inline = rules.inline;
48045 var repeatString = helpers.repeatString;
48047 * smartypants text replacement
48050 function smartypants(text) {
48051 return text // em-dashes
48052 .replace(/---/g, "\u2014") // en-dashes
48053 .replace(/--/g, "\u2013") // opening singles
48054 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
48055 .replace(/'/g, "\u2019") // opening doubles
48056 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
48057 .replace(/"/g, "\u201D") // ellipses
48058 .replace(/\.{3}/g, "\u2026");
48061 * mangle email addresses
48065 function mangle(text) {
48069 var l = text.length;
48071 for (i = 0; i < l; i++) {
48072 ch = text.charCodeAt(i);
48074 if (Math.random() > 0.5) {
48075 ch = 'x' + ch.toString(16);
48078 out += '&#' + ch + ';';
48088 var Lexer_1 = /*#__PURE__*/function () {
48089 function Lexer(options) {
48090 _classCallCheck$1(this, Lexer);
48093 this.tokens.links = Object.create(null);
48094 this.options = options || defaults$3;
48095 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
48096 this.tokenizer = this.options.tokenizer;
48097 this.tokenizer.options = this.options;
48099 block: block.normal,
48100 inline: inline.normal
48103 if (this.options.pedantic) {
48104 rules.block = block.pedantic;
48105 rules.inline = inline.pedantic;
48106 } else if (this.options.gfm) {
48107 rules.block = block.gfm;
48109 if (this.options.breaks) {
48110 rules.inline = inline.breaks;
48112 rules.inline = inline.gfm;
48116 this.tokenizer.rules = rules;
48123 _createClass$1(Lexer, [{
48129 function lex(src) {
48130 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
48131 this.blockTokens(src, this.tokens, true);
48132 this.inline(this.tokens);
48133 return this.tokens;
48140 key: "blockTokens",
48141 value: function blockTokens(src) {
48142 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48143 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
48145 if (this.options.pedantic) {
48146 src = src.replace(/^ +$/gm, '');
48149 var token, i, l, lastToken;
48153 if (token = this.tokenizer.space(src)) {
48154 src = src.substring(token.raw.length);
48157 tokens.push(token);
48164 if (token = this.tokenizer.code(src)) {
48165 src = src.substring(token.raw.length);
48166 lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
48168 if (lastToken && lastToken.type === 'paragraph') {
48169 lastToken.raw += '\n' + token.raw;
48170 lastToken.text += '\n' + token.text;
48172 tokens.push(token);
48179 if (token = this.tokenizer.fences(src)) {
48180 src = src.substring(token.raw.length);
48181 tokens.push(token);
48186 if (token = this.tokenizer.heading(src)) {
48187 src = src.substring(token.raw.length);
48188 tokens.push(token);
48190 } // table no leading pipe (gfm)
48193 if (token = this.tokenizer.nptable(src)) {
48194 src = src.substring(token.raw.length);
48195 tokens.push(token);
48200 if (token = this.tokenizer.hr(src)) {
48201 src = src.substring(token.raw.length);
48202 tokens.push(token);
48207 if (token = this.tokenizer.blockquote(src)) {
48208 src = src.substring(token.raw.length);
48209 token.tokens = this.blockTokens(token.text, [], top);
48210 tokens.push(token);
48215 if (token = this.tokenizer.list(src)) {
48216 src = src.substring(token.raw.length);
48217 l = token.items.length;
48219 for (i = 0; i < l; i++) {
48220 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
48223 tokens.push(token);
48228 if (token = this.tokenizer.html(src)) {
48229 src = src.substring(token.raw.length);
48230 tokens.push(token);
48235 if (top && (token = this.tokenizer.def(src))) {
48236 src = src.substring(token.raw.length);
48238 if (!this.tokens.links[token.tag]) {
48239 this.tokens.links[token.tag] = {
48249 if (token = this.tokenizer.table(src)) {
48250 src = src.substring(token.raw.length);
48251 tokens.push(token);
48256 if (token = this.tokenizer.lheading(src)) {
48257 src = src.substring(token.raw.length);
48258 tokens.push(token);
48260 } // top-level paragraph
48263 if (top && (token = this.tokenizer.paragraph(src))) {
48264 src = src.substring(token.raw.length);
48265 tokens.push(token);
48270 if (token = this.tokenizer.text(src)) {
48271 src = src.substring(token.raw.length);
48272 lastToken = tokens[tokens.length - 1];
48274 if (lastToken && lastToken.type === 'text') {
48275 lastToken.raw += '\n' + token.raw;
48276 lastToken.text += '\n' + token.text;
48278 tokens.push(token);
48285 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48287 if (this.options.silent) {
48288 console.error(errMsg);
48291 throw new Error(errMsg);
48300 value: function inline(tokens) {
48301 var i, j, k, l2, row, token;
48302 var l = tokens.length;
48304 for (i = 0; i < l; i++) {
48307 switch (token.type) {
48313 this.inlineTokens(token.text, token.tokens);
48324 l2 = token.header.length;
48326 for (j = 0; j < l2; j++) {
48327 token.tokens.header[j] = [];
48328 this.inlineTokens(token.header[j], token.tokens.header[j]);
48332 l2 = token.cells.length;
48334 for (j = 0; j < l2; j++) {
48335 row = token.cells[j];
48336 token.tokens.cells[j] = [];
48338 for (k = 0; k < row.length; k++) {
48339 token.tokens.cells[j][k] = [];
48340 this.inlineTokens(row[k], token.tokens.cells[j][k]);
48349 this.inline(token.tokens);
48355 l2 = token.items.length;
48357 for (j = 0; j < l2; j++) {
48358 this.inline(token.items[j].tokens);
48373 key: "inlineTokens",
48374 value: function inlineTokens(src) {
48375 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
48376 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
48377 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
48378 var token, lastToken; // String with links masked to avoid interference with em and strong
48380 var maskedSrc = src;
48382 var keepPrevChar, prevChar; // Mask out reflinks
48384 if (this.tokens.links) {
48385 var links = Object.keys(this.tokens.links);
48387 if (links.length > 0) {
48388 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
48389 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
48390 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
48394 } // Mask out other blocks
48397 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
48398 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
48399 } // Mask out escaped em & strong delimiters
48402 while ((match = this.tokenizer.rules.inline.escapedEmSt.exec(maskedSrc)) != null) {
48403 maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.escapedEmSt.lastIndex);
48407 if (!keepPrevChar) {
48411 keepPrevChar = false; // escape
48413 if (token = this.tokenizer.escape(src)) {
48414 src = src.substring(token.raw.length);
48415 tokens.push(token);
48420 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
48421 src = src.substring(token.raw.length);
48422 inLink = token.inLink;
48423 inRawBlock = token.inRawBlock;
48424 var _lastToken = tokens[tokens.length - 1];
48426 if (_lastToken && token.type === 'text' && _lastToken.type === 'text') {
48427 _lastToken.raw += token.raw;
48428 _lastToken.text += token.text;
48430 tokens.push(token);
48437 if (token = this.tokenizer.link(src)) {
48438 src = src.substring(token.raw.length);
48440 if (token.type === 'link') {
48441 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48444 tokens.push(token);
48446 } // reflink, nolink
48449 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
48450 src = src.substring(token.raw.length);
48451 var _lastToken2 = tokens[tokens.length - 1];
48453 if (token.type === 'link') {
48454 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
48455 tokens.push(token);
48456 } else if (_lastToken2 && token.type === 'text' && _lastToken2.type === 'text') {
48457 _lastToken2.raw += token.raw;
48458 _lastToken2.text += token.text;
48460 tokens.push(token);
48467 if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
48468 src = src.substring(token.raw.length);
48469 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48470 tokens.push(token);
48475 if (token = this.tokenizer.codespan(src)) {
48476 src = src.substring(token.raw.length);
48477 tokens.push(token);
48482 if (token = this.tokenizer.br(src)) {
48483 src = src.substring(token.raw.length);
48484 tokens.push(token);
48489 if (token = this.tokenizer.del(src)) {
48490 src = src.substring(token.raw.length);
48491 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
48492 tokens.push(token);
48497 if (token = this.tokenizer.autolink(src, mangle)) {
48498 src = src.substring(token.raw.length);
48499 tokens.push(token);
48504 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
48505 src = src.substring(token.raw.length);
48506 tokens.push(token);
48511 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
48512 src = src.substring(token.raw.length);
48514 if (token.raw.slice(-1) !== '_') {
48515 // Track prevChar before string of ____ started
48516 prevChar = token.raw.slice(-1);
48519 keepPrevChar = true;
48520 lastToken = tokens[tokens.length - 1];
48522 if (lastToken && lastToken.type === 'text') {
48523 lastToken.raw += token.raw;
48524 lastToken.text += token.text;
48526 tokens.push(token);
48533 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
48535 if (this.options.silent) {
48536 console.error(errMsg);
48539 throw new Error(errMsg);
48548 get: function get() {
48555 * Static Lex Method
48560 value: function lex(src, options) {
48561 var lexer = new Lexer(options);
48562 return lexer.lex(src);
48565 * Static Lex Inline Method
48570 value: function lexInline(src, options) {
48571 var lexer = new Lexer(options);
48572 return lexer.inlineTokens(src);
48579 var defaults$2 = defaults$5.defaults;
48580 var cleanUrl = helpers.cleanUrl,
48581 escape$2 = helpers.escape;
48586 var Renderer_1 = /*#__PURE__*/function () {
48587 function Renderer(options) {
48588 _classCallCheck$1(this, Renderer);
48590 this.options = options || defaults$2;
48593 _createClass$1(Renderer, [{
48595 value: function code(_code, infostring, escaped) {
48596 var lang = (infostring || '').match(/\S*/)[0];
48598 if (this.options.highlight) {
48599 var out = this.options.highlight(_code, lang);
48601 if (out != null && out !== _code) {
48607 _code = _code.replace(/\n$/, '') + '\n';
48610 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48613 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
48617 value: function blockquote(quote) {
48618 return '<blockquote>\n' + quote + '</blockquote>\n';
48622 value: function html(_html) {
48627 value: function heading(text, level, raw, slugger) {
48628 if (this.options.headerIds) {
48629 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
48633 return '<h' + level + '>' + text + '</h' + level + '>\n';
48637 value: function hr() {
48638 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
48642 value: function list(body, ordered, start) {
48643 var type = ordered ? 'ol' : 'ul',
48644 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
48645 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
48649 value: function listitem(text) {
48650 return '<li>' + text + '</li>\n';
48654 value: function checkbox(checked) {
48655 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
48659 value: function paragraph(text) {
48660 return '<p>' + text + '</p>\n';
48664 value: function table(header, body) {
48665 if (body) body = '<tbody>' + body + '</tbody>';
48666 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
48670 value: function tablerow(content) {
48671 return '<tr>\n' + content + '</tr>\n';
48675 value: function tablecell(content, flags) {
48676 var type = flags.header ? 'th' : 'td';
48677 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
48678 return tag + content + '</' + type + '>\n';
48679 } // span level renderer
48683 value: function strong(text) {
48684 return '<strong>' + text + '</strong>';
48688 value: function em(text) {
48689 return '<em>' + text + '</em>';
48693 value: function codespan(text) {
48694 return '<code>' + text + '</code>';
48698 value: function br() {
48699 return this.options.xhtml ? '<br/>' : '<br>';
48703 value: function del(text) {
48704 return '<del>' + text + '</del>';
48708 value: function link(href, title, text) {
48709 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48711 if (href === null) {
48715 var out = '<a href="' + escape$2(href) + '"';
48718 out += ' title="' + title + '"';
48721 out += '>' + text + '</a>';
48726 value: function image(href, title, text) {
48727 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
48729 if (href === null) {
48733 var out = '<img src="' + href + '" alt="' + text + '"';
48736 out += ' title="' + title + '"';
48739 out += this.options.xhtml ? '/>' : '>';
48744 value: function text(_text) {
48754 * returns only the textual part of the token
48756 var TextRenderer_1 = /*#__PURE__*/function () {
48757 function TextRenderer() {
48758 _classCallCheck$1(this, TextRenderer);
48761 _createClass$1(TextRenderer, [{
48763 value: // no need for block level renderers
48764 function strong(text) {
48769 value: function em(text) {
48774 value: function codespan(text) {
48779 value: function del(text) {
48784 value: function html(text) {
48789 value: function text(_text) {
48794 value: function link(href, title, text) {
48799 value: function image(href, title, text) {
48804 value: function br() {
48809 return TextRenderer;
48813 * Slugger generates header id
48815 var Slugger_1 = /*#__PURE__*/function () {
48816 function Slugger() {
48817 _classCallCheck$1(this, Slugger);
48822 _createClass$1(Slugger, [{
48824 value: function serialize(value) {
48825 return value.toLowerCase().trim() // remove html tags
48826 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
48827 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
48830 * Finds the next safe (unique) slug to use
48834 key: "getNextSafeSlug",
48835 value: function getNextSafeSlug(originalSlug, isDryRun) {
48836 var slug = originalSlug;
48837 var occurenceAccumulator = 0;
48839 if (this.seen.hasOwnProperty(slug)) {
48840 occurenceAccumulator = this.seen[originalSlug];
48843 occurenceAccumulator++;
48844 slug = originalSlug + '-' + occurenceAccumulator;
48845 } while (this.seen.hasOwnProperty(slug));
48849 this.seen[originalSlug] = occurenceAccumulator;
48850 this.seen[slug] = 0;
48856 * Convert string to unique id
48857 * @param {object} options
48858 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
48863 value: function slug(value) {
48864 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
48865 var slug = this.serialize(value);
48866 return this.getNextSafeSlug(slug, options.dryrun);
48873 var defaults$1 = defaults$5.defaults;
48874 var unescape$1 = helpers.unescape;
48876 * Parsing & Compiling
48879 var Parser_1 = /*#__PURE__*/function () {
48880 function Parser(options) {
48881 _classCallCheck$1(this, Parser);
48883 this.options = options || defaults$1;
48884 this.options.renderer = this.options.renderer || new Renderer_1();
48885 this.renderer = this.options.renderer;
48886 this.renderer.options = this.options;
48887 this.textRenderer = new TextRenderer_1();
48888 this.slugger = new Slugger_1();
48891 * Static Parse Method
48895 _createClass$1(Parser, [{
48901 function parse(tokens) {
48902 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
48922 var l = tokens.length;
48924 for (i = 0; i < l; i++) {
48927 switch (token.type) {
48935 out += this.renderer.hr();
48941 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
48947 out += this.renderer.code(token.text, token.lang, token.escaped);
48953 header = ''; // header
48956 l2 = token.header.length;
48958 for (j = 0; j < l2; j++) {
48959 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
48961 align: token.align[j]
48965 header += this.renderer.tablerow(cell);
48967 l2 = token.cells.length;
48969 for (j = 0; j < l2; j++) {
48970 row = token.tokens.cells[j];
48974 for (k = 0; k < l3; k++) {
48975 cell += this.renderer.tablecell(this.parseInline(row[k]), {
48977 align: token.align[k]
48981 body += this.renderer.tablerow(cell);
48984 out += this.renderer.table(header, body);
48990 body = this.parse(token.tokens);
48991 out += this.renderer.blockquote(body);
48997 ordered = token.ordered;
48998 start = token.start;
48999 loose = token.loose;
49000 l2 = token.items.length;
49003 for (j = 0; j < l2; j++) {
49004 item = token.items[j];
49005 checked = item.checked;
49010 checkbox = this.renderer.checkbox(checked);
49013 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
49014 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
49016 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
49017 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
49020 item.tokens.unshift({
49026 itemBody += checkbox;
49030 itemBody += this.parse(item.tokens, loose);
49031 body += this.renderer.listitem(itemBody, task, checked);
49034 out += this.renderer.list(body, ordered, start);
49040 // TODO parse inline content if parameter markdown=1
49041 out += this.renderer.html(token.text);
49047 out += this.renderer.paragraph(this.parseInline(token.tokens));
49053 body = token.tokens ? this.parseInline(token.tokens) : token.text;
49055 while (i + 1 < l && tokens[i + 1].type === 'text') {
49056 token = tokens[++i];
49057 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
49060 out += top ? this.renderer.paragraph(body) : body;
49066 var errMsg = 'Token with "' + token.type + '" type was not found.';
49068 if (this.options.silent) {
49069 console.error(errMsg);
49072 throw new Error(errMsg);
49081 * Parse Inline Tokens
49085 key: "parseInline",
49086 value: function parseInline(tokens, renderer) {
49087 renderer = renderer || this.renderer;
49091 var l = tokens.length;
49093 for (i = 0; i < l; i++) {
49096 switch (token.type) {
49099 out += renderer.text(token.text);
49105 out += renderer.html(token.text);
49111 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
49117 out += renderer.image(token.href, token.title, token.text);
49123 out += renderer.strong(this.parseInline(token.tokens, renderer));
49129 out += renderer.em(this.parseInline(token.tokens, renderer));
49135 out += renderer.codespan(token.text);
49141 out += renderer.br();
49147 out += renderer.del(this.parseInline(token.tokens, renderer));
49153 out += renderer.text(token.text);
49159 var errMsg = 'Token with "' + token.type + '" type was not found.';
49161 if (this.options.silent) {
49162 console.error(errMsg);
49165 throw new Error(errMsg);
49175 value: function parse(tokens, options) {
49176 var parser = new Parser(options);
49177 return parser.parse(tokens);
49180 * Static Parse Inline Method
49184 key: "parseInline",
49185 value: function parseInline(tokens, options) {
49186 var parser = new Parser(options);
49187 return parser.parseInline(tokens);
49194 var merge = helpers.merge,
49195 checkSanitizeDeprecation = helpers.checkSanitizeDeprecation,
49196 escape$1 = helpers.escape;
49197 var getDefaults = defaults$5.getDefaults,
49198 changeDefaults = defaults$5.changeDefaults,
49199 defaults = defaults$5.defaults;
49204 function marked(src, opt, callback) {
49205 // throw error in case of non string input
49206 if (typeof src === 'undefined' || src === null) {
49207 throw new Error('marked(): input parameter is undefined or null');
49210 if (typeof src !== 'string') {
49211 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49214 if (typeof opt === 'function') {
49219 opt = merge({}, marked.defaults, opt || {});
49220 checkSanitizeDeprecation(opt);
49223 var highlight = opt.highlight;
49227 tokens = Lexer_1.lex(src, opt);
49229 return callback(e);
49232 var done = function done(err) {
49237 if (opt.walkTokens) {
49238 marked.walkTokens(tokens, opt.walkTokens);
49241 out = Parser_1.parse(tokens, opt);
49247 opt.highlight = highlight;
49248 return err ? callback(err) : callback(null, out);
49251 if (!highlight || highlight.length < 3) {
49255 delete opt.highlight;
49256 if (!tokens.length) return done();
49258 marked.walkTokens(tokens, function (token) {
49259 if (token.type === 'code') {
49261 setTimeout(function () {
49262 highlight(token.text, token.lang, function (err, code) {
49267 if (code != null && code !== token.text) {
49269 token.escaped = true;
49274 if (pending === 0) {
49282 if (pending === 0) {
49290 var _tokens = Lexer_1.lex(src, opt);
49292 if (opt.walkTokens) {
49293 marked.walkTokens(_tokens, opt.walkTokens);
49296 return Parser_1.parse(_tokens, opt);
49298 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49301 return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49312 marked.options = marked.setOptions = function (opt) {
49313 merge(marked.defaults, opt);
49314 changeDefaults(marked.defaults);
49318 marked.getDefaults = getDefaults;
49319 marked.defaults = defaults;
49324 marked.use = function (extension) {
49325 var opts = merge({}, extension);
49327 if (extension.renderer) {
49329 var renderer = marked.defaults.renderer || new Renderer_1();
49331 var _loop = function _loop(prop) {
49332 var prevRenderer = renderer[prop];
49334 renderer[prop] = function () {
49335 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
49336 args[_key] = arguments[_key];
49339 var ret = extension.renderer[prop].apply(renderer, args);
49341 if (ret === false) {
49342 ret = prevRenderer.apply(renderer, args);
49349 for (var prop in extension.renderer) {
49353 opts.renderer = renderer;
49357 if (extension.tokenizer) {
49359 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
49361 var _loop2 = function _loop2(prop) {
49362 var prevTokenizer = tokenizer[prop];
49364 tokenizer[prop] = function () {
49365 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
49366 args[_key2] = arguments[_key2];
49369 var ret = extension.tokenizer[prop].apply(tokenizer, args);
49371 if (ret === false) {
49372 ret = prevTokenizer.apply(tokenizer, args);
49379 for (var prop in extension.tokenizer) {
49383 opts.tokenizer = tokenizer;
49387 if (extension.walkTokens) {
49388 var walkTokens = marked.defaults.walkTokens;
49390 opts.walkTokens = function (token) {
49391 extension.walkTokens(token);
49399 marked.setOptions(opts);
49402 * Run callback for every token
49406 marked.walkTokens = function (tokens, callback) {
49407 var _iterator = _createForOfIteratorHelper(tokens),
49411 for (_iterator.s(); !(_step = _iterator.n()).done;) {
49412 var token = _step.value;
49415 switch (token.type) {
49418 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
49422 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
49423 var cell = _step2.value;
49424 marked.walkTokens(cell, callback);
49432 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
49436 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
49437 var row = _step3.value;
49439 var _iterator4 = _createForOfIteratorHelper(row),
49443 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
49444 var _cell = _step4.value;
49445 marked.walkTokens(_cell, callback);
49464 marked.walkTokens(token.items, callback);
49470 if (token.tokens) {
49471 marked.walkTokens(token.tokens, callback);
49487 marked.parseInline = function (src, opt) {
49488 // throw error in case of non string input
49489 if (typeof src === 'undefined' || src === null) {
49490 throw new Error('marked.parseInline(): input parameter is undefined or null');
49493 if (typeof src !== 'string') {
49494 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
49497 opt = merge({}, marked.defaults, opt || {});
49498 checkSanitizeDeprecation(opt);
49501 var tokens = Lexer_1.lexInline(src, opt);
49503 if (opt.walkTokens) {
49504 marked.walkTokens(tokens, opt.walkTokens);
49507 return Parser_1.parseInline(tokens, opt);
49509 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
49512 return '<p>An error occurred:</p><pre>' + escape$1(e.message + '', true) + '</pre>';
49523 marked.Parser = Parser_1;
49524 marked.parser = Parser_1.parse;
49525 marked.Renderer = Renderer_1;
49526 marked.TextRenderer = TextRenderer_1;
49527 marked.Lexer = Lexer_1;
49528 marked.lexer = Lexer_1.lex;
49529 marked.Tokenizer = Tokenizer_1;
49530 marked.Slugger = Slugger_1;
49531 marked.parse = marked;
49532 var marked_1 = marked;
49534 var tiler$4 = utilTiler();
49535 var dispatch$5 = dispatch$8('loaded');
49536 var _tileZoom$1 = 14;
49537 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
49538 var _osmoseData = {
49541 }; // This gets reassigned if reset
49545 function abortRequest$4(controller) {
49547 controller.abort();
49551 function abortUnwantedRequests$1(cache, tiles) {
49552 Object.keys(cache.inflightTile).forEach(function (k) {
49553 var wanted = tiles.find(function (tile) {
49554 return k === tile.id;
49558 abortRequest$4(cache.inflightTile[k]);
49559 delete cache.inflightTile[k];
49564 function encodeIssueRtree(d) {
49572 } // Replace or remove QAItem from rtree
49575 function updateRtree$1(item, replace) {
49576 _cache.rtree.remove(item, function (a, b) {
49577 return a.data.id === b.data.id;
49581 _cache.rtree.insert(item);
49583 } // Issues shouldn't obscure each other
49586 function preventCoincident(loc) {
49587 var coincident = false;
49590 // first time, move marker up. after that, move marker right.
49591 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
49592 loc = geoVecAdd(loc, delta);
49593 var bbox = geoExtent(loc).bbox();
49594 coincident = _cache.rtree.search(bbox).length;
49595 } while (coincident);
49600 var serviceOsmose = {
49602 init: function init() {
49603 _mainFileFetcher.get('qa_data').then(function (d) {
49604 _osmoseData = d.osmose;
49605 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
49606 return s.split('-')[0];
49607 }).reduce(function (unique, item) {
49608 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
49616 this.event = utilRebind(this, dispatch$5, 'on');
49618 reset: function reset() {
49623 Object.values(_cache.inflightTile).forEach(abortRequest$4); // Strings and colors are static and should not be re-populated
49625 _strings = _cache.strings;
49626 _colors = _cache.colors;
49635 rtree: new RBush(),
49640 loadIssues: function loadIssues(projection) {
49644 // Tiles return a maximum # of issues
49645 // So we want to filter our request for only types iD supports
49646 item: _osmoseData.items
49647 }; // determine the needed tiles to cover the view
49649 var tiles = tiler$4.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
49651 abortUnwantedRequests$1(_cache, tiles); // issue new requests..
49653 tiles.forEach(function (tile) {
49654 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
49656 var _tile$xyz = _slicedToArray(tile.xyz, 3),
49661 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
49662 var controller = new AbortController();
49663 _cache.inflightTile[tile.id] = controller;
49665 signal: controller.signal
49666 }).then(function (data) {
49667 delete _cache.inflightTile[tile.id];
49668 _cache.loadedTile[tile.id] = true;
49670 if (data.features) {
49671 data.features.forEach(function (issue) {
49672 var _issue$properties = issue.properties,
49673 item = _issue$properties.item,
49674 cl = _issue$properties["class"],
49675 id = _issue$properties.uuid;
49676 /* Osmose issues are uniquely identified by a unique
49677 `item` and `class` combination (both integer values) */
49679 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
49681 if (itemType in _osmoseData.icons) {
49682 var loc = issue.geometry.coordinates; // lon, lat
49684 loc = preventCoincident(loc);
49685 var d = new QAItem(loc, _this, itemType, id, {
49687 }); // Setting elems here prevents UI detail requests
49689 if (item === 8300 || item === 8360) {
49693 _cache.data[d.id] = d;
49695 _cache.rtree.insert(encodeIssueRtree(d));
49700 dispatch$5.call('loaded');
49701 })["catch"](function () {
49702 delete _cache.inflightTile[tile.id];
49703 _cache.loadedTile[tile.id] = true;
49707 loadIssueDetail: function loadIssueDetail(issue) {
49710 // Issue details only need to be fetched once
49711 if (issue.elems !== undefined) {
49712 return Promise.resolve(issue);
49715 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
49717 var cacheDetails = function cacheDetails(data) {
49718 // Associated elements used for highlighting
49719 // Assign directly for immediate use in the callback
49720 issue.elems = data.elems.map(function (e) {
49721 return e.type.substring(0, 1) + e.id;
49722 }); // Some issues have instance specific detail in a subtitle
49724 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
49726 _this2.replaceItem(issue);
49729 return d3_json(url).then(cacheDetails).then(function () {
49733 loadStrings: function loadStrings() {
49734 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
49735 var items = Object.keys(_osmoseData.icons);
49737 if (locale in _cache.strings && Object.keys(_cache.strings[locale]).length === items.length) {
49738 return Promise.resolve(_cache.strings[locale]);
49739 } // May be partially populated already if some requests were successful
49742 if (!(locale in _cache.strings)) {
49743 _cache.strings[locale] = {};
49744 } // Only need to cache strings for supported issue types
49745 // Using multiple individual item + class requests to reduce fetched data size
49748 var allRequests = items.map(function (itemType) {
49749 // No need to request data we already have
49750 if (itemType in _cache.strings[locale]) return null;
49752 var cacheData = function cacheData(data) {
49753 // Bunch of nested single value arrays of objects
49754 var _data$categories = _slicedToArray(data.categories, 1),
49755 _data$categories$ = _data$categories[0],
49756 cat = _data$categories$ === void 0 ? {
49758 } : _data$categories$;
49760 var _cat$items = _slicedToArray(cat.items, 1),
49761 _cat$items$ = _cat$items[0],
49762 item = _cat$items$ === void 0 ? {
49766 var _item$class = _slicedToArray(item["class"], 1),
49767 _item$class$ = _item$class[0],
49768 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
49772 /* eslint-disable no-console */
49773 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
49774 /* eslint-enable no-console */
49777 } // Cache served item colors to automatically style issue markers later
49780 var itemInt = item.item,
49781 color = item.color;
49783 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
49784 _cache.colors[itemInt] = color;
49785 } // Value of root key will be null if no string exists
49786 // If string exists, value is an object with key 'auto' for string
49789 var title = cl.title,
49790 detail = cl.detail,
49792 trap = cl.trap; // Osmose titles shouldn't contain markdown
49794 var issueStrings = {};
49795 if (title) issueStrings.title = title.auto;
49796 if (detail) issueStrings.detail = marked_1(detail.auto);
49797 if (trap) issueStrings.trap = marked_1(trap.auto);
49798 if (fix) issueStrings.fix = marked_1(fix.auto);
49799 _cache.strings[locale][itemType] = issueStrings;
49802 var _itemType$split = itemType.split('-'),
49803 _itemType$split2 = _slicedToArray(_itemType$split, 2),
49804 item = _itemType$split2[0],
49805 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
49808 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
49809 return d3_json(url).then(cacheData);
49810 }).filter(Boolean);
49811 return Promise.all(allRequests).then(function () {
49812 return _cache.strings[locale];
49815 getStrings: function getStrings(itemType) {
49816 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
49817 // No need to fallback to English, Osmose API handles this for us
49818 return locale in _cache.strings ? _cache.strings[locale][itemType] : {};
49820 getColor: function getColor(itemType) {
49821 return itemType in _cache.colors ? _cache.colors[itemType] : '#FFFFFF';
49823 postUpdate: function postUpdate(issue, callback) {
49826 if (_cache.inflightPost[issue.id]) {
49828 message: 'Issue update already inflight',
49831 } // UI sets the status to either 'done' or 'false'
49834 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
49835 var controller = new AbortController();
49837 var after = function after() {
49838 delete _cache.inflightPost[issue.id];
49840 _this3.removeItem(issue);
49842 if (issue.newStatus === 'done') {
49843 // Keep track of the number of issues closed per `item` to tag the changeset
49844 if (!(issue.item in _cache.closed)) {
49845 _cache.closed[issue.item] = 0;
49848 _cache.closed[issue.item] += 1;
49851 if (callback) callback(null, issue);
49854 _cache.inflightPost[issue.id] = controller;
49856 signal: controller.signal
49857 }).then(after)["catch"](function (err) {
49858 delete _cache.inflightPost[issue.id];
49859 if (callback) callback(err.message);
49862 // Get all cached QAItems covering the viewport
49863 getItems: function getItems(projection) {
49864 var viewport = projection.clipExtent();
49865 var min = [viewport[0][0], viewport[1][1]];
49866 var max = [viewport[1][0], viewport[0][1]];
49867 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
49868 return _cache.rtree.search(bbox).map(function (d) {
49872 // Get a QAItem from cache
49873 // NOTE: Don't change method name until UI v3 is merged
49874 getError: function getError(id) {
49875 return _cache.data[id];
49877 // get the name of the icon to display for this item
49878 getIcon: function getIcon(itemType) {
49879 return _osmoseData.icons[itemType];
49881 // Replace a single QAItem in the cache
49882 replaceItem: function replaceItem(item) {
49883 if (!(item instanceof QAItem) || !item.id) return;
49884 _cache.data[item.id] = item;
49885 updateRtree$1(encodeIssueRtree(item), true); // true = replace
49889 // Remove a single QAItem from the cache
49890 removeItem: function removeItem(item) {
49891 if (!(item instanceof QAItem) || !item.id) return;
49892 delete _cache.data[item.id];
49893 updateRtree$1(encodeIssueRtree(item), false); // false = remove
49895 // Used to populate `closed:osmose:*` changeset tags
49896 getClosedCounts: function getClosedCounts() {
49897 return _cache.closed;
49899 itemURL: function itemURL(item) {
49900 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
49904 /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
49905 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
49907 var eLen = nBytes * 8 - mLen - 1;
49908 var eMax = (1 << eLen) - 1;
49909 var eBias = eMax >> 1;
49911 var i = isLE ? nBytes - 1 : 0;
49912 var d = isLE ? -1 : 1;
49913 var s = buffer[offset + i];
49915 e = s & (1 << -nBits) - 1;
49919 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49921 m = e & (1 << -nBits) - 1;
49925 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
49929 } else if (e === eMax) {
49930 return m ? NaN : (s ? -1 : 1) * Infinity;
49932 m = m + Math.pow(2, mLen);
49936 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
49939 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
49941 var eLen = nBytes * 8 - mLen - 1;
49942 var eMax = (1 << eLen) - 1;
49943 var eBias = eMax >> 1;
49944 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
49945 var i = isLE ? 0 : nBytes - 1;
49946 var d = isLE ? 1 : -1;
49947 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
49948 value = Math.abs(value);
49950 if (isNaN(value) || value === Infinity) {
49951 m = isNaN(value) ? 1 : 0;
49954 e = Math.floor(Math.log(value) / Math.LN2);
49956 if (value * (c = Math.pow(2, -e)) < 1) {
49961 if (e + eBias >= 1) {
49964 value += rt * Math.pow(2, 1 - eBias);
49967 if (value * c >= 2) {
49972 if (e + eBias >= eMax) {
49975 } else if (e + eBias >= 1) {
49976 m = (value * c - 1) * Math.pow(2, mLen);
49979 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
49984 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
49989 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
49991 buffer[offset + i - d] |= s * 128;
50001 function Pbf(buf) {
50002 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
50005 this.length = this.buf.length;
50008 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50010 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50012 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50014 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50016 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50017 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50018 // data structures (which currently switch structure types at 12 bytes or more)
50020 var TEXT_DECODER_MIN_LENGTH = 12;
50021 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50023 destroy: function destroy() {
50026 // === READING =================================================================
50027 readFields: function readFields(readField, result, end) {
50028 end = end || this.length;
50030 while (this.pos < end) {
50031 var val = this.readVarint(),
50033 startPos = this.pos;
50034 this.type = val & 0x7;
50035 readField(tag, result, this);
50036 if (this.pos === startPos) this.skip(val);
50041 readMessage: function readMessage(readField, result) {
50042 return this.readFields(readField, result, this.readVarint() + this.pos);
50044 readFixed32: function readFixed32() {
50045 var val = readUInt32(this.buf, this.pos);
50049 readSFixed32: function readSFixed32() {
50050 var val = readInt32(this.buf, this.pos);
50054 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50055 readFixed64: function readFixed64() {
50056 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50060 readSFixed64: function readSFixed64() {
50061 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50065 readFloat: function readFloat() {
50066 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
50070 readDouble: function readDouble() {
50071 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
50075 readVarint: function readVarint(isSigned) {
50076 var buf = this.buf,
50079 b = buf[this.pos++];
50081 if (b < 0x80) return val;
50082 b = buf[this.pos++];
50083 val |= (b & 0x7f) << 7;
50084 if (b < 0x80) return val;
50085 b = buf[this.pos++];
50086 val |= (b & 0x7f) << 14;
50087 if (b < 0x80) return val;
50088 b = buf[this.pos++];
50089 val |= (b & 0x7f) << 21;
50090 if (b < 0x80) return val;
50092 val |= (b & 0x0f) << 28;
50093 return readVarintRemainder(val, isSigned, this);
50095 readVarint64: function readVarint64() {
50096 // for compatibility with v2.0.1
50097 return this.readVarint(true);
50099 readSVarint: function readSVarint() {
50100 var num = this.readVarint();
50101 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50103 readBoolean: function readBoolean() {
50104 return Boolean(this.readVarint());
50106 readString: function readString() {
50107 var end = this.readVarint() + this.pos;
50108 var pos = this.pos;
50111 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50112 // longer strings are fast with the built-in browser TextDecoder API
50113 return readUtf8TextDecoder(this.buf, pos, end);
50114 } // short strings are fast with our custom implementation
50117 return readUtf8(this.buf, pos, end);
50119 readBytes: function readBytes() {
50120 var end = this.readVarint() + this.pos,
50121 buffer = this.buf.subarray(this.pos, end);
50125 // verbose for performance reasons; doesn't affect gzipped size
50126 readPackedVarint: function readPackedVarint(arr, isSigned) {
50127 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50128 var end = readPackedEnd(this);
50131 while (this.pos < end) {
50132 arr.push(this.readVarint(isSigned));
50137 readPackedSVarint: function readPackedSVarint(arr) {
50138 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50139 var end = readPackedEnd(this);
50142 while (this.pos < end) {
50143 arr.push(this.readSVarint());
50148 readPackedBoolean: function readPackedBoolean(arr) {
50149 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50150 var end = readPackedEnd(this);
50153 while (this.pos < end) {
50154 arr.push(this.readBoolean());
50159 readPackedFloat: function readPackedFloat(arr) {
50160 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50161 var end = readPackedEnd(this);
50164 while (this.pos < end) {
50165 arr.push(this.readFloat());
50170 readPackedDouble: function readPackedDouble(arr) {
50171 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50172 var end = readPackedEnd(this);
50175 while (this.pos < end) {
50176 arr.push(this.readDouble());
50181 readPackedFixed32: function readPackedFixed32(arr) {
50182 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50183 var end = readPackedEnd(this);
50186 while (this.pos < end) {
50187 arr.push(this.readFixed32());
50192 readPackedSFixed32: function readPackedSFixed32(arr) {
50193 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50194 var end = readPackedEnd(this);
50197 while (this.pos < end) {
50198 arr.push(this.readSFixed32());
50203 readPackedFixed64: function readPackedFixed64(arr) {
50204 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
50205 var end = readPackedEnd(this);
50208 while (this.pos < end) {
50209 arr.push(this.readFixed64());
50214 readPackedSFixed64: function readPackedSFixed64(arr) {
50215 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
50216 var end = readPackedEnd(this);
50219 while (this.pos < end) {
50220 arr.push(this.readSFixed64());
50225 skip: function skip(val) {
50226 var type = val & 0x7;
50227 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);
50229 // === WRITING =================================================================
50230 writeTag: function writeTag(tag, type) {
50231 this.writeVarint(tag << 3 | type);
50233 realloc: function realloc(min) {
50234 var length = this.length || 16;
50236 while (length < this.pos + min) {
50240 if (length !== this.length) {
50241 var buf = new Uint8Array(length);
50244 this.length = length;
50247 finish: function finish() {
50248 this.length = this.pos;
50250 return this.buf.subarray(0, this.length);
50252 writeFixed32: function writeFixed32(val) {
50254 writeInt32(this.buf, val, this.pos);
50257 writeSFixed32: function writeSFixed32(val) {
50259 writeInt32(this.buf, val, this.pos);
50262 writeFixed64: function writeFixed64(val) {
50264 writeInt32(this.buf, val & -1, this.pos);
50265 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50268 writeSFixed64: function writeSFixed64(val) {
50270 writeInt32(this.buf, val & -1, this.pos);
50271 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
50274 writeVarint: function writeVarint(val) {
50277 if (val > 0xfffffff || val < 0) {
50278 writeBigVarint(val, this);
50283 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
50284 if (val <= 0x7f) return;
50285 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50286 if (val <= 0x7f) return;
50287 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
50288 if (val <= 0x7f) return;
50289 this.buf[this.pos++] = val >>> 7 & 0x7f;
50291 writeSVarint: function writeSVarint(val) {
50292 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
50294 writeBoolean: function writeBoolean(val) {
50295 this.writeVarint(Boolean(val));
50297 writeString: function writeString(str) {
50299 this.realloc(str.length * 4);
50300 this.pos++; // reserve 1 byte for short string length
50302 var startPos = this.pos; // write the string directly to the buffer and see how much was written
50304 this.pos = writeUtf8(this.buf, str, this.pos);
50305 var len = this.pos - startPos;
50306 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50308 this.pos = startPos - 1;
50309 this.writeVarint(len);
50312 writeFloat: function writeFloat(val) {
50314 ieee754.write(this.buf, val, this.pos, true, 23, 4);
50317 writeDouble: function writeDouble(val) {
50319 ieee754.write(this.buf, val, this.pos, true, 52, 8);
50322 writeBytes: function writeBytes(buffer) {
50323 var len = buffer.length;
50324 this.writeVarint(len);
50327 for (var i = 0; i < len; i++) {
50328 this.buf[this.pos++] = buffer[i];
50331 writeRawMessage: function writeRawMessage(fn, obj) {
50332 this.pos++; // reserve 1 byte for short message length
50333 // write the message directly to the buffer and see how much was written
50335 var startPos = this.pos;
50337 var len = this.pos - startPos;
50338 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
50340 this.pos = startPos - 1;
50341 this.writeVarint(len);
50344 writeMessage: function writeMessage(tag, fn, obj) {
50345 this.writeTag(tag, Pbf.Bytes);
50346 this.writeRawMessage(fn, obj);
50348 writePackedVarint: function writePackedVarint(tag, arr) {
50349 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
50351 writePackedSVarint: function writePackedSVarint(tag, arr) {
50352 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
50354 writePackedBoolean: function writePackedBoolean(tag, arr) {
50355 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
50357 writePackedFloat: function writePackedFloat(tag, arr) {
50358 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
50360 writePackedDouble: function writePackedDouble(tag, arr) {
50361 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
50363 writePackedFixed32: function writePackedFixed32(tag, arr) {
50364 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
50366 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
50367 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
50369 writePackedFixed64: function writePackedFixed64(tag, arr) {
50370 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
50372 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
50373 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
50375 writeBytesField: function writeBytesField(tag, buffer) {
50376 this.writeTag(tag, Pbf.Bytes);
50377 this.writeBytes(buffer);
50379 writeFixed32Field: function writeFixed32Field(tag, val) {
50380 this.writeTag(tag, Pbf.Fixed32);
50381 this.writeFixed32(val);
50383 writeSFixed32Field: function writeSFixed32Field(tag, val) {
50384 this.writeTag(tag, Pbf.Fixed32);
50385 this.writeSFixed32(val);
50387 writeFixed64Field: function writeFixed64Field(tag, val) {
50388 this.writeTag(tag, Pbf.Fixed64);
50389 this.writeFixed64(val);
50391 writeSFixed64Field: function writeSFixed64Field(tag, val) {
50392 this.writeTag(tag, Pbf.Fixed64);
50393 this.writeSFixed64(val);
50395 writeVarintField: function writeVarintField(tag, val) {
50396 this.writeTag(tag, Pbf.Varint);
50397 this.writeVarint(val);
50399 writeSVarintField: function writeSVarintField(tag, val) {
50400 this.writeTag(tag, Pbf.Varint);
50401 this.writeSVarint(val);
50403 writeStringField: function writeStringField(tag, str) {
50404 this.writeTag(tag, Pbf.Bytes);
50405 this.writeString(str);
50407 writeFloatField: function writeFloatField(tag, val) {
50408 this.writeTag(tag, Pbf.Fixed32);
50409 this.writeFloat(val);
50411 writeDoubleField: function writeDoubleField(tag, val) {
50412 this.writeTag(tag, Pbf.Fixed64);
50413 this.writeDouble(val);
50415 writeBooleanField: function writeBooleanField(tag, val) {
50416 this.writeVarintField(tag, Boolean(val));
50420 function readVarintRemainder(l, s, p) {
50425 h = (b & 0x70) >> 4;
50426 if (b < 0x80) return toNum(l, h, s);
50428 h |= (b & 0x7f) << 3;
50429 if (b < 0x80) return toNum(l, h, s);
50431 h |= (b & 0x7f) << 10;
50432 if (b < 0x80) return toNum(l, h, s);
50434 h |= (b & 0x7f) << 17;
50435 if (b < 0x80) return toNum(l, h, s);
50437 h |= (b & 0x7f) << 24;
50438 if (b < 0x80) return toNum(l, h, s);
50440 h |= (b & 0x01) << 31;
50441 if (b < 0x80) return toNum(l, h, s);
50442 throw new Error('Expected varint not more than 10 bytes');
50445 function readPackedEnd(pbf) {
50446 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
50449 function toNum(low, high, isSigned) {
50451 return high * 0x100000000 + (low >>> 0);
50454 return (high >>> 0) * 0x100000000 + (low >>> 0);
50457 function writeBigVarint(val, pbf) {
50461 low = val % 0x100000000 | 0;
50462 high = val / 0x100000000 | 0;
50464 low = ~(-val % 0x100000000);
50465 high = ~(-val / 0x100000000);
50467 if (low ^ 0xffffffff) {
50471 high = high + 1 | 0;
50475 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
50476 throw new Error('Given varint doesn\'t fit into 10 bytes');
50480 writeBigVarintLow(low, high, pbf);
50481 writeBigVarintHigh(high, pbf);
50484 function writeBigVarintLow(low, high, pbf) {
50485 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50487 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50489 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50491 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
50493 pbf.buf[pbf.pos] = low & 0x7f;
50496 function writeBigVarintHigh(high, pbf) {
50497 var lsb = (high & 0x07) << 4;
50498 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
50500 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50502 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50504 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50506 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
50508 pbf.buf[pbf.pos++] = high & 0x7f;
50511 function makeRoomForExtraLength(startPos, len, pbf) {
50512 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
50514 pbf.realloc(extraLen);
50516 for (var i = pbf.pos - 1; i >= startPos; i--) {
50517 pbf.buf[i + extraLen] = pbf.buf[i];
50521 function _writePackedVarint(arr, pbf) {
50522 for (var i = 0; i < arr.length; i++) {
50523 pbf.writeVarint(arr[i]);
50527 function _writePackedSVarint(arr, pbf) {
50528 for (var i = 0; i < arr.length; i++) {
50529 pbf.writeSVarint(arr[i]);
50533 function _writePackedFloat(arr, pbf) {
50534 for (var i = 0; i < arr.length; i++) {
50535 pbf.writeFloat(arr[i]);
50539 function _writePackedDouble(arr, pbf) {
50540 for (var i = 0; i < arr.length; i++) {
50541 pbf.writeDouble(arr[i]);
50545 function _writePackedBoolean(arr, pbf) {
50546 for (var i = 0; i < arr.length; i++) {
50547 pbf.writeBoolean(arr[i]);
50551 function _writePackedFixed(arr, pbf) {
50552 for (var i = 0; i < arr.length; i++) {
50553 pbf.writeFixed32(arr[i]);
50557 function _writePackedSFixed(arr, pbf) {
50558 for (var i = 0; i < arr.length; i++) {
50559 pbf.writeSFixed32(arr[i]);
50563 function _writePackedFixed2(arr, pbf) {
50564 for (var i = 0; i < arr.length; i++) {
50565 pbf.writeFixed64(arr[i]);
50569 function _writePackedSFixed2(arr, pbf) {
50570 for (var i = 0; i < arr.length; i++) {
50571 pbf.writeSFixed64(arr[i]);
50573 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
50576 function readUInt32(buf, pos) {
50577 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
50580 function writeInt32(buf, val, pos) {
50582 buf[pos + 1] = val >>> 8;
50583 buf[pos + 2] = val >>> 16;
50584 buf[pos + 3] = val >>> 24;
50587 function readInt32(buf, pos) {
50588 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
50591 function readUtf8(buf, pos, end) {
50597 var c = null; // codepoint
50599 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
50600 if (i + bytesPerSequence > end) break;
50603 if (bytesPerSequence === 1) {
50607 } else if (bytesPerSequence === 2) {
50610 if ((b1 & 0xC0) === 0x80) {
50611 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
50617 } else if (bytesPerSequence === 3) {
50621 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
50622 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
50624 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
50628 } else if (bytesPerSequence === 4) {
50633 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
50634 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
50636 if (c <= 0xFFFF || c >= 0x110000) {
50644 bytesPerSequence = 1;
50645 } else if (c > 0xFFFF) {
50647 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
50648 c = 0xDC00 | c & 0x3FF;
50651 str += String.fromCharCode(c);
50652 i += bytesPerSequence;
50658 function readUtf8TextDecoder(buf, pos, end) {
50659 return utf8TextDecoder.decode(buf.subarray(pos, end));
50662 function writeUtf8(buf, str, pos) {
50663 for (var i = 0, c, lead; i < str.length; i++) {
50664 c = str.charCodeAt(i); // code point
50666 if (c > 0xD7FF && c < 0xE000) {
50675 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
50679 if (c > 0xDBFF || i + 1 === str.length) {
50700 buf[pos++] = c >> 0x6 | 0xC0;
50703 buf[pos++] = c >> 0xC | 0xE0;
50705 buf[pos++] = c >> 0x12 | 0xF0;
50706 buf[pos++] = c >> 0xC & 0x3F | 0x80;
50709 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
50712 buf[pos++] = c & 0x3F | 0x80;
50719 var pointGeometry = Point;
50721 * A standalone point geometry with useful accessor, comparison, and
50722 * modification methods.
50725 * @param {Number} x the x-coordinate. this could be longitude or screen
50726 * pixels, or any other sort of unit.
50727 * @param {Number} y the y-coordinate. this could be latitude or screen
50728 * pixels, or any other sort of unit.
50730 * var point = new Point(-77, 38);
50733 function Point(x, y) {
50738 Point.prototype = {
50740 * Clone this point, returning a new point that can be modified
50741 * without affecting the old one.
50742 * @return {Point} the clone
50744 clone: function clone() {
50745 return new Point(this.x, this.y);
50749 * Add this point's x & y coordinates to another point,
50750 * yielding a new point.
50751 * @param {Point} p the other point
50752 * @return {Point} output point
50754 add: function add(p) {
50755 return this.clone()._add(p);
50759 * Subtract this point's x & y coordinates to from point,
50760 * yielding a new point.
50761 * @param {Point} p the other point
50762 * @return {Point} output point
50764 sub: function sub(p) {
50765 return this.clone()._sub(p);
50769 * Multiply this point's x & y coordinates by point,
50770 * yielding a new point.
50771 * @param {Point} p the other point
50772 * @return {Point} output point
50774 multByPoint: function multByPoint(p) {
50775 return this.clone()._multByPoint(p);
50779 * Divide this point's x & y coordinates by point,
50780 * yielding a new point.
50781 * @param {Point} p the other point
50782 * @return {Point} output point
50784 divByPoint: function divByPoint(p) {
50785 return this.clone()._divByPoint(p);
50789 * Multiply this point's x & y coordinates by a factor,
50790 * yielding a new point.
50791 * @param {Point} k factor
50792 * @return {Point} output point
50794 mult: function mult(k) {
50795 return this.clone()._mult(k);
50799 * Divide this point's x & y coordinates by a factor,
50800 * yielding a new point.
50801 * @param {Point} k factor
50802 * @return {Point} output point
50804 div: function div(k) {
50805 return this.clone()._div(k);
50809 * Rotate this point around the 0, 0 origin by an angle a,
50811 * @param {Number} a angle to rotate around, in radians
50812 * @return {Point} output point
50814 rotate: function rotate(a) {
50815 return this.clone()._rotate(a);
50819 * Rotate this point around p point by an angle a,
50821 * @param {Number} a angle to rotate around, in radians
50822 * @param {Point} p Point to rotate around
50823 * @return {Point} output point
50825 rotateAround: function rotateAround(a, p) {
50826 return this.clone()._rotateAround(a, p);
50830 * Multiply this point by a 4x1 transformation matrix
50831 * @param {Array<Number>} m transformation matrix
50832 * @return {Point} output point
50834 matMult: function matMult(m) {
50835 return this.clone()._matMult(m);
50839 * Calculate this point but as a unit vector from 0, 0, meaning
50840 * that the distance from the resulting point to the 0, 0
50841 * coordinate will be equal to 1 and the angle from the resulting
50842 * point to the 0, 0 coordinate will be the same as before.
50843 * @return {Point} unit vector point
50845 unit: function unit() {
50846 return this.clone()._unit();
50850 * Compute a perpendicular point, where the new y coordinate
50851 * is the old x coordinate and the new x coordinate is the old y
50852 * coordinate multiplied by -1
50853 * @return {Point} perpendicular point
50855 perp: function perp() {
50856 return this.clone()._perp();
50860 * Return a version of this point with the x & y coordinates
50861 * rounded to integers.
50862 * @return {Point} rounded point
50864 round: function round() {
50865 return this.clone()._round();
50869 * Return the magitude of this point: this is the Euclidean
50870 * distance from the 0, 0 coordinate to this point's x and y
50872 * @return {Number} magnitude
50874 mag: function mag() {
50875 return Math.sqrt(this.x * this.x + this.y * this.y);
50879 * Judge whether this point is equal to another point, returning
50881 * @param {Point} other the other point
50882 * @return {boolean} whether the points are equal
50884 equals: function equals(other) {
50885 return this.x === other.x && this.y === other.y;
50889 * Calculate the distance from this point to another point
50890 * @param {Point} p the other point
50891 * @return {Number} distance
50893 dist: function dist(p) {
50894 return Math.sqrt(this.distSqr(p));
50898 * Calculate the distance from this point to another point,
50899 * without the square root step. Useful if you're comparing
50900 * relative distances.
50901 * @param {Point} p the other point
50902 * @return {Number} distance
50904 distSqr: function distSqr(p) {
50905 var dx = p.x - this.x,
50907 return dx * dx + dy * dy;
50911 * Get the angle from the 0, 0 coordinate to this point, in radians
50913 * @return {Number} angle
50915 angle: function angle() {
50916 return Math.atan2(this.y, this.x);
50920 * Get the angle from this point to another point, in radians
50921 * @param {Point} b the other point
50922 * @return {Number} angle
50924 angleTo: function angleTo(b) {
50925 return Math.atan2(this.y - b.y, this.x - b.x);
50929 * Get the angle between this point and another point, in radians
50930 * @param {Point} b the other point
50931 * @return {Number} angle
50933 angleWith: function angleWith(b) {
50934 return this.angleWithSep(b.x, b.y);
50938 * Find the angle of the two vectors, solving the formula for
50939 * the cross product a x b = |a||b|sin(θ) for θ.
50940 * @param {Number} x the x-coordinate
50941 * @param {Number} y the y-coordinate
50942 * @return {Number} the angle in radians
50944 angleWithSep: function angleWithSep(x, y) {
50945 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
50947 _matMult: function _matMult(m) {
50948 var x = m[0] * this.x + m[1] * this.y,
50949 y = m[2] * this.x + m[3] * this.y;
50954 _add: function _add(p) {
50959 _sub: function _sub(p) {
50964 _mult: function _mult(k) {
50969 _div: function _div(k) {
50974 _multByPoint: function _multByPoint(p) {
50979 _divByPoint: function _divByPoint(p) {
50984 _unit: function _unit() {
50985 this._div(this.mag());
50989 _perp: function _perp() {
50995 _rotate: function _rotate(angle) {
50996 var cos = Math.cos(angle),
50997 sin = Math.sin(angle),
50998 x = cos * this.x - sin * this.y,
50999 y = sin * this.x + cos * this.y;
51004 _rotateAround: function _rotateAround(angle, p) {
51005 var cos = Math.cos(angle),
51006 sin = Math.sin(angle),
51007 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51008 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51013 _round: function _round() {
51014 this.x = Math.round(this.x);
51015 this.y = Math.round(this.y);
51020 * Construct a point from an array if necessary, otherwise if the input
51021 * is already a Point, or an unknown type, return it unchanged
51022 * @param {Array<Number>|Point|*} a any kind of input value
51023 * @return {Point} constructed point, or passed-through value.
51026 * var point = Point.convert([0, 1]);
51027 * // is equivalent to
51028 * var point = new Point(0, 1);
51031 Point.convert = function (a) {
51032 if (a instanceof Point) {
51036 if (Array.isArray(a)) {
51037 return new Point(a[0], a[1]);
51043 var vectortilefeature = VectorTileFeature$1;
51045 function VectorTileFeature$1(pbf, end, extent, keys, values) {
51047 this.properties = {};
51048 this.extent = extent;
51049 this.type = 0; // Private
51052 this._geometry = -1;
51054 this._values = values;
51055 pbf.readFields(readFeature, this, end);
51058 function readFeature(tag, feature, pbf) {
51059 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;
51062 function readTag(pbf, feature) {
51063 var end = pbf.readVarint() + pbf.pos;
51065 while (pbf.pos < end) {
51066 var key = feature._keys[pbf.readVarint()],
51067 value = feature._values[pbf.readVarint()];
51069 feature.properties[key] = value;
51073 VectorTileFeature$1.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51075 VectorTileFeature$1.prototype.loadGeometry = function () {
51076 var pbf = this._pbf;
51077 pbf.pos = this._geometry;
51078 var end = pbf.readVarint() + pbf.pos,
51086 while (pbf.pos < end) {
51088 var cmdLen = pbf.readVarint();
51089 cmd = cmdLen & 0x7;
51090 length = cmdLen >> 3;
51095 if (cmd === 1 || cmd === 2) {
51096 x += pbf.readSVarint();
51097 y += pbf.readSVarint();
51101 if (line) lines.push(line);
51105 line.push(new pointGeometry(x, y));
51106 } else if (cmd === 7) {
51107 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51109 line.push(line[0].clone()); // closePolygon
51112 throw new Error('unknown command ' + cmd);
51116 if (line) lines.push(line);
51120 VectorTileFeature$1.prototype.bbox = function () {
51121 var pbf = this._pbf;
51122 pbf.pos = this._geometry;
51123 var end = pbf.readVarint() + pbf.pos,
51133 while (pbf.pos < end) {
51135 var cmdLen = pbf.readVarint();
51136 cmd = cmdLen & 0x7;
51137 length = cmdLen >> 3;
51142 if (cmd === 1 || cmd === 2) {
51143 x += pbf.readSVarint();
51144 y += pbf.readSVarint();
51145 if (x < x1) x1 = x;
51146 if (x > x2) x2 = x;
51147 if (y < y1) y1 = y;
51148 if (y > y2) y2 = y;
51149 } else if (cmd !== 7) {
51150 throw new Error('unknown command ' + cmd);
51154 return [x1, y1, x2, y2];
51157 VectorTileFeature$1.prototype.toGeoJSON = function (x, y, z) {
51158 var size = this.extent * Math.pow(2, z),
51159 x0 = this.extent * x,
51160 y0 = this.extent * y,
51161 coords = this.loadGeometry(),
51162 type = VectorTileFeature$1.types[this.type],
51166 function project(line) {
51167 for (var j = 0; j < line.length; j++) {
51169 y2 = 180 - (p.y + y0) * 360 / size;
51170 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51174 switch (this.type) {
51178 for (i = 0; i < coords.length; i++) {
51179 points[i] = coords[i][0];
51187 for (i = 0; i < coords.length; i++) {
51188 project(coords[i]);
51194 coords = classifyRings(coords);
51196 for (i = 0; i < coords.length; i++) {
51197 for (j = 0; j < coords[i].length; j++) {
51198 project(coords[i][j]);
51205 if (coords.length === 1) {
51206 coords = coords[0];
51208 type = 'Multi' + type;
51215 coordinates: coords
51217 properties: this.properties
51220 if ('id' in this) {
51221 result.id = this.id;
51225 }; // classifies an array of rings into polygons with outer rings and holes
51228 function classifyRings(rings) {
51229 var len = rings.length;
51230 if (len <= 1) return [rings];
51235 for (var i = 0; i < len; i++) {
51236 var area = signedArea(rings[i]);
51237 if (area === 0) continue;
51238 if (ccw === undefined) ccw = area < 0;
51240 if (ccw === area < 0) {
51241 if (polygon) polygons.push(polygon);
51242 polygon = [rings[i]];
51244 polygon.push(rings[i]);
51248 if (polygon) polygons.push(polygon);
51252 function signedArea(ring) {
51255 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
51258 sum += (p2.x - p1.x) * (p1.y + p2.y);
51264 var vectortilelayer = VectorTileLayer$1;
51266 function VectorTileLayer$1(pbf, end) {
51270 this.extent = 4096;
51271 this.length = 0; // Private
51276 this._features = [];
51277 pbf.readFields(readLayer, this, end);
51278 this.length = this._features.length;
51281 function readLayer(tag, layer, pbf) {
51282 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));
51285 function readValueMessage(pbf) {
51287 end = pbf.readVarint() + pbf.pos;
51289 while (pbf.pos < end) {
51290 var tag = pbf.readVarint() >> 3;
51291 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;
51295 } // return feature `i` from this layer as a `VectorTileFeature`
51298 VectorTileLayer$1.prototype.feature = function (i) {
51299 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
51300 this._pbf.pos = this._features[i];
51302 var end = this._pbf.readVarint() + this._pbf.pos;
51304 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
51307 var vectortile = VectorTile$1;
51309 function VectorTile$1(pbf, end) {
51310 this.layers = pbf.readFields(readTile, {}, end);
51313 function readTile(tag, layers, pbf) {
51315 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
51316 if (layer.length) layers[layer.name] = layer;
51320 var VectorTile = vectortile;
51321 var VectorTileFeature = vectortilefeature;
51322 var VectorTileLayer = vectortilelayer;
51324 VectorTile: VectorTile,
51325 VectorTileFeature: VectorTileFeature,
51326 VectorTileLayer: VectorTileLayer
51329 var accessToken = 'MLY|4100327730013843|5bb78b81720791946a9a7b956c57b7cf';
51330 var apiUrl = 'https://graph.mapillary.com/';
51331 var baseTileUrl = 'https://tiles.mapillary.com/maps/vtp';
51332 var mapFeatureTileUrl = "".concat(baseTileUrl, "/mly_map_feature_point/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51333 var tileUrl = "".concat(baseTileUrl, "/mly1_public/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51334 var trafficSignTileUrl = "".concat(baseTileUrl, "/mly_map_feature_traffic_sign/2/{z}/{x}/{y}?access_token=").concat(accessToken);
51335 var viewercss = 'mapillary-js/mapillary.css';
51336 var viewerjs = 'mapillary-js/mapillary.js';
51337 var minZoom$1 = 14;
51338 var dispatch$4 = dispatch$8('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged');
51340 var _loadViewerPromise$2;
51342 var _mlyActiveImage;
51346 var _mlyFallback = false;
51348 var _mlyHighlightedDetection;
51350 var _mlyShowFeatureDetections = false;
51351 var _mlyShowSignDetections = false;
51355 var _mlyViewerFilter = ['all']; // Load all data for the specified type from Mapillary vector tiles
51357 function loadTiles$2(which, url, maxZoom, projection) {
51358 var tiler = utilTiler().zoomExtent([minZoom$1, maxZoom]).skipNullIsland(true);
51359 var tiles = tiler.getTiles(projection);
51360 tiles.forEach(function (tile) {
51361 loadTile$1(which, url, tile);
51363 } // Load all data for the specified type from one vector tile
51366 function loadTile$1(which, url, tile) {
51367 var cache = _mlyCache.requests;
51368 var tileId = "".concat(tile.id, "-").concat(which);
51369 if (cache.loaded[tileId] || cache.inflight[tileId]) return;
51370 var controller = new AbortController();
51371 cache.inflight[tileId] = controller;
51372 var requestUrl = url.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]).replace('{z}', tile.xyz[2]);
51373 fetch(requestUrl, {
51374 signal: controller.signal
51375 }).then(function (response) {
51376 if (!response.ok) {
51377 throw new Error(response.status + ' ' + response.statusText);
51380 cache.loaded[tileId] = true;
51381 delete cache.inflight[tileId];
51382 return response.arrayBuffer();
51383 }).then(function (data) {
51385 throw new Error('No Data');
51388 loadTileDataToCache(data, tile, which);
51390 if (which === 'images') {
51391 dispatch$4.call('loadedImages');
51392 } else if (which === 'signs') {
51393 dispatch$4.call('loadedSigns');
51394 } else if (which === 'points') {
51395 dispatch$4.call('loadedMapFeatures');
51397 })["catch"](function () {
51398 cache.loaded[tileId] = true;
51399 delete cache.inflight[tileId];
51401 } // Load the data from the vector tile into cache
51404 function loadTileDataToCache(data, tile, which) {
51405 var vectorTile = new VectorTile(new pbf(data));
51406 var features, cache, layer, i, feature, loc, d;
51408 if (vectorTile.layers.hasOwnProperty('image')) {
51410 cache = _mlyCache.images;
51411 layer = vectorTile.layers.image;
51413 for (i = 0; i < layer.length; i++) {
51414 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51415 loc = feature.geometry.coordinates;
51418 captured_at: feature.properties.captured_at,
51419 ca: feature.properties.compass_angle,
51420 id: feature.properties.id,
51421 is_pano: feature.properties.is_pano,
51422 sequence_id: feature.properties.sequence_id
51424 cache.forImageId[d.id] = d;
51435 cache.rtree.load(features);
51439 if (vectorTile.layers.hasOwnProperty('sequence')) {
51441 cache = _mlyCache.sequences;
51442 layer = vectorTile.layers.sequence;
51444 for (i = 0; i < layer.length; i++) {
51445 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51447 if (cache.lineString[feature.properties.id]) {
51448 cache.lineString[feature.properties.id].push(feature);
51450 cache.lineString[feature.properties.id] = [feature];
51455 if (vectorTile.layers.hasOwnProperty('point')) {
51457 cache = _mlyCache[which];
51458 layer = vectorTile.layers.point;
51460 for (i = 0; i < layer.length; i++) {
51461 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51462 loc = feature.geometry.coordinates;
51465 id: feature.properties.id,
51466 first_seen_at: feature.properties.first_seen_at,
51467 last_seen_at: feature.properties.last_seen_at,
51468 value: feature.properties.value
51480 cache.rtree.load(features);
51484 if (vectorTile.layers.hasOwnProperty('traffic_sign')) {
51486 cache = _mlyCache[which];
51487 layer = vectorTile.layers.traffic_sign;
51489 for (i = 0; i < layer.length; i++) {
51490 feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
51491 loc = feature.geometry.coordinates;
51494 id: feature.properties.id,
51495 first_seen_at: feature.properties.first_seen_at,
51496 last_seen_at: feature.properties.last_seen_at,
51497 value: feature.properties.value
51509 cache.rtree.load(features);
51512 } // Get data from the API
51515 function loadData(url) {
51516 return fetch(url).then(function (response) {
51517 if (!response.ok) {
51518 throw new Error(response.status + ' ' + response.statusText);
51521 return response.json();
51522 }).then(function (result) {
51527 return result.data || [];
51529 } // Partition viewport into higher zoom tiles
51532 function partitionViewport$2(projection) {
51533 var z = geoScaleToZoom(projection.scale());
51534 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
51536 var tiler = utilTiler().zoomExtent([z2, z2]);
51537 return tiler.getTiles(projection).map(function (tile) {
51538 return tile.extent;
51540 } // Return no more than `limit` results per partition.
51543 function searchLimited$2(limit, projection, rtree) {
51544 limit = limit || 5;
51545 return partitionViewport$2(projection).reduce(function (result, extent) {
51546 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
51549 return found.length ? result.concat(found) : result;
51553 var serviceMapillary = {
51554 // Initialize Mapillary
51555 init: function init() {
51560 this.event = utilRebind(this, dispatch$4, 'on');
51562 // Reset cache and state
51563 reset: function reset() {
51565 Object.values(_mlyCache.requests.inflight).forEach(function (request) {
51572 rtree: new RBush(),
51575 image_detections: {
51585 rtree: new RBush(),
51593 _mlyActiveImage = null;
51595 // Get visible images
51596 images: function images(projection) {
51598 return searchLimited$2(limit, projection, _mlyCache.images.rtree);
51600 // Get visible traffic signs
51601 signs: function signs(projection) {
51603 return searchLimited$2(limit, projection, _mlyCache.signs.rtree);
51605 // Get visible map (point) features
51606 mapFeatures: function mapFeatures(projection) {
51608 return searchLimited$2(limit, projection, _mlyCache.points.rtree);
51610 // Get cached image by id
51611 cachedImage: function cachedImage(imageId) {
51612 return _mlyCache.images.forImageId[imageId];
51614 // Get visible sequences
51615 sequences: function sequences(projection) {
51616 var viewport = projection.clipExtent();
51617 var min = [viewport[0][0], viewport[1][1]];
51618 var max = [viewport[1][0], viewport[0][1]];
51619 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
51620 var sequenceIds = {};
51621 var lineStrings = [];
51623 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
51624 if (d.data.sequence_id) {
51625 sequenceIds[d.data.sequence_id] = true;
51629 Object.keys(sequenceIds).forEach(function (sequenceId) {
51630 if (_mlyCache.sequences.lineString[sequenceId]) {
51631 lineStrings = lineStrings.concat(_mlyCache.sequences.lineString[sequenceId]);
51634 return lineStrings;
51636 // Load images in the visible area
51637 loadImages: function loadImages(projection) {
51638 loadTiles$2('images', tileUrl, 14, projection);
51640 // Load traffic signs in the visible area
51641 loadSigns: function loadSigns(projection) {
51642 loadTiles$2('signs', trafficSignTileUrl, 14, projection);
51644 // Load map (point) features in the visible area
51645 loadMapFeatures: function loadMapFeatures(projection) {
51646 loadTiles$2('points', mapFeatureTileUrl, 14, projection);
51648 // Return a promise that resolves when the image viewer (Mapillary JS) library has finished loading
51649 ensureViewerLoaded: function ensureViewerLoaded(context) {
51650 if (_loadViewerPromise$2) return _loadViewerPromise$2; // add mly-wrapper
51652 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
51653 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
51655 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
51656 var loadedCount = 0;
51658 function loaded() {
51659 loadedCount += 1; // wait until both files are loaded
51661 if (loadedCount === 2) resolve();
51664 var head = select('head'); // load mapillary-viewercss
51666 head.selectAll('#ideditor-mapillary-viewercss').data([0]).enter().append('link').attr('id', 'ideditor-mapillary-viewercss').attr('rel', 'stylesheet').attr('crossorigin', 'anonymous').attr('href', context.asset(viewercss)).on('load.serviceMapillary', loaded).on('error.serviceMapillary', function () {
51668 }); // load mapillary-viewerjs
51670 head.selectAll('#ideditor-mapillary-viewerjs').data([0]).enter().append('script').attr('id', 'ideditor-mapillary-viewerjs').attr('crossorigin', 'anonymous').attr('src', context.asset(viewerjs)).on('load.serviceMapillary', loaded).on('error.serviceMapillary', function () {
51673 })["catch"](function () {
51674 _loadViewerPromise$2 = null;
51675 }).then(function () {
51676 that.initViewer(context);
51678 return _loadViewerPromise$2;
51680 // Load traffic sign image sprites
51681 loadSignResources: function loadSignResources(context) {
51682 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
51683 /* don't override colors */
51687 // Load map (point) feature image sprites
51688 loadObjectResources: function loadObjectResources(context) {
51689 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
51690 /* don't override colors */
51694 // Remove previous detections in image viewer
51695 resetTags: function resetTags() {
51696 if (_mlyViewer && !_mlyFallback) {
51697 _mlyViewer.getComponent('tag').removeAll();
51700 // Show map feature detections in image viewer
51701 showFeatureDetections: function showFeatureDetections(value) {
51702 _mlyShowFeatureDetections = value;
51704 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51708 // Show traffic sign detections in image viewer
51709 showSignDetections: function showSignDetections(value) {
51710 _mlyShowSignDetections = value;
51712 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
51716 // Apply filter to image viewer
51717 filterViewer: function filterViewer(context) {
51718 var showsPano = context.photos().showsPanoramic();
51719 var showsFlat = context.photos().showsFlat();
51720 var fromDate = context.photos().fromDate();
51721 var toDate = context.photos().toDate();
51722 var filter = ['all'];
51723 if (!showsPano) filter.push(['!=', 'cameraType', 'spherical']);
51724 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
51727 filter.push(['>=', 'capturedAt', new Date(fromDate).getTime()]);
51731 filter.push(['>=', 'capturedAt', new Date(toDate).getTime()]);
51735 _mlyViewer.setFilter(filter);
51738 _mlyViewerFilter = filter;
51741 // Make the image viewer visible
51742 showViewer: function showViewer(context) {
51743 var wrap = context.container().select('.photoviewer').classed('hide', false);
51744 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
51746 if (isHidden && _mlyViewer) {
51747 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
51748 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
51750 _mlyViewer.resize();
51755 // Hide the image viewer and resets map markers
51756 hideViewer: function hideViewer(context) {
51757 _mlyActiveImage = null;
51759 if (!_mlyFallback && _mlyViewer) {
51760 _mlyViewer.getComponent('sequence').stop();
51763 var viewer = context.container().select('.photoviewer');
51764 if (!viewer.empty()) viewer.datum(null);
51765 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
51766 this.updateUrlImage(null);
51767 dispatch$4.call('imageChanged');
51768 dispatch$4.call('loadedMapFeatures');
51769 dispatch$4.call('loadedSigns');
51770 return this.setStyles(context, null);
51772 // Update the URL with current image id
51773 updateUrlImage: function updateUrlImage(imageId) {
51774 if (!window.mocha) {
51775 var hash = utilStringQs(window.location.hash);
51778 hash.photo = 'mapillary/' + imageId;
51783 window.location.replace('#' + utilQsString(hash, true));
51786 // Highlight the detection in the viewer that is related to the clicked map feature
51787 highlightDetection: function highlightDetection(detection) {
51789 _mlyHighlightedDetection = detection.id;
51794 // Initialize image viewer (Mapillar JS)
51795 initViewer: function initViewer(context) {
51797 if (!window.mapillary) return;
51799 accessToken: accessToken,
51805 container: 'ideditor-mly'
51806 }; // Disable components requiring WebGL support
51808 if (!mapillary.isSupported() && mapillary.isFallbackSupported()) {
51809 _mlyFallback = true;
51820 navigation: true // fallback
51825 _mlyViewer = new mapillary.Viewer(opts);
51827 _mlyViewer.on('image', imageChanged);
51829 _mlyViewer.on('bearing', bearingChanged);
51831 if (_mlyViewerFilter) {
51832 _mlyViewer.setFilter(_mlyViewerFilter);
51833 } // Register viewer resize handler
51836 context.ui().photoviewer.on('resize.mapillary', function () {
51837 if (_mlyViewer) _mlyViewer.resize();
51838 }); // imageChanged: called after the viewer has changed images and is ready.
51840 function imageChanged(node) {
51842 var image = node.image;
51843 that.setActiveImage(image);
51844 that.setStyles(context, null);
51845 var loc = [image.originalLngLat.lng, image.originalLngLat.lat];
51846 context.map().centerEase(loc);
51847 that.updateUrlImage(image.id);
51849 if (_mlyShowFeatureDetections || _mlyShowSignDetections) {
51850 that.updateDetections(image.id, "".concat(apiUrl, "/").concat(image.id, "/detections?access_token=").concat(accessToken, "&fields=id,image,geometry,value"));
51853 dispatch$4.call('imageChanged');
51854 } // bearingChanged: called when the bearing changes in the image viewer.
51857 function bearingChanged(e) {
51858 dispatch$4.call('bearingChanged', undefined, e);
51861 // Move to an image
51862 selectImage: function selectImage(context, imageId) {
51863 if (_mlyViewer && imageId) {
51864 _mlyViewer.moveTo(imageId)["catch"](function (e) {
51865 console.error('mly3', e); // eslint-disable-line no-console
51871 // Return the currently displayed image
51872 getActiveImage: function getActiveImage() {
51873 return _mlyActiveImage;
51875 // Return a list of detection objects for the given id
51876 getDetections: function getDetections(id) {
51877 return loadData("".concat(apiUrl, "/").concat(id, "/detections?access_token=").concat(accessToken, "&fields=id,value,image"));
51879 // Set the currently visible image
51880 setActiveImage: function setActiveImage(image) {
51882 _mlyActiveImage = {
51883 ca: image.originalCompassAngle,
51885 loc: [image.originalLngLat.lng, image.originalLngLat.lat],
51886 is_pano: image.cameraType === 'spherical',
51887 sequence_id: image.sequenceId
51890 _mlyActiveImage = null;
51893 // Update the currently highlighted sequence and selected bubble.
51894 setStyles: function setStyles(context, hovered) {
51895 var hoveredImageId = hovered && hovered.id;
51896 var hoveredSequenceId = hovered && hovered.sequence_id;
51897 var selectedSequenceId = _mlyActiveImage && _mlyActiveImage.sequence_id;
51898 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
51899 return d.sequence_id === selectedSequenceId || d.id === hoveredImageId;
51900 }).classed('hovered', function (d) {
51901 return d.id === hoveredImageId;
51903 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
51904 return d.properties.id === hoveredSequenceId;
51905 }).classed('currentView', function (d) {
51906 return d.properties.id === selectedSequenceId;
51910 // Get detections for the current image and shows them in the image viewer
51911 updateDetections: function updateDetections(imageId, url) {
51912 if (!_mlyViewer || _mlyFallback) return;
51913 if (!imageId) return;
51914 var cache = _mlyCache.image_detections;
51916 if (cache.forImageId[imageId]) {
51917 showDetections(_mlyCache.image_detections.forImageId[imageId]);
51919 loadData(url).then(function (detections) {
51920 detections.forEach(function (detection) {
51921 if (!cache.forImageId[imageId]) {
51922 cache.forImageId[imageId] = [];
51925 cache.forImageId[imageId].push({
51926 geometry: detection.geometry,
51929 value: detection.value
51932 showDetections(_mlyCache.image_detections.forImageId[imageId] || []);
51934 } // Create a tag for each detection and shows it in the image viewer
51937 function showDetections(detections) {
51938 var tagComponent = _mlyViewer.getComponent('tag');
51940 detections.forEach(function (data) {
51941 var tag = makeTag(data);
51944 tagComponent.add([tag]);
51947 } // Create a Mapillary JS tag object
51950 function makeTag(data) {
51951 var valueParts = data.value.split('--');
51952 if (!valueParts.length) return;
51955 var color = 0xffffff;
51957 if (_mlyHighlightedDetection === data.id) {
51959 text = valueParts[1];
51961 if (text === 'flat' || text === 'discrete' || text === 'sign') {
51962 text = valueParts[2];
51965 text = text.replace(/-/g, ' ');
51966 text = text.charAt(0).toUpperCase() + text.slice(1);
51967 _mlyHighlightedDetection = null;
51970 var decodedGeometry = window.atob(data.geometry);
51971 var uintArray = new Uint8Array(decodedGeometry.length);
51973 for (var i = 0; i < decodedGeometry.length; i++) {
51974 uintArray[i] = decodedGeometry.charCodeAt(i);
51977 var tile = new VectorTile(new pbf(uintArray.buffer));
51978 var layer = tile.layers['mpy-or'];
51979 var geometries = layer.feature(0).loadGeometry();
51980 var polygon = geometries.map(function (ring) {
51981 return ring.map(function (point) {
51982 return [point.x / layer.extent, point.y / layer.extent];
51985 tag = new mapillary.OutlineTag(data.id, new mapillary.PolygonGeometry(polygon[0]), {
51996 // Return the current cache
51997 cache: function cache() {
52002 function validationIssue(attrs) {
52003 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
52005 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
52007 this.severity = attrs.severity; // required - 'warning' or 'error'
52009 this.message = attrs.message; // required - function returning localized string
52011 this.reference = attrs.reference; // optional - function(selection) to render reference information
52013 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
52015 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
52017 this.data = attrs.data; // optional - object containing extra data for the fixes
52019 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
52021 this.hash = attrs.hash; // optional - string to further differentiate the issue
52023 this.id = generateID.apply(this); // generated - see below
52025 this.autoFix = null; // generated - if autofix exists, will be set below
52026 // A unique, deterministic string hash.
52027 // Issues with identical id values are considered identical.
52029 function generateID() {
52030 var parts = [this.type];
52033 // subclasses can pass in their own differentiator
52034 parts.push(this.hash);
52037 if (this.subtype) {
52038 parts.push(this.subtype);
52039 } // include the entities this issue is for
52040 // (sort them so the id is deterministic)
52043 if (this.entityIds) {
52044 var entityKeys = this.entityIds.slice().sort();
52045 parts.push.apply(parts, entityKeys);
52048 return parts.join(':');
52051 this.extent = function (resolver) {
52053 return geoExtent(this.loc);
52056 if (this.entityIds && this.entityIds.length) {
52057 return this.entityIds.reduce(function (extent, entityId) {
52058 return extent.extend(resolver.entity(entityId).extent(resolver));
52065 this.fixes = function (context) {
52066 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
52069 if (issue.severity === 'warning') {
52070 // allow ignoring any issue that's not an error
52071 fixes.push(new validationIssueFix({
52072 title: _t.html('issues.fix.ignore_issue.title'),
52073 icon: 'iD-icon-close',
52074 onClick: function onClick() {
52075 context.validator().ignoreIssue(this.issue.id);
52080 fixes.forEach(function (fix) {
52081 // the id doesn't matter as long as it's unique to this issue/fix
52082 fix.id = fix.title; // add a reference to the issue for use in actions
52086 if (fix.autoArgs) {
52087 issue.autoFix = fix;
52093 function validationIssueFix(attrs) {
52094 this.title = attrs.title; // Required
52096 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
52098 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
52100 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
52102 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
52104 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
52106 this.issue = null; // Generated link - added by validationIssue
52109 var buildRuleChecks = function buildRuleChecks() {
52111 equals: function equals(_equals) {
52112 return function (tags) {
52113 return Object.keys(_equals).every(function (k) {
52114 return _equals[k] === tags[k];
52118 notEquals: function notEquals(_notEquals) {
52119 return function (tags) {
52120 return Object.keys(_notEquals).some(function (k) {
52121 return _notEquals[k] !== tags[k];
52125 absence: function absence(_absence) {
52126 return function (tags) {
52127 return Object.keys(tags).indexOf(_absence) === -1;
52130 presence: function presence(_presence) {
52131 return function (tags) {
52132 return Object.keys(tags).indexOf(_presence) > -1;
52135 greaterThan: function greaterThan(_greaterThan) {
52136 var key = Object.keys(_greaterThan)[0];
52137 var value = _greaterThan[key];
52138 return function (tags) {
52139 return tags[key] > value;
52142 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
52143 var key = Object.keys(_greaterThanEqual)[0];
52144 var value = _greaterThanEqual[key];
52145 return function (tags) {
52146 return tags[key] >= value;
52149 lessThan: function lessThan(_lessThan) {
52150 var key = Object.keys(_lessThan)[0];
52151 var value = _lessThan[key];
52152 return function (tags) {
52153 return tags[key] < value;
52156 lessThanEqual: function lessThanEqual(_lessThanEqual) {
52157 var key = Object.keys(_lessThanEqual)[0];
52158 var value = _lessThanEqual[key];
52159 return function (tags) {
52160 return tags[key] <= value;
52163 positiveRegex: function positiveRegex(_positiveRegex) {
52164 var tagKey = Object.keys(_positiveRegex)[0];
52166 var expression = _positiveRegex[tagKey].join('|');
52168 var regex = new RegExp(expression);
52169 return function (tags) {
52170 return regex.test(tags[tagKey]);
52173 negativeRegex: function negativeRegex(_negativeRegex) {
52174 var tagKey = Object.keys(_negativeRegex)[0];
52176 var expression = _negativeRegex[tagKey].join('|');
52178 var regex = new RegExp(expression);
52179 return function (tags) {
52180 return !regex.test(tags[tagKey]);
52186 var buildLineKeys = function buildLineKeys() {
52202 var serviceMapRules = {
52203 init: function init() {
52204 this._ruleChecks = buildRuleChecks();
52205 this._validationRules = [];
52206 this._areaKeys = osmAreaKeys;
52207 this._lineKeys = buildLineKeys();
52209 // list of rules only relevant to tag checks...
52210 filterRuleChecks: function filterRuleChecks(selector) {
52211 var _ruleChecks = this._ruleChecks;
52212 return Object.keys(selector).reduce(function (rules, key) {
52213 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
52214 rules.push(_ruleChecks[key](selector[key]));
52220 // builds tagMap from mapcss-parse selector object...
52221 buildTagMap: function buildTagMap(selector) {
52222 var getRegexValues = function getRegexValues(regexes) {
52223 return regexes.map(function (regex) {
52224 return regex.replace(/\$|\^/g, '');
52228 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
52230 var isRegex = /regex/gi.test(key);
52231 var isEqual = /equals/gi.test(key);
52233 if (isRegex || isEqual) {
52234 Object.keys(selector[key]).forEach(function (selectorKey) {
52235 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
52237 if (expectedTags.hasOwnProperty(selectorKey)) {
52238 values = values.concat(expectedTags[selectorKey]);
52241 expectedTags[selectorKey] = values;
52243 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
52244 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
52245 values = [selector[key][tagKey]];
52247 if (expectedTags.hasOwnProperty(tagKey)) {
52248 values = values.concat(expectedTags[tagKey]);
52251 expectedTags[tagKey] = values;
52254 return expectedTags;
52258 // inspired by osmWay#isArea()
52259 inferGeometry: function inferGeometry(tagMap) {
52260 var _lineKeys = this._lineKeys;
52261 var _areaKeys = this._areaKeys;
52263 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
52264 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
52267 var keyValueImpliesLine = function keyValueImpliesLine(key) {
52268 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
52271 if (tagMap.hasOwnProperty('area')) {
52272 if (tagMap.area.indexOf('yes') > -1) {
52276 if (tagMap.area.indexOf('no') > -1) {
52281 for (var key in tagMap) {
52282 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
52286 if (key in _lineKeys && keyValueImpliesLine(key)) {
52293 // adds from mapcss-parse selector check...
52294 addRule: function addRule(selector) {
52296 // checks relevant to mapcss-selector
52297 checks: this.filterRuleChecks(selector),
52298 // true if all conditions for a tag error are true..
52299 matches: function matches(entity) {
52300 return this.checks.every(function (check) {
52301 return check(entity.tags);
52304 // borrowed from Way#isArea()
52305 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
52306 geometryMatches: function geometryMatches(entity, graph) {
52307 if (entity.type === 'node' || entity.type === 'relation') {
52308 return selector.geometry === entity.type;
52309 } else if (entity.type === 'way') {
52310 return this.inferredGeometry === entity.geometry(graph);
52313 // when geometries match and tag matches are present, return a warning...
52314 findIssues: function findIssues(entity, graph, issues) {
52315 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
52316 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
52317 var _message = selector[severity];
52318 issues.push(new validationIssue({
52320 severity: severity,
52321 message: function message() {
52324 entityIds: [entity.id]
52330 this._validationRules.push(rule);
52332 clearRules: function clearRules() {
52333 this._validationRules = [];
52335 // returns validationRules...
52336 validationRules: function validationRules() {
52337 return this._validationRules;
52339 // returns ruleChecks
52340 ruleChecks: function ruleChecks() {
52341 return this._ruleChecks;
52345 var apibase$2 = 'https://nominatim.openstreetmap.org/';
52346 var _inflight$2 = {};
52348 var _nominatimCache;
52350 var serviceNominatim = {
52351 init: function init() {
52353 _nominatimCache = new RBush();
52355 reset: function reset() {
52356 Object.values(_inflight$2).forEach(function (controller) {
52357 controller.abort();
52360 _nominatimCache = new RBush();
52362 countryCode: function countryCode(location, callback) {
52363 this.reverse(location, function (err, result) {
52365 return callback(err);
52366 } else if (result.address) {
52367 return callback(null, result.address.country_code);
52369 return callback('Unable to geocode', null);
52373 reverse: function reverse(loc, callback) {
52374 var cached = _nominatimCache.search({
52381 if (cached.length > 0) {
52382 if (callback) callback(null, cached[0].data);
52393 var url = apibase$2 + 'reverse?' + utilQsString(params);
52394 if (_inflight$2[url]) return;
52395 var controller = new AbortController();
52396 _inflight$2[url] = controller;
52398 signal: controller.signal
52399 }).then(function (result) {
52400 delete _inflight$2[url];
52402 if (result && result.error) {
52403 throw new Error(result.error);
52406 var extent = geoExtent(loc).padByMeters(200);
52408 _nominatimCache.insert(Object.assign(extent.bbox(), {
52412 if (callback) callback(null, result);
52413 })["catch"](function (err) {
52414 delete _inflight$2[url];
52415 if (err.name === 'AbortError') return;
52416 if (callback) callback(err.message);
52419 search: function search(val, callback) {
52420 var searchVal = encodeURIComponent(val);
52421 var url = apibase$2 + 'search/' + searchVal + '?limit=10&format=json';
52422 if (_inflight$2[url]) return;
52423 var controller = new AbortController();
52424 _inflight$2[url] = controller;
52426 signal: controller.signal
52427 }).then(function (result) {
52428 delete _inflight$2[url];
52430 if (result && result.error) {
52431 throw new Error(result.error);
52434 if (callback) callback(null, result);
52435 })["catch"](function (err) {
52436 delete _inflight$2[url];
52437 if (err.name === 'AbortError') return;
52438 if (callback) callback(err.message);
52443 // for punction see https://stackoverflow.com/a/21224179
52445 function simplify$1(str) {
52446 if (typeof str !== 'string') return '';
52447 return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i') // for BİM, İşbank - #5017
52448 .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\u2000-\u206f\u2cf9-\u2cfc\u2cfe\u2cff\u2d70\u2e00-\u2e7f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
52451 var matchGroups$1 = {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/beer","shop/beverages","shop/wine"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],clinic:["amenity/clinic","amenity/doctors","healthcare/clinic","healthcare/dialysis"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/grocery","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],dentist:["amenity/dentist","amenity/doctors","healthcare/dentist"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],fabric:["shop/fabric","shop/haberdashery","shop/sewing"],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/pub","amenity/bar","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/bathroom_furnishing","shop/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/hardware_store","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],hobby:["shop/electronics","shop/hobby","shop/books","shop/games","shop/collector","shop/toys","shop/model","shop/video_games","shop/anime"],hospital:["amenity/doctors","amenity/hospital","healthcare/hospital"],houseware:["shop/houseware","shop/interior_decoration"],lifeboat_station:["amenity/lifeboat_station","emergency/lifeboat_station","emergency/marine_rescue"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],office_supplies:["shop/office_supplies","shop/stationary","shop/stationery"],outdoor:["shop/outdoor","shop/sports"],pharmacy:["amenity/doctors","amenity/pharmacy","healthcare/pharmacy"],playground:["amenity/theme_park","leisure/amusement_arcade","leisure/playground"],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/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],storage:["shop/storage_units","shop/storage_rental"],weight_loss:["amenity/doctors","amenity/weight_clinic","healthcare/counselling","leisure/fitness_centre","office/therapist","shop/beauty","shop/diet","shop/food","shop/health_food","shop/herbalist","shop/nutrition","shop/nutrition_supplements","shop/weight_loss"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
52452 var matchGroupsJSON = {
52453 matchGroups: matchGroups$1
52456 var genericWords = ["^(barn|bazaa?r|bench|bou?tique|building|casa|church)$","^(baseball|basketball|football|soccer|softball|tennis(halle)?)\\s?(field|court)?$","^(club|green|out|ware)\\s?house$","^(driveway|el árbol|fountain|golf|government|graveyard)$","^(hofladen|librairie|magazine?|maison)$","^(mobile home|skate)?\\s?park$","^(n\\s?\\/?\\s?a|name|no\\s?name|none|null|temporary|test|unknown)$","^(obuwie|pond|pool|sale|shops?|sklep|stores?)$","^\\?+$","^tattoo( studio)?$","^windmill$","^церковная( лавка)?$"];
52457 var genericWordsJSON = {
52458 genericWords: genericWords
52461 var trees$1 = {brands:{emoji:"🍔",mainTag:"brand:wikidata",sourceTags:["brand","name"],nameTags:{primary:"^(name|name:\\w+)$",alternate:"^(brand|brand:\\w+|operator|operator:\\w+|\\w+_name|\\w+_name:\\w+)$"}},flags:{emoji:"🚩",mainTag:"flag:wikidata",nameTags:{primary:"^(flag:name|flag:name:\\w+)$",alternate:"^(country|country:\\w+|flag|flag:\\w+|subject|subject:\\w+)$"}},operators:{emoji:"💼",mainTag:"operator:wikidata",sourceTags:["operator"],nameTags:{primary:"^(name|name:\\w+|operator|operator:\\w+)$",alternate:"^(brand|brand:\\w+|\\w+_name|\\w+_name:\\w+)$"}},transit:{emoji:"🚇",mainTag:"network:wikidata",sourceTags:["network"],nameTags:{primary:"^network$",alternate:"^(operator|operator:\\w+|network:\\w+|\\w+_name|\\w+_name:\\w+)$"}}};
52466 var matchGroups = matchGroupsJSON.matchGroups;
52467 var trees = treesJSON.trees;
52468 var Matcher = /*#__PURE__*/function () {
52471 // initialize the genericWords regexes
52472 function Matcher() {
52475 _classCallCheck$1(this, Matcher);
52477 // The `matchIndex` is a specialized structure that allows us to quickly answer
52478 // _"Given a [key/value tagpair, name, location], what canonical items (brands etc) can match it?"_
52480 // The index contains all valid combinations of k/v tagpairs and names
52484 // 'primary': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `name`, `name:xx`, etc.
52485 // 'alternate': Map (String 'nsimple' -> Set (itemIDs…), // matches for tags like `alt_name`, `brand`, etc.
52486 // 'excludeNamed': Map (String 'pattern' -> RegExp),
52487 // 'excludeGeneric': Map (String 'pattern' -> RegExp)
52492 // 'amenity/bank': {
52494 // 'firstbank': Set ("firstbank-978cca", "firstbank-9794e6", "firstbank-f17495", …),
52498 // '1stbank': Set ("firstbank-f17495"),
52502 // 'shop/supermarket': {
52504 // 'coop': Set ("coop-76454b", "coop-ebf2d9", "coop-36e991", …),
52505 // 'coopfood': Set ("coopfood-a8278b", …),
52509 // 'coop': Set ("coopfood-a8278b", …),
52510 // 'federatedcooperatives': Set ("coop-76454b", …),
52511 // 'thecooperative': Set ("coopfood-a8278b", …),
52517 this.matchIndex = undefined; // The `genericWords` structure matches the contents of genericWords.json to instantiated RegExp objects
52518 // Map (String 'pattern' -> RegExp),
52520 this.genericWords = new Map();
52521 (genericWordsJSON.genericWords || []).forEach(function (s) {
52522 return _this.genericWords.set(s, new RegExp(s, 'i'));
52523 }); // The `itemLocation` structure maps itemIDs to locationSetIDs:
52525 // 'firstbank-f17495': '+[first_bank_western_us.geojson]',
52526 // 'firstbank-978cca': '+[first_bank_carolinas.geojson]',
52527 // 'coop-76454b': '+[Q16]',
52528 // 'coopfood-a8278b': '+[Q23666]',
52532 this.itemLocation = undefined; // The `locationSets` structure maps locationSetIDs to *resolved* locationSets:
52534 // '+[first_bank_western_us.geojson]': GeoJSON {…},
52535 // '+[first_bank_carolinas.geojson]': GeoJSON {…},
52536 // '+[Q16]': GeoJSON {…},
52537 // '+[Q23666]': GeoJSON {…},
52541 this.locationSets = undefined; // The `locationIndex` is an instance of which-polygon spatial index for the locationSets.
52543 this.locationIndex = undefined; // Array of match conflict pairs (currently unused)
52545 this.warnings = [];
52547 // `buildMatchIndex()`
52548 // Call this to prepare the matcher for use
52550 // `data` needs to be an Object indexed on a 'tree/key/value' path.
52551 // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52553 // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52554 // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] },
52560 _createClass$1(Matcher, [{
52561 key: "buildMatchIndex",
52562 value: function buildMatchIndex(data) {
52564 if (that.matchIndex) return; // it was built already
52566 that.matchIndex = new Map();
52567 Object.keys(data).forEach(function (tkv) {
52568 var category = data[tkv];
52569 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
52574 var thiskv = "".concat(k, "/").concat(v);
52575 var tree = trees[t];
52576 var branch = that.matchIndex.get(thiskv);
52580 primary: new Map(),
52581 alternate: new Map(),
52582 excludeGeneric: new Map(),
52583 excludeNamed: new Map()
52585 that.matchIndex.set(thiskv, branch);
52586 } // ADD EXCLUSIONS
52589 var properties = category.properties || {};
52590 var exclude = properties.exclude || {};
52591 (exclude.generic || []).forEach(function (s) {
52592 return branch.excludeGeneric.set(s, new RegExp(s, 'i'));
52594 (exclude.named || []).forEach(function (s) {
52595 return branch.excludeNamed.set(s, new RegExp(s, 'i'));
52597 var excludeRegexes = [].concat(_toConsumableArray(branch.excludeGeneric.values()), _toConsumableArray(branch.excludeNamed.values())); // ADD ITEMS
52599 var items = category.items;
52600 if (!Array.isArray(items) || !items.length) return; // Primary name patterns, match tags to take first
52601 // e.g. `name`, `name:ru`
52603 var primaryName = new RegExp(tree.nameTags.primary, 'i'); // Alternate name patterns, match tags to consider after primary
52604 // e.g. `alt_name`, `short_name`, `brand`, `brand:ru`, etc..
52606 var alternateName = new RegExp(tree.nameTags.alternate, 'i'); // There are a few exceptions to the name matching regexes.
52607 // Usually a tag suffix contains a language code like `name:en`, `name:ru`
52608 // but we want to exclude things like `operator:type`, `name:etymology`, etc..
52610 var notName = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52612 var skipGenericKV = skipGenericKVMatches(t, k, v); // We will collect the generic KV pairs anyway (for the purpose of filtering them out of matchTags)
52614 var genericKV = new Set(["".concat(k, "/yes"), "building/yes"]); // Collect alternate tagpairs for this kv category from matchGroups.
52615 // We might also pick up a few more generic KVs (like `shop/yes`)
52617 var matchGroupKV = new Set();
52618 Object.values(matchGroups).forEach(function (matchGroup) {
52619 var inGroup = matchGroup.some(function (otherkv) {
52620 return otherkv === thiskv;
52622 if (!inGroup) return;
52623 matchGroup.forEach(function (otherkv) {
52624 if (otherkv === thiskv) return; // skip self
52626 matchGroupKV.add(otherkv);
52627 var otherk = otherkv.split('/', 2)[0]; // we might pick up a `shop/yes`
52629 genericKV.add("".concat(otherk, "/yes"));
52631 }); // For each item, insert all [key, value, name] combinations into the match index
52633 items.forEach(function (item) {
52634 if (!item.id) return; // Automatically remove redundant `matchTags` - #3417
52635 // (i.e. This kv is already covered by matchGroups, so it doesn't need to be in `item.matchTags`)
52637 if (Array.isArray(item.matchTags) && item.matchTags.length) {
52638 item.matchTags = item.matchTags.filter(function (matchTag) {
52639 return !matchGroupKV.has(matchTag) && !genericKV.has(matchTag);
52641 if (!item.matchTags.length) delete item.matchTags;
52642 } // key/value tagpairs to insert into the match index..
52645 var kvTags = ["".concat(thiskv)].concat(item.matchTags || []);
52647 if (!skipGenericKV) {
52648 kvTags = kvTags.concat(Array.from(genericKV)); // #3454 - match some generic tags
52649 } // Index all the namelike tag values
52652 Object.keys(item.tags).forEach(function (osmkey) {
52653 if (notName.test(osmkey)) return; // osmkey is not a namelike tag, skip
52655 var osmvalue = item.tags[osmkey];
52656 if (!osmvalue || excludeRegexes.some(function (regex) {
52657 return regex.test(osmvalue);
52658 })) return; // osmvalue missing or excluded
52660 if (primaryName.test(osmkey)) {
52661 kvTags.forEach(function (kv) {
52662 return insertName('primary', kv, simplify$1(osmvalue), item.id);
52664 } else if (alternateName.test(osmkey)) {
52665 kvTags.forEach(function (kv) {
52666 return insertName('alternate', kv, simplify$1(osmvalue), item.id);
52669 }); // Index `matchNames` after indexing all other names..
52671 var keepMatchNames = new Set();
52672 (item.matchNames || []).forEach(function (matchName) {
52673 // If this matchname isn't already indexed, add it to the alternate index
52674 var nsimple = simplify$1(matchName);
52675 kvTags.forEach(function (kv) {
52676 var branch = that.matchIndex.get(kv);
52677 var primaryLeaf = branch && branch.primary.get(nsimple);
52678 var alternateLeaf = branch && branch.alternate.get(nsimple);
52679 var inPrimary = primaryLeaf && primaryLeaf.has(item.id);
52680 var inAlternate = alternateLeaf && alternateLeaf.has(item.id);
52682 if (!inPrimary && !inAlternate) {
52683 insertName('alternate', kv, nsimple, item.id);
52684 keepMatchNames.add(matchName);
52687 }); // Automatically remove redundant `matchNames` - #3417
52688 // (i.e. This name got indexed some other way, so it doesn't need to be in `item.matchNames`)
52690 if (keepMatchNames.size) {
52691 item.matchNames = Array.from(keepMatchNames);
52693 delete item.matchNames;
52697 // Insert this item into the matchIndex
52699 function insertName(which, kv, nsimple, itemID) {
52700 if (!nsimple) return;
52701 var branch = that.matchIndex.get(kv);
52705 primary: new Map(),
52706 alternate: new Map(),
52707 excludeGeneric: new Map(),
52708 excludeNamed: new Map()
52710 that.matchIndex.set(kv, branch);
52713 var leaf = branch[which].get(nsimple);
52717 branch[which].set(nsimple, leaf);
52720 leaf.add(itemID); // insert
52721 } // For certain categories we do not want to match generic KV pairs like `building/yes` or `amenity/yes`
52724 function skipGenericKVMatches(t, k, v) {
52725 return t === 'flags' || t === 'transit' || k === 'landuse' || v === 'atm' || v === 'bicycle_parking' || v === 'car_sharing' || v === 'caravan_site' || v === 'charging_station' || v === 'dog_park' || v === 'parking' || v === 'phone' || v === 'playground' || v === 'post_box' || v === 'public_bookcase' || v === 'recycling' || v === 'vending_machine';
52728 // `buildLocationIndex()`
52729 // Call this to prepare a which-polygon location index.
52730 // This *resolves* all the locationSets into GeoJSON, which takes some time.
52731 // You can skip this step if you don't care about matching within a location.
52733 // `data` needs to be an Object indexed on a 'tree/key/value' path.
52734 // (e.g. cache filled by `fileTree.read` or data found in `dist/nsi.json`)
52736 // 'brands/amenity/bank': { properties: {}, items: [ {}, {}, … ] },
52737 // 'brands/amenity/bar': { properties: {}, items: [ {}, {}, … ] },
52743 key: "buildLocationIndex",
52744 value: function buildLocationIndex(data, loco) {
52746 if (that.locationIndex) return; // it was built already
52748 that.itemLocation = new Map();
52749 that.locationSets = new Map();
52750 Object.keys(data).forEach(function (tkv) {
52751 var items = data[tkv].items;
52752 if (!Array.isArray(items) || !items.length) return;
52753 items.forEach(function (item) {
52754 if (that.itemLocation.has(item.id)) return; // we've seen item id already - shouldn't be possible?
52759 resolved = loco.resolveLocationSet(item.locationSet); // resolve a feature for this locationSet
52761 console.warn("buildLocationIndex: ".concat(err.message)); // couldn't resolve
52764 if (!resolved || !resolved.id) return;
52765 that.itemLocation.set(item.id, resolved.id); // link it to the item
52767 if (that.locationSets.has(resolved.id)) return; // we've seen this locationSet feature before..
52768 // First time seeing this locationSet feature, make a copy and add to locationSet cache..
52770 var feature = _cloneDeep(resolved.feature);
52772 feature.id = resolved.id; // Important: always use the locationSet `id` (`+[Q30]`), not the feature `id` (`Q30`)
52774 feature.properties.id = resolved.id;
52776 if (!feature.geometry.coordinates.length || !feature.properties.area) {
52777 console.warn("buildLocationIndex: locationSet ".concat(resolved.id, " for ").concat(item.id, " resolves to an empty feature:"));
52778 console.warn(JSON.stringify(feature));
52782 that.locationSets.set(resolved.id, feature);
52785 that.locationIndex = whichPolygon_1({
52786 type: 'FeatureCollection',
52787 features: _toConsumableArray(that.locationSets.values())
52790 function _cloneDeep(obj) {
52791 return JSON.parse(JSON.stringify(obj));
52795 // Pass parts and return an Array of matches.
52799 // `loc` - optional - [lon,lat] location to search
52801 // 1. If the [k,v,n] tuple matches a canonical item…
52802 // Return an Array of match results.
52803 // Each result will include the area in km² that the item is valid.
52805 // Order of results:
52806 // Primary ordering will be on the "match" column:
52807 // "primary" - where the query matches the `name` tag, followed by
52808 // "alternate" - where the query matches an alternate name tag (e.g. short_name, brand, operator, etc)
52809 // Secondary ordering will be on the "area" column:
52810 // "area descending" if no location was provided, (worldwide before local)
52811 // "area ascending" if location was provided (local before worldwide)
52814 // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String },
52815 // { match: 'primary', itemID: String, area: Number, kv: String, nsimple: String },
52816 // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String },
52817 // { match: 'alternate', itemID: String, area: Number, kv: String, nsimple: String },
52823 // 2. If the [k,v,n] tuple matches an exclude pattern…
52824 // Return an Array with a single exclude result, either
52826 // [ { match: 'excludeGeneric', pattern: String, kv: String } ] // "generic" e.g. "Food Court"
52828 // [ { match: 'excludeNamed', pattern: String, kv: String } ] // "named", e.g. "Kebabai"
52831 // "generic" - a generic word that is probably not really a name.
52832 // For these, iD should warn the user "Hey don't put 'food court' in the name tag".
52833 // "named" - a real name like "Kebabai" that is just common, but not a brand.
52834 // For these, iD should just let it be. We don't include these in NSI, but we don't want to nag users about it either.
52838 // 3. If the [k,v,n] tuple matches nothing of any kind, return `null`
52844 value: function match(k, v, n, loc) {
52847 if (!that.matchIndex) {
52848 throw new Error('match: matchIndex not built.');
52849 } // If we were supplied a location, and a that.locationIndex has been set up,
52850 // get the locationSets that are valid there so we can filter results.
52853 var matchLocations;
52855 if (Array.isArray(loc) && that.locationIndex) {
52856 // which-polygon query returns an array of GeoJSON properties, pass true to return all results
52857 matchLocations = that.locationIndex([loc[0], loc[1], loc[0], loc[1]], true);
52860 var nsimple = simplify$1(n);
52861 var seen = new Set();
52863 gatherResults('primary');
52864 gatherResults('alternate');
52865 if (results.length) return results;
52866 gatherResults('exclude');
52867 return results.length ? results : null;
52869 function gatherResults(which) {
52870 // First try an exact match on k/v
52871 var kv = "".concat(k, "/").concat(v);
52872 var didMatch = tryMatch(which, kv);
52873 if (didMatch) return; // If that didn't work, look in match groups for other pairs considered equivalent to k/v..
52875 for (var mg in matchGroups) {
52876 var matchGroup = matchGroups[mg];
52877 var inGroup = matchGroup.some(function (otherkv) {
52878 return otherkv === kv;
52880 if (!inGroup) continue;
52882 for (var i = 0; i < matchGroup.length; i++) {
52883 var otherkv = matchGroup[i];
52884 if (otherkv === kv) continue; // skip self
52886 didMatch = tryMatch(which, otherkv);
52887 if (didMatch) return;
52889 } // If finished 'exclude' pass and still haven't matched anything, try the global `genericWords.json` patterns
52892 if (which === 'exclude') {
52893 var regex = _toConsumableArray(that.genericWords.values()).find(function (regex) {
52894 return regex.test(n);
52899 match: 'excludeGeneric',
52900 pattern: String(regex)
52901 }); // note no `branch`, no `kv`
52908 function tryMatch(which, kv) {
52909 var branch = that.matchIndex.get(kv);
52910 if (!branch) return;
52912 if (which === 'exclude') {
52913 // Test name `n` against named and generic exclude patterns
52914 var regex = _toConsumableArray(branch.excludeNamed.values()).find(function (regex) {
52915 return regex.test(n);
52920 match: 'excludeNamed',
52921 pattern: String(regex),
52927 regex = _toConsumableArray(branch.excludeGeneric.values()).find(function (regex) {
52928 return regex.test(n);
52933 match: 'excludeGeneric',
52934 pattern: String(regex),
52943 var leaf = branch[which].get(nsimple);
52944 if (!leaf || !leaf.size) return; // If we get here, we matched something..
52945 // Prepare the results, calculate areas (if location index was set up)
52947 var hits = Array.from(leaf).map(function (itemID) {
52948 var area = Infinity;
52950 if (that.itemLocation && that.locationSets) {
52951 var location = that.locationSets.get(that.itemLocation.get(itemID));
52952 area = location && location.properties.area || Infinity;
52963 var sortFn = byAreaDescending; // Filter the match to include only results valid in the requested `loc`..
52965 if (matchLocations) {
52966 hits = hits.filter(isValidLocation);
52967 sortFn = byAreaAscending;
52970 if (!hits.length) return; // push results
52972 hits.sort(sortFn).forEach(function (hit) {
52973 if (seen.has(hit.itemID)) return;
52974 seen.add(hit.itemID);
52979 function isValidLocation(hit) {
52980 if (!that.itemLocation) return true;
52981 return matchLocations.find(function (props) {
52982 return props.id === that.itemLocation.get(hit.itemID);
52984 } // Sort smaller (more local) locations first.
52987 function byAreaAscending(hitA, hitB) {
52988 return hitA.area - hitB.area;
52989 } // Sort larger (more worldwide) locations first.
52992 function byAreaDescending(hitA, hitB) {
52993 return hitB.area - hitA.area;
52998 // Return any warnings discovered when buiding the index.
52999 // (currently this does nothing)
53003 key: "getWarnings",
53004 value: function getWarnings() {
53005 return this.warnings;
53013 * Checks if `value` is the
53014 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
53015 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
53021 * @param {*} value The value to check.
53022 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
53028 * _.isObject([1, 2, 3]);
53031 * _.isObject(_.noop);
53034 * _.isObject(null);
53037 function isObject$2(value) {
53038 var type = _typeof(value);
53040 return value != null && (type == 'object' || type == 'function');
53043 /** Detect free variable `global` from Node.js. */
53044 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
53046 /** Detect free variable `self`. */
53048 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
53049 /** Used as a reference to the global object. */
53051 var root = freeGlobal || freeSelf || Function('return this')();
53054 * Gets the timestamp of the number of milliseconds that have elapsed since
53055 * the Unix epoch (1 January 1970 00:00:00 UTC).
53061 * @returns {number} Returns the timestamp.
53064 * _.defer(function(stamp) {
53065 * console.log(_.now() - stamp);
53067 * // => Logs the number of milliseconds it took for the deferred invocation.
53070 var now = function now() {
53071 return root.Date.now();
53074 /** Used to match a single whitespace character. */
53075 var reWhitespace = /\s/;
53077 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
53078 * character of `string`.
53081 * @param {string} string The string to inspect.
53082 * @returns {number} Returns the index of the last non-whitespace character.
53085 function trimmedEndIndex(string) {
53086 var index = string.length;
53088 while (index-- && reWhitespace.test(string.charAt(index))) {}
53093 /** Used to match leading whitespace. */
53095 var reTrimStart = /^\s+/;
53097 * The base implementation of `_.trim`.
53100 * @param {string} string The string to trim.
53101 * @returns {string} Returns the trimmed string.
53104 function baseTrim(string) {
53105 return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;
53108 /** Built-in value references. */
53110 var _Symbol = root.Symbol;
53112 /** Used for built-in method references. */
53114 var objectProto$1 = Object.prototype;
53115 /** Used to check objects for own properties. */
53117 var hasOwnProperty$2 = objectProto$1.hasOwnProperty;
53119 * Used to resolve the
53120 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53124 var nativeObjectToString$1 = objectProto$1.toString;
53125 /** Built-in value references. */
53127 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
53129 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
53132 * @param {*} value The value to query.
53133 * @returns {string} Returns the raw `toStringTag`.
53136 function getRawTag(value) {
53137 var isOwn = hasOwnProperty$2.call(value, symToStringTag$1),
53138 tag = value[symToStringTag$1];
53141 value[symToStringTag$1] = undefined;
53142 var unmasked = true;
53145 var result = nativeObjectToString$1.call(value);
53149 value[symToStringTag$1] = tag;
53151 delete value[symToStringTag$1];
53158 /** Used for built-in method references. */
53159 var objectProto = Object.prototype;
53161 * Used to resolve the
53162 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
53166 var nativeObjectToString = objectProto.toString;
53168 * Converts `value` to a string using `Object.prototype.toString`.
53171 * @param {*} value The value to convert.
53172 * @returns {string} Returns the converted string.
53175 function objectToString(value) {
53176 return nativeObjectToString.call(value);
53179 /** `Object#toString` result references. */
53181 var nullTag = '[object Null]',
53182 undefinedTag = '[object Undefined]';
53183 /** Built-in value references. */
53185 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
53187 * The base implementation of `getTag` without fallbacks for buggy environments.
53190 * @param {*} value The value to query.
53191 * @returns {string} Returns the `toStringTag`.
53194 function baseGetTag(value) {
53195 if (value == null) {
53196 return value === undefined ? undefinedTag : nullTag;
53199 return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
53203 * Checks if `value` is object-like. A value is object-like if it's not `null`
53204 * and has a `typeof` result of "object".
53210 * @param {*} value The value to check.
53211 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
53214 * _.isObjectLike({});
53217 * _.isObjectLike([1, 2, 3]);
53220 * _.isObjectLike(_.noop);
53223 * _.isObjectLike(null);
53226 function isObjectLike(value) {
53227 return value != null && _typeof(value) == 'object';
53230 /** `Object#toString` result references. */
53232 var symbolTag = '[object Symbol]';
53234 * Checks if `value` is classified as a `Symbol` primitive or object.
53240 * @param {*} value The value to check.
53241 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
53244 * _.isSymbol(Symbol.iterator);
53247 * _.isSymbol('abc');
53251 function isSymbol(value) {
53252 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
53255 /** Used as references for various `Number` constants. */
53258 /** Used to detect bad signed hexadecimal string values. */
53260 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
53261 /** Used to detect binary string values. */
53263 var reIsBinary = /^0b[01]+$/i;
53264 /** Used to detect octal string values. */
53266 var reIsOctal = /^0o[0-7]+$/i;
53267 /** Built-in method references without a dependency on `root`. */
53269 var freeParseInt = parseInt;
53271 * Converts `value` to a number.
53277 * @param {*} value The value to process.
53278 * @returns {number} Returns the number.
53284 * _.toNumber(Number.MIN_VALUE);
53287 * _.toNumber(Infinity);
53290 * _.toNumber('3.2');
53294 function toNumber(value) {
53295 if (typeof value == 'number') {
53299 if (isSymbol(value)) {
53303 if (isObject$2(value)) {
53304 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
53305 value = isObject$2(other) ? other + '' : other;
53308 if (typeof value != 'string') {
53309 return value === 0 ? value : +value;
53312 value = baseTrim(value);
53313 var isBinary = reIsBinary.test(value);
53314 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
53317 /** Error message constants. */
53319 var FUNC_ERROR_TEXT$1 = 'Expected a function';
53320 /* Built-in method references for those with the same name as other `lodash` methods. */
53322 var nativeMax = Math.max,
53323 nativeMin = Math.min;
53325 * Creates a debounced function that delays invoking `func` until after `wait`
53326 * milliseconds have elapsed since the last time the debounced function was
53327 * invoked. The debounced function comes with a `cancel` method to cancel
53328 * delayed `func` invocations and a `flush` method to immediately invoke them.
53329 * Provide `options` to indicate whether `func` should be invoked on the
53330 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
53331 * with the last arguments provided to the debounced function. Subsequent
53332 * calls to the debounced function return the result of the last `func`
53335 * **Note:** If `leading` and `trailing` options are `true`, `func` is
53336 * invoked on the trailing edge of the timeout only if the debounced function
53337 * is invoked more than once during the `wait` timeout.
53339 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
53340 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
53342 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
53343 * for details over the differences between `_.debounce` and `_.throttle`.
53348 * @category Function
53349 * @param {Function} func The function to debounce.
53350 * @param {number} [wait=0] The number of milliseconds to delay.
53351 * @param {Object} [options={}] The options object.
53352 * @param {boolean} [options.leading=false]
53353 * Specify invoking on the leading edge of the timeout.
53354 * @param {number} [options.maxWait]
53355 * The maximum time `func` is allowed to be delayed before it's invoked.
53356 * @param {boolean} [options.trailing=true]
53357 * Specify invoking on the trailing edge of the timeout.
53358 * @returns {Function} Returns the new debounced function.
53361 * // Avoid costly calculations while the window size is in flux.
53362 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
53364 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
53365 * jQuery(element).on('click', _.debounce(sendMail, 300, {
53367 * 'trailing': false
53370 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
53371 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
53372 * var source = new EventSource('/stream');
53373 * jQuery(source).on('message', debounced);
53375 * // Cancel the trailing debounced invocation.
53376 * jQuery(window).on('popstate', debounced.cancel);
53379 function debounce(func, wait, options) {
53386 lastInvokeTime = 0,
53391 if (typeof func != 'function') {
53392 throw new TypeError(FUNC_ERROR_TEXT$1);
53395 wait = toNumber(wait) || 0;
53397 if (isObject$2(options)) {
53398 leading = !!options.leading;
53399 maxing = 'maxWait' in options;
53400 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
53401 trailing = 'trailing' in options ? !!options.trailing : trailing;
53404 function invokeFunc(time) {
53405 var args = lastArgs,
53406 thisArg = lastThis;
53407 lastArgs = lastThis = undefined;
53408 lastInvokeTime = time;
53409 result = func.apply(thisArg, args);
53413 function leadingEdge(time) {
53414 // Reset any `maxWait` timer.
53415 lastInvokeTime = time; // Start the timer for the trailing edge.
53417 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
53419 return leading ? invokeFunc(time) : result;
53422 function remainingWait(time) {
53423 var timeSinceLastCall = time - lastCallTime,
53424 timeSinceLastInvoke = time - lastInvokeTime,
53425 timeWaiting = wait - timeSinceLastCall;
53426 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
53429 function shouldInvoke(time) {
53430 var timeSinceLastCall = time - lastCallTime,
53431 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
53432 // trailing edge, the system time has gone backwards and we're treating
53433 // it as the trailing edge, or we've hit the `maxWait` limit.
53435 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
53438 function timerExpired() {
53441 if (shouldInvoke(time)) {
53442 return trailingEdge(time);
53443 } // Restart the timer.
53446 timerId = setTimeout(timerExpired, remainingWait(time));
53449 function trailingEdge(time) {
53450 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
53451 // debounced at least once.
53453 if (trailing && lastArgs) {
53454 return invokeFunc(time);
53457 lastArgs = lastThis = undefined;
53461 function cancel() {
53462 if (timerId !== undefined) {
53463 clearTimeout(timerId);
53466 lastInvokeTime = 0;
53467 lastArgs = lastCallTime = lastThis = timerId = undefined;
53471 return timerId === undefined ? result : trailingEdge(now());
53474 function debounced() {
53476 isInvoking = shouldInvoke(time);
53477 lastArgs = arguments;
53479 lastCallTime = time;
53482 if (timerId === undefined) {
53483 return leadingEdge(lastCallTime);
53487 // Handle invocations in a tight loop.
53488 clearTimeout(timerId);
53489 timerId = setTimeout(timerExpired, wait);
53490 return invokeFunc(lastCallTime);
53494 if (timerId === undefined) {
53495 timerId = setTimeout(timerExpired, wait);
53501 debounced.cancel = cancel;
53502 debounced.flush = flush;
53507 iD.coreDifference represents the difference between two graphs.
53508 It knows how to calculate the set of entities that were
53509 created, modified, or deleted, and also contains the logic
53510 for recursively extending a difference to the complete set
53511 of entities that will require a redraw, taking into account
53512 child and parent relationships.
53515 function coreDifference(base, head) {
53517 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
53521 function checkEntityID(id) {
53522 var h = head.entities[id];
53523 var b = base.entities[id];
53524 if (h === b) return;
53525 if (_changes[id]) return;
53532 _didChange.deletion = true;
53541 _didChange.addition = true;
53546 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
53551 _didChange.geometry = true;
53552 _didChange.properties = true;
53556 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
53561 _didChange.geometry = true;
53564 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
53569 _didChange.geometry = true;
53572 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
53577 _didChange.properties = true;
53583 // HOT CODE: there can be many thousands of downloaded entities, so looping
53584 // through them all can become a performance bottleneck. Optimize by
53585 // resolving duplicates and using a basic `for` loop
53586 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
53588 for (var i = 0; i < ids.length; i++) {
53589 checkEntityID(ids[i]);
53595 _diff.length = function length() {
53596 return Object.keys(_changes).length;
53599 _diff.changes = function changes() {
53603 _diff.didChange = _didChange; // pass true to include affected relation members
53605 _diff.extantIDs = function extantIDs(includeRelMembers) {
53606 var result = new Set();
53607 Object.keys(_changes).forEach(function (id) {
53608 if (_changes[id].head) {
53612 var h = _changes[id].head;
53613 var b = _changes[id].base;
53614 var entity = h || b;
53616 if (includeRelMembers && entity.type === 'relation') {
53617 var mh = h ? h.members.map(function (m) {
53620 var mb = b ? b.members.map(function (m) {
53623 utilArrayUnion(mh, mb).forEach(function (memberID) {
53624 if (head.hasEntity(memberID)) {
53625 result.add(memberID);
53630 return Array.from(result);
53633 _diff.modified = function modified() {
53635 Object.values(_changes).forEach(function (change) {
53636 if (change.base && change.head) {
53637 result.push(change.head);
53643 _diff.created = function created() {
53645 Object.values(_changes).forEach(function (change) {
53646 if (!change.base && change.head) {
53647 result.push(change.head);
53653 _diff.deleted = function deleted() {
53655 Object.values(_changes).forEach(function (change) {
53656 if (change.base && !change.head) {
53657 result.push(change.base);
53663 _diff.summary = function summary() {
53665 var keys = Object.keys(_changes);
53667 for (var i = 0; i < keys.length; i++) {
53668 var change = _changes[keys[i]];
53670 if (change.head && change.head.geometry(head) !== 'vertex') {
53671 addEntity(change.head, head, change.base ? 'modified' : 'created');
53672 } else if (change.base && change.base.geometry(base) !== 'vertex') {
53673 addEntity(change.base, base, 'deleted');
53674 } else if (change.base && change.head) {
53676 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
53677 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
53680 addParents(change.head);
53683 if (retagged || moved && change.head.hasInterestingTags()) {
53684 addEntity(change.head, head, 'modified');
53686 } else if (change.head && change.head.hasInterestingTags()) {
53688 addEntity(change.head, head, 'created');
53689 } else if (change.base && change.base.hasInterestingTags()) {
53691 addEntity(change.base, base, 'deleted');
53695 return Object.values(relevant);
53697 function addEntity(entity, graph, changeType) {
53698 relevant[entity.id] = {
53701 changeType: changeType
53705 function addParents(entity) {
53706 var parents = head.parentWays(entity);
53708 for (var j = parents.length - 1; j >= 0; j--) {
53709 var parent = parents[j];
53711 if (!(parent.id in relevant)) {
53712 addEntity(parent, head, 'modified');
53716 }; // returns complete set of entities that require a redraw
53717 // (optionally within given `extent`)
53720 _diff.complete = function complete(extent) {
53724 for (id in _changes) {
53725 change = _changes[id];
53726 var h = change.head;
53727 var b = change.base;
53728 var entity = h || b;
53731 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) {
53737 if (entity.type === 'way') {
53738 var nh = h ? h.nodes : [];
53739 var nb = b ? b.nodes : [];
53741 diff = utilArrayDifference(nh, nb);
53743 for (i = 0; i < diff.length; i++) {
53744 result[diff[i]] = head.hasEntity(diff[i]);
53747 diff = utilArrayDifference(nb, nh);
53749 for (i = 0; i < diff.length; i++) {
53750 result[diff[i]] = head.hasEntity(diff[i]);
53754 if (entity.type === 'relation' && entity.isMultipolygon()) {
53755 var mh = h ? h.members.map(function (m) {
53758 var mb = b ? b.members.map(function (m) {
53761 var ids = utilArrayUnion(mh, mb);
53763 for (i = 0; i < ids.length; i++) {
53764 var member = head.hasEntity(ids[i]);
53765 if (!member) continue; // not downloaded
53767 if (extent && !member.intersects(extent, head)) continue; // not visible
53769 result[ids[i]] = member;
53773 addParents(head.parentWays(entity), result);
53774 addParents(head.parentRelations(entity), result);
53779 function addParents(parents, result) {
53780 for (var i = 0; i < parents.length; i++) {
53781 var parent = parents[i];
53782 if (parent.id in result) continue;
53783 result[parent.id] = parent;
53784 addParents(head.parentRelations(parent), result);
53792 function coreTree(head) {
53793 // tree for entities
53794 var _rtree = new RBush();
53796 var _bboxes = {}; // maintain a separate tree for granular way segments
53798 var _segmentsRTree = new RBush();
53800 var _segmentsBBoxes = {};
53801 var _segmentsByWayId = {};
53804 function entityBBox(entity) {
53805 var bbox = entity.extent(head).bbox();
53806 bbox.id = entity.id;
53807 _bboxes[entity.id] = bbox;
53811 function segmentBBox(segment) {
53812 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
53814 if (!extent) return null;
53815 var bbox = extent.bbox();
53816 bbox.segment = segment;
53817 _segmentsBBoxes[segment.id] = bbox;
53821 function removeEntity(entity) {
53822 _rtree.remove(_bboxes[entity.id]);
53824 delete _bboxes[entity.id];
53826 if (_segmentsByWayId[entity.id]) {
53827 _segmentsByWayId[entity.id].forEach(function (segment) {
53828 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
53830 delete _segmentsBBoxes[segment.id];
53833 delete _segmentsByWayId[entity.id];
53837 function loadEntities(entities) {
53838 _rtree.load(entities.map(entityBBox));
53841 entities.forEach(function (entity) {
53842 if (entity.segments) {
53843 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
53845 _segmentsByWayId[entity.id] = entitySegments;
53846 segments = segments.concat(entitySegments);
53849 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
53852 function updateParents(entity, insertions, memo) {
53853 head.parentWays(entity).forEach(function (way) {
53854 if (_bboxes[way.id]) {
53856 insertions[way.id] = way;
53859 updateParents(way, insertions, memo);
53861 head.parentRelations(entity).forEach(function (relation) {
53862 if (memo[entity.id]) return;
53863 memo[entity.id] = true;
53865 if (_bboxes[relation.id]) {
53866 removeEntity(relation);
53867 insertions[relation.id] = relation;
53870 updateParents(relation, insertions, memo);
53874 tree.rebase = function (entities, force) {
53875 var insertions = {};
53877 for (var i = 0; i < entities.length; i++) {
53878 var entity = entities[i];
53879 if (!entity.visible) continue;
53881 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
53884 } else if (_bboxes[entity.id]) {
53885 removeEntity(entity);
53889 insertions[entity.id] = entity;
53890 updateParents(entity, insertions, {});
53893 loadEntities(Object.values(insertions));
53897 function updateToGraph(graph) {
53898 if (graph === head) return;
53899 var diff = coreDifference(head, graph);
53901 var changed = diff.didChange;
53902 if (!changed.addition && !changed.deletion && !changed.geometry) return;
53903 var insertions = {};
53905 if (changed.deletion) {
53906 diff.deleted().forEach(function (entity) {
53907 removeEntity(entity);
53911 if (changed.geometry) {
53912 diff.modified().forEach(function (entity) {
53913 removeEntity(entity);
53914 insertions[entity.id] = entity;
53915 updateParents(entity, insertions, {});
53919 if (changed.addition) {
53920 diff.created().forEach(function (entity) {
53921 insertions[entity.id] = entity;
53925 loadEntities(Object.values(insertions));
53926 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
53929 tree.intersects = function (extent, graph) {
53930 updateToGraph(graph);
53931 return _rtree.search(extent.bbox()).map(function (bbox) {
53932 return graph.entity(bbox.id);
53934 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
53937 tree.waySegments = function (extent, graph) {
53938 updateToGraph(graph);
53939 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
53940 return bbox.segment;
53947 function svgIcon(name, svgklass, useklass) {
53948 return function drawIcon(selection) {
53949 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);
53953 function uiModal(selection, blocking) {
53956 var keybinding = utilKeybinding('modal');
53957 var previous = selection.select('div.modal');
53958 var animate = previous.empty();
53959 previous.transition().duration(200).style('opacity', 0).remove();
53960 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
53962 shaded.close = function () {
53963 shaded.transition().duration(200).style('opacity', 0).remove();
53964 modal.transition().duration(200).style('top', '0px');
53965 select(document).call(keybinding.unbind);
53968 var modal = shaded.append('div').attr('class', 'modal fillL');
53969 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
53972 shaded.on('click.remove-modal', function (d3_event) {
53973 if (d3_event.target === _this) {
53977 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
53978 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
53979 select(document).call(keybinding);
53982 modal.append('div').attr('class', 'content');
53983 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
53986 shaded.transition().style('opacity', 1);
53988 shaded.style('opacity', 1);
53993 function moveFocusToFirst() {
53994 var node = modal // there are additional rules about what's focusable, but this suits our purposes
53995 .select('a, button, input:not(.keytrap), select, textarea').node();
54000 select(this).node().blur();
54004 function moveFocusToLast() {
54005 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
54007 if (nodes.length) {
54008 nodes[nodes.length - 1].focus();
54010 select(this).node().blur();
54015 function uiLoading(context) {
54016 var _modalSelection = select(null);
54019 var _blocking = false;
54021 var loading = function loading(selection) {
54022 _modalSelection = uiModal(selection, _blocking);
54024 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
54026 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
54027 loadertext.append('h3').html(_message);
54029 _modalSelection.select('button.close').attr('class', 'hide');
54034 loading.message = function (val) {
54035 if (!arguments.length) return _message;
54040 loading.blocking = function (val) {
54041 if (!arguments.length) return _blocking;
54046 loading.close = function () {
54047 _modalSelection.remove();
54050 loading.isShown = function () {
54051 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
54057 function coreHistory(context) {
54058 var dispatch = dispatch$8('reset', 'change', 'merge', 'restore', 'undone', 'redone');
54060 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
54063 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
54065 var duration = 150;
54066 var _imageryUsed = [];
54067 var _photoOverlaysUsed = [];
54068 var _checkpoints = {};
54076 var _tree; // internal _act, accepts list of actions and eased time
54079 function _act(actions, t) {
54080 actions = Array.prototype.slice.call(actions);
54083 if (typeof actions[actions.length - 1] !== 'function') {
54084 annotation = actions.pop();
54087 var graph = _stack[_index].graph;
54089 for (var i = 0; i < actions.length; i++) {
54090 graph = actions[i](graph, t);
54095 annotation: annotation,
54096 imageryUsed: _imageryUsed,
54097 photoOverlaysUsed: _photoOverlaysUsed,
54098 transform: context.projection.transform(),
54099 selectedIDs: context.selectedIDs()
54101 } // internal _perform with eased time
54104 function _perform(args, t) {
54105 var previous = _stack[_index].graph;
54106 _stack = _stack.slice(0, _index + 1);
54108 var actionResult = _act(args, t);
54110 _stack.push(actionResult);
54113 return change(previous);
54114 } // internal _replace with eased time
54117 function _replace(args, t) {
54118 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
54120 var actionResult = _act(args, t);
54122 _stack[_index] = actionResult;
54123 return change(previous);
54124 } // internal _overwrite with eased time
54127 function _overwrite(args, t) {
54128 var previous = _stack[_index].graph;
54136 _stack = _stack.slice(0, _index + 1);
54138 var actionResult = _act(args, t);
54140 _stack.push(actionResult);
54143 return change(previous);
54144 } // determine difference and dispatch a change event
54147 function change(previous) {
54148 var difference = coreDifference(previous, history.graph());
54150 if (!_pausedGraph) {
54151 dispatch.call('change', this, difference);
54155 } // iD uses namespaced keys so multiple installations do not conflict
54158 function getKey(n) {
54159 return 'iD_' + window.location.origin + '_' + n;
54163 graph: function graph() {
54164 return _stack[_index].graph;
54166 tree: function tree() {
54169 base: function base() {
54170 return _stack[0].graph;
54172 merge: function merge(entities
54175 var stack = _stack.map(function (state) {
54176 return state.graph;
54179 _stack[0].graph.rebase(entities, stack, false);
54181 _tree.rebase(entities, false);
54183 dispatch.call('merge', this, entities);
54185 perform: function perform() {
54186 // complete any transition already in progress
54187 select(document).interrupt('history.perform');
54188 var transitionable = false;
54189 var action0 = arguments[0];
54191 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
54192 transitionable = !!action0.transitionable;
54195 if (transitionable) {
54196 var origArguments = arguments;
54197 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
54198 return function (t) {
54199 if (t < 1) _overwrite([action0], t);
54201 }).on('start', function () {
54202 _perform([action0], 0);
54203 }).on('end interrupt', function () {
54204 _overwrite(origArguments, 1);
54207 return _perform(arguments);
54210 replace: function replace() {
54211 select(document).interrupt('history.perform');
54212 return _replace(arguments, 1);
54214 // Same as calling pop and then perform
54215 overwrite: function overwrite() {
54216 select(document).interrupt('history.perform');
54217 return _overwrite(arguments, 1);
54219 pop: function pop(n) {
54220 select(document).interrupt('history.perform');
54221 var previous = _stack[_index].graph;
54223 if (isNaN(+n) || +n < 0) {
54227 while (n-- > 0 && _index > 0) {
54233 return change(previous);
54235 // Back to the previous annotated state or _index = 0.
54236 undo: function undo() {
54237 select(document).interrupt('history.perform');
54238 var previousStack = _stack[_index];
54239 var previous = previousStack.graph;
54241 while (_index > 0) {
54243 if (_stack[_index].annotation) break;
54246 dispatch.call('undone', this, _stack[_index], previousStack);
54247 return change(previous);
54249 // Forward to the next annotated state.
54250 redo: function redo() {
54251 select(document).interrupt('history.perform');
54252 var previousStack = _stack[_index];
54253 var previous = previousStack.graph;
54254 var tryIndex = _index;
54256 while (tryIndex < _stack.length - 1) {
54259 if (_stack[tryIndex].annotation) {
54261 dispatch.call('redone', this, _stack[_index], previousStack);
54266 return change(previous);
54268 pauseChangeDispatch: function pauseChangeDispatch() {
54269 if (!_pausedGraph) {
54270 _pausedGraph = _stack[_index].graph;
54273 resumeChangeDispatch: function resumeChangeDispatch() {
54274 if (_pausedGraph) {
54275 var previous = _pausedGraph;
54276 _pausedGraph = null;
54277 return change(previous);
54280 undoAnnotation: function undoAnnotation() {
54284 if (_stack[i].annotation) return _stack[i].annotation;
54288 redoAnnotation: function redoAnnotation() {
54289 var i = _index + 1;
54291 while (i <= _stack.length - 1) {
54292 if (_stack[i].annotation) return _stack[i].annotation;
54296 // Returns the entities from the active graph with bounding boxes
54297 // overlapping the given `extent`.
54298 intersects: function intersects(extent) {
54299 return _tree.intersects(extent, _stack[_index].graph);
54301 difference: function difference() {
54302 var base = _stack[0].graph;
54303 var head = _stack[_index].graph;
54304 return coreDifference(base, head);
54306 changes: function changes(action) {
54307 var base = _stack[0].graph;
54308 var head = _stack[_index].graph;
54311 head = action(head);
54314 var difference = coreDifference(base, head);
54316 modified: difference.modified(),
54317 created: difference.created(),
54318 deleted: difference.deleted()
54321 hasChanges: function hasChanges() {
54322 return this.difference().length() > 0;
54324 imageryUsed: function imageryUsed(sources) {
54326 _imageryUsed = sources;
54331 _stack.slice(1, _index + 1).forEach(function (state) {
54332 state.imageryUsed.forEach(function (source) {
54333 if (source !== 'Custom') {
54339 return Array.from(s);
54342 photoOverlaysUsed: function photoOverlaysUsed(sources) {
54344 _photoOverlaysUsed = sources;
54349 _stack.slice(1, _index + 1).forEach(function (state) {
54350 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
54351 state.photoOverlaysUsed.forEach(function (photoOverlay) {
54352 s.add(photoOverlay);
54357 return Array.from(s);
54360 // save the current history state
54361 checkpoint: function checkpoint(key) {
54362 _checkpoints[key] = {
54368 // restore history state to a given checkpoint or reset completely
54369 reset: function reset(key) {
54370 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
54371 _stack = _checkpoints[key].stack;
54372 _index = _checkpoints[key].index;
54378 _tree = coreTree(_stack[0].graph);
54382 dispatch.call('reset');
54383 dispatch.call('change');
54386 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
54389 // 1. Start the walkthrough.
54390 // 2. Get to a "free editing" tutorial step
54391 // 3. Make your edits to the walkthrough map
54392 // 4. In your browser dev console run:
54393 // `id.history().toIntroGraph()`
54394 // 5. This outputs stringified JSON to the browser console
54395 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
54396 toIntroGraph: function toIntroGraph() {
54403 var graph = this.graph();
54404 var baseEntities = {}; // clone base entities..
54406 Object.values(graph.base().entities).forEach(function (entity) {
54407 var copy = copyIntroEntity(entity);
54408 baseEntities[copy.id] = copy;
54409 }); // replace base entities with head entities..
54411 Object.keys(graph.entities).forEach(function (id) {
54412 var entity = graph.entities[id];
54415 var copy = copyIntroEntity(entity);
54416 baseEntities[copy.id] = copy;
54418 delete baseEntities[id];
54420 }); // swap temporary for permanent ids..
54422 Object.values(baseEntities).forEach(function (entity) {
54423 if (Array.isArray(entity.nodes)) {
54424 entity.nodes = entity.nodes.map(function (node) {
54425 return permIDs[node] || node;
54429 if (Array.isArray(entity.members)) {
54430 entity.members = entity.members.map(function (member) {
54431 member.id = permIDs[member.id] || member.id;
54436 return JSON.stringify({
54437 dataIntroGraph: baseEntities
54440 function copyIntroEntity(source) {
54441 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
54443 if (copy.tags && !Object.keys(copy.tags)) {
54447 if (Array.isArray(copy.loc)) {
54448 copy.loc[0] = +copy.loc[0].toFixed(6);
54449 copy.loc[1] = +copy.loc[1].toFixed(6);
54452 var match = source.id.match(/([nrw])-\d*/); // temporary id
54454 if (match !== null) {
54455 var nrw = match[1];
54459 permID = nrw + ++nextID[nrw];
54460 } while (baseEntities.hasOwnProperty(permID));
54462 copy.id = permIDs[source.id] = permID;
54468 toJSON: function toJSON() {
54469 if (!this.hasChanges()) return;
54470 var allEntities = {};
54471 var baseEntities = {};
54472 var base = _stack[0];
54474 var s = _stack.map(function (i) {
54477 Object.keys(i.graph.entities).forEach(function (id) {
54478 var entity = i.graph.entities[id];
54481 var key = osmEntity.key(entity);
54482 allEntities[key] = entity;
54483 modified.push(key);
54486 } // make sure that the originals of changed or deleted entities get merged
54487 // into the base of the _stack after restoring the data from JSON.
54490 if (id in base.graph.entities) {
54491 baseEntities[id] = base.graph.entities[id];
54494 if (entity && entity.nodes) {
54495 // get originals of pre-existing child nodes
54496 entity.nodes.forEach(function (nodeID) {
54497 if (nodeID in base.graph.entities) {
54498 baseEntities[nodeID] = base.graph.entities[nodeID];
54501 } // get originals of parent entities too
54504 var baseParents = base.graph._parentWays[id];
54507 baseParents.forEach(function (parentID) {
54508 if (parentID in base.graph.entities) {
54509 baseEntities[parentID] = base.graph.entities[parentID];
54515 if (modified.length) x.modified = modified;
54516 if (deleted.length) x.deleted = deleted;
54517 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
54518 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
54519 if (i.annotation) x.annotation = i.annotation;
54520 if (i.transform) x.transform = i.transform;
54521 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
54525 return JSON.stringify({
54527 entities: Object.values(allEntities),
54528 baseEntities: Object.values(baseEntities),
54530 nextIDs: osmEntity.id.next,
54532 // note the time the changes were saved
54533 timestamp: new Date().getTime()
54536 fromJSON: function fromJSON(json, loadChildNodes) {
54537 var h = JSON.parse(json);
54538 var loadComplete = true;
54539 osmEntity.id.next = h.nextIDs;
54542 if (h.version === 2 || h.version === 3) {
54543 var allEntities = {};
54544 h.entities.forEach(function (entity) {
54545 allEntities[osmEntity.key(entity)] = osmEntity(entity);
54548 if (h.version === 3) {
54549 // This merges originals for changed entities into the base of
54550 // the _stack even if the current _stack doesn't have them (for
54551 // example when iD has been restarted in a different region)
54552 var baseEntities = h.baseEntities.map(function (d) {
54553 return osmEntity(d);
54556 var stack = _stack.map(function (state) {
54557 return state.graph;
54560 _stack[0].graph.rebase(baseEntities, stack, true);
54562 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
54563 // childnodes that would normally have been downloaded with it.. #2142
54566 if (loadChildNodes) {
54567 var osm = context.connection();
54568 var baseWays = baseEntities.filter(function (e) {
54569 return e.type === 'way';
54571 var nodeIDs = baseWays.reduce(function (acc, way) {
54572 return utilArrayUnion(acc, way.nodes);
54574 var missing = nodeIDs.filter(function (n) {
54575 return !_stack[0].graph.hasEntity(n);
54578 if (missing.length && osm) {
54579 loadComplete = false;
54580 context.map().redrawEnable(false);
54581 var loading = uiLoading(context).blocking(true);
54582 context.container().call(loading);
54584 var childNodesLoaded = function childNodesLoaded(err, result) {
54586 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
54587 var visibles = visibleGroups["true"] || []; // alive nodes
54589 var invisibles = visibleGroups["false"] || []; // deleted nodes
54591 if (visibles.length) {
54592 var visibleIDs = visibles.map(function (entity) {
54596 var stack = _stack.map(function (state) {
54597 return state.graph;
54600 missing = utilArrayDifference(missing, visibleIDs);
54602 _stack[0].graph.rebase(visibles, stack, true);
54604 _tree.rebase(visibles, true);
54605 } // fetch older versions of nodes that were deleted..
54608 invisibles.forEach(function (entity) {
54609 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
54613 if (err || !missing.length) {
54615 context.map().redrawEnable(true);
54616 dispatch.call('change');
54617 dispatch.call('restore', this);
54621 osm.loadMultiple(missing, childNodesLoaded);
54626 _stack = h.stack.map(function (d) {
54631 d.modified.forEach(function (key) {
54632 entity = allEntities[key];
54633 entities[entity.id] = entity;
54638 d.deleted.forEach(function (id) {
54639 entities[id] = undefined;
54644 graph: coreGraph(_stack[0].graph).load(entities),
54645 annotation: d.annotation,
54646 imageryUsed: d.imageryUsed,
54647 photoOverlaysUsed: d.photoOverlaysUsed,
54648 transform: d.transform,
54649 selectedIDs: d.selectedIDs
54653 // original version
54654 _stack = h.stack.map(function (d) {
54657 for (var i in d.entities) {
54658 var entity = d.entities[i];
54659 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
54662 d.graph = coreGraph(_stack[0].graph).load(entities);
54667 var transform = _stack[_index].transform;
54670 context.map().transformEase(transform, 0); // 0 = immediate, no easing
54673 if (loadComplete) {
54674 dispatch.call('change');
54675 dispatch.call('restore', this);
54680 lock: function lock() {
54681 return _lock.lock();
54683 unlock: function unlock() {
54686 save: function save() {
54687 if (_lock.locked() && // don't overwrite existing, unresolved changes
54688 !_hasUnresolvedRestorableChanges) {
54689 corePreferences(getKey('saved_history'), history.toJSON() || null);
54694 // delete the history version saved in localStorage
54695 clearSaved: function clearSaved() {
54696 context.debouncedSave.cancel();
54698 if (_lock.locked()) {
54699 _hasUnresolvedRestorableChanges = false;
54700 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
54702 corePreferences('comment', null);
54703 corePreferences('hashtags', null);
54704 corePreferences('source', null);
54709 savedHistoryJSON: function savedHistoryJSON() {
54710 return corePreferences(getKey('saved_history'));
54712 hasRestorableChanges: function hasRestorableChanges() {
54713 return _hasUnresolvedRestorableChanges;
54715 // load history from a version stored in localStorage
54716 restore: function restore() {
54717 if (_lock.locked()) {
54718 _hasUnresolvedRestorableChanges = false;
54719 var json = this.savedHistoryJSON();
54720 if (json) history.fromJSON(json, true);
54726 return utilRebind(history, dispatch, 'on');
54730 * Look for roads that can be connected to other roads with a short extension
54733 function validationAlmostJunction(context) {
54734 var type = 'almost_junction';
54735 var EXTEND_TH_METERS = 5;
54736 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
54738 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
54740 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
54742 function isHighway(entity) {
54743 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
54746 function isTaggedAsNotContinuing(node) {
54747 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
54750 var validation = function checkAlmostJunction(entity, graph) {
54751 if (!isHighway(entity)) return [];
54752 if (entity.isDegenerate()) return [];
54753 var tree = context.history().tree();
54754 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
54756 extendableNodeInfos.forEach(function (extendableNodeInfo) {
54757 issues.push(new validationIssue({
54759 subtype: 'highway-highway',
54760 severity: 'warning',
54761 message: function message(context) {
54762 var entity1 = context.hasEntity(this.entityIds[0]);
54764 if (this.entityIds[0] === this.entityIds[2]) {
54765 return entity1 ? _t.html('issues.almost_junction.self.message', {
54766 feature: utilDisplayLabel(entity1, context.graph())
54769 var entity2 = context.hasEntity(this.entityIds[2]);
54770 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
54771 feature: utilDisplayLabel(entity1, context.graph()),
54772 feature2: utilDisplayLabel(entity2, context.graph())
54776 reference: showReference,
54777 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
54778 loc: extendableNodeInfo.node.loc,
54779 hash: JSON.stringify(extendableNodeInfo.node.loc),
54781 midId: extendableNodeInfo.mid.id,
54782 edge: extendableNodeInfo.edge,
54783 cross_loc: extendableNodeInfo.cross_loc
54785 dynamicFixes: makeFixes
54790 function makeFixes(context) {
54791 var fixes = [new validationIssueFix({
54792 icon: 'iD-icon-abutment',
54793 title: _t.html('issues.fix.connect_features.title'),
54794 onClick: function onClick(context) {
54795 var annotation = _t('issues.fix.connect_almost_junction.annotation');
54797 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
54798 endNodeId = _this$issue$entityIds[1],
54799 crossWayId = _this$issue$entityIds[2];
54801 var midNode = context.entity(this.issue.data.midId);
54802 var endNode = context.entity(endNodeId);
54803 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
54805 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
54807 if (nearEndNodes.length > 0) {
54808 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
54811 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
54816 var targetEdge = this.issue.data.edge;
54817 var crossLoc = this.issue.data.cross_loc;
54818 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
54819 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
54821 if (closestNodeInfo.distance < WELD_TH_METERS) {
54822 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
54824 context.perform(actionAddMidpoint({
54827 }, endNode), annotation);
54831 var node = context.hasEntity(this.entityIds[1]);
54833 if (node && !node.hasInterestingTags()) {
54834 // node has no descriptive tags, suggest noexit fix
54835 fixes.push(new validationIssueFix({
54836 icon: 'maki-barrier',
54837 title: _t.html('issues.fix.tag_as_disconnected.title'),
54838 onClick: function onClick(context) {
54839 var nodeID = this.issue.entityIds[1];
54840 var tags = Object.assign({}, context.entity(nodeID).tags);
54841 tags.noexit = 'yes';
54842 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
54850 function showReference(selection) {
54851 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
54854 function isExtendableCandidate(node, way) {
54855 // can not accurately test vertices on tiles not downloaded from osm - #5938
54856 var osm = services.osm;
54858 if (osm && !osm.isDataLoaded(node.loc)) {
54862 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
54866 var occurrences = 0;
54868 for (var index in way.nodes) {
54869 if (way.nodes[index] === node.id) {
54872 if (occurrences > 1) {
54881 function findConnectableEndNodesByExtension(way) {
54883 if (way.isClosed()) return results;
54885 var indices = [0, way.nodes.length - 1];
54886 indices.forEach(function (nodeIndex) {
54887 var nodeID = way.nodes[nodeIndex];
54888 var node = graph.entity(nodeID);
54889 if (!isExtendableCandidate(node, way)) return;
54890 var connectionInfo = canConnectByExtend(way, nodeIndex);
54891 if (!connectionInfo) return;
54892 testNodes = graph.childNodes(way).slice(); // shallow copy
54894 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
54896 if (geoHasSelfIntersections(testNodes, nodeID)) return;
54897 results.push(connectionInfo);
54902 function findNearbyEndNodes(node, way) {
54903 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
54904 return graph.entity(d);
54905 }).filter(function (d) {
54906 // Node cannot be near to itself, but other endnode of same way could be
54907 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
54911 function findSmallJoinAngle(midNode, tipNode, endNodes) {
54912 // Both nodes could be close, so want to join whichever is closest to collinear
54914 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
54916 endNodes.forEach(function (endNode) {
54917 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
54918 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
54919 var diff = Math.max(a1, a2) - Math.min(a1, a2);
54921 if (diff < minAngle) {
54926 /* Threshold set by considering right angle triangle
54927 based on node joining threshold and extension distance */
54929 if (minAngle <= SIG_ANGLE_TH) return joinTo;
54933 function hasTag(tags, key) {
54934 return tags[key] !== undefined && tags[key] !== 'no';
54937 function canConnectWays(way, way2) {
54938 // allow self-connections
54939 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
54941 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
54942 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
54944 var layer1 = way.tags.layer || '0',
54945 layer2 = way2.tags.layer || '0';
54946 if (layer1 !== layer2) return false;
54947 var level1 = way.tags.level || '0',
54948 level2 = way2.tags.level || '0';
54949 if (level1 !== level2) return false;
54953 function canConnectByExtend(way, endNodeIdx) {
54954 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
54956 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
54958 var tipNode = graph.entity(tipNid);
54959 var midNode = graph.entity(midNid);
54960 var lon = tipNode.loc[0];
54961 var lat = tipNode.loc[1];
54962 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
54963 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
54964 var 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
54966 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
54967 var t = EXTEND_TH_METERS / edgeLen + 1.0;
54968 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
54970 var segmentInfos = tree.waySegments(queryExtent, graph);
54972 for (var i = 0; i < segmentInfos.length; i++) {
54973 var segmentInfo = segmentInfos[i];
54974 var way2 = graph.entity(segmentInfo.wayId);
54975 if (!isHighway(way2)) continue;
54976 if (!canConnectWays(way, way2)) continue;
54977 var nAid = segmentInfo.nodes[0],
54978 nBid = segmentInfo.nodes[1];
54979 if (nAid === tipNid || nBid === tipNid) continue;
54980 var nA = graph.entity(nAid),
54981 nB = graph.entity(nBid);
54982 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
54989 edge: [nA.id, nB.id],
54990 cross_loc: crossLoc
54999 validation.type = type;
55003 function validationCloseNodes(context) {
55004 var type = 'close_nodes';
55005 var pointThresholdMeters = 0.2;
55007 var validation = function validation(entity, graph) {
55008 if (entity.type === 'node') {
55009 return getIssuesForNode(entity);
55010 } else if (entity.type === 'way') {
55011 return getIssuesForWay(entity);
55016 function getIssuesForNode(node) {
55017 var parentWays = graph.parentWays(node);
55019 if (parentWays.length) {
55020 return getIssuesForVertex(node, parentWays);
55022 return getIssuesForDetachedPoint(node);
55026 function wayTypeFor(way) {
55027 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
55028 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
55029 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
55030 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
55031 var parentRelations = graph.parentRelations(way);
55033 for (var i in parentRelations) {
55034 var relation = parentRelations[i];
55035 if (relation.tags.type === 'boundary') return 'boundary';
55037 if (relation.isMultipolygon()) {
55038 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
55039 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
55046 function shouldCheckWay(way) {
55047 // don't flag issues where merging would create degenerate ways
55048 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
55049 var bbox = way.extent(graph).bbox();
55050 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
55052 if (hypotenuseMeters < 1.5) return false;
55056 function getIssuesForWay(way) {
55057 if (!shouldCheckWay(way)) return [];
55059 nodes = graph.childNodes(way);
55061 for (var i = 0; i < nodes.length - 1; i++) {
55062 var node1 = nodes[i];
55063 var node2 = nodes[i + 1];
55064 var issue = getWayIssueIfAny(node1, node2, way);
55065 if (issue) issues.push(issue);
55071 function getIssuesForVertex(node, parentWays) {
55074 function checkForCloseness(node1, node2, way) {
55075 var issue = getWayIssueIfAny(node1, node2, way);
55076 if (issue) issues.push(issue);
55079 for (var i = 0; i < parentWays.length; i++) {
55080 var parentWay = parentWays[i];
55081 if (!shouldCheckWay(parentWay)) continue;
55082 var lastIndex = parentWay.nodes.length - 1;
55084 for (var j = 0; j < parentWay.nodes.length; j++) {
55086 if (parentWay.nodes[j - 1] === node.id) {
55087 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
55091 if (j !== lastIndex) {
55092 if (parentWay.nodes[j + 1] === node.id) {
55093 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
55102 function thresholdMetersForWay(way) {
55103 if (!shouldCheckWay(way)) return 0;
55104 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
55106 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
55108 if (wayType === 'indoor') return 0.01;
55109 if (wayType === 'building') return 0.05;
55110 if (wayType === 'path') return 0.1;
55114 function getIssuesForDetachedPoint(node) {
55116 var lon = node.loc[0];
55117 var lat = node.loc[1];
55118 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
55119 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
55120 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
55121 var intersected = context.history().tree().intersects(queryExtent, graph);
55123 for (var j = 0; j < intersected.length; j++) {
55124 var nearby = intersected[j];
55125 if (nearby.id === node.id) continue;
55126 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
55128 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
55129 // allow very close points if tags indicate the z-axis might vary
55133 'addr:housenumber': true,
55136 var zAxisDifferentiates = false;
55138 for (var key in zAxisKeys) {
55139 var nodeValue = node.tags[key] || '0';
55140 var nearbyValue = nearby.tags[key] || '0';
55142 if (nodeValue !== nearbyValue) {
55143 zAxisDifferentiates = true;
55148 if (zAxisDifferentiates) continue;
55149 issues.push(new validationIssue({
55151 subtype: 'detached',
55152 severity: 'warning',
55153 message: function message(context) {
55154 var entity = context.hasEntity(this.entityIds[0]),
55155 entity2 = context.hasEntity(this.entityIds[1]);
55156 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
55157 feature: utilDisplayLabel(entity, context.graph()),
55158 feature2: utilDisplayLabel(entity2, context.graph())
55161 reference: showReference,
55162 entityIds: [node.id, nearby.id],
55163 dynamicFixes: function dynamicFixes() {
55164 return [new validationIssueFix({
55165 icon: 'iD-operation-disconnect',
55166 title: _t.html('issues.fix.move_points_apart.title')
55167 }), new validationIssueFix({
55168 icon: 'iD-icon-layers',
55169 title: _t.html('issues.fix.use_different_layers_or_levels.title')
55178 function showReference(selection) {
55179 var referenceText = _t('issues.close_nodes.detached.reference');
55180 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55184 function getWayIssueIfAny(node1, node2, way) {
55185 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
55189 if (node1.loc !== node2.loc) {
55190 var parentWays1 = graph.parentWays(node1);
55191 var parentWays2 = new Set(graph.parentWays(node2));
55192 var sharedWays = parentWays1.filter(function (parentWay) {
55193 return parentWays2.has(parentWay);
55195 var thresholds = sharedWays.map(function (parentWay) {
55196 return thresholdMetersForWay(parentWay);
55198 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
55199 var distance = geoSphericalDistance(node1.loc, node2.loc);
55200 if (distance > threshold) return null;
55203 return new validationIssue({
55205 subtype: 'vertices',
55206 severity: 'warning',
55207 message: function message(context) {
55208 var entity = context.hasEntity(this.entityIds[0]);
55209 return entity ? _t.html('issues.close_nodes.message', {
55210 way: utilDisplayLabel(entity, context.graph())
55213 reference: showReference,
55214 entityIds: [way.id, node1.id, node2.id],
55216 dynamicFixes: function dynamicFixes() {
55217 return [new validationIssueFix({
55218 icon: 'iD-icon-plus',
55219 title: _t.html('issues.fix.merge_points.title'),
55220 onClick: function onClick(context) {
55221 var entityIds = this.issue.entityIds;
55222 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
55223 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
55225 }), new validationIssueFix({
55226 icon: 'iD-operation-disconnect',
55227 title: _t.html('issues.fix.move_points_apart.title')
55232 function showReference(selection) {
55233 var referenceText = _t('issues.close_nodes.reference');
55234 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
55239 validation.type = type;
55243 function validationCrossingWays(context) {
55244 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
55246 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
55247 if (getFeatureType(way, graph) === null) {
55248 // if the way doesn't match a feature type, check its parent relations
55249 var parentRels = graph.parentRelations(way);
55251 for (var i = 0; i < parentRels.length; i++) {
55252 var rel = parentRels[i];
55254 if (getFeatureType(rel, graph) !== null) {
55263 function hasTag(tags, key) {
55264 return tags[key] !== undefined && tags[key] !== 'no';
55267 function taggedAsIndoor(tags) {
55268 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
55271 function allowsBridge(featureType) {
55272 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55275 function allowsTunnel(featureType) {
55276 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
55280 var ignoredBuildings = {
55287 function getFeatureType(entity, graph) {
55288 var geometry = entity.geometry(graph);
55289 if (geometry !== 'line' && geometry !== 'area') return null;
55290 var tags = entity.tags;
55291 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
55292 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
55294 if (geometry !== 'line') return null;
55295 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
55296 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
55300 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
55301 // assume 0 by default
55302 var level1 = tags1.level || '0';
55303 var level2 = tags2.level || '0';
55305 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
55306 // assume features don't interact if they're indoor on different levels
55308 } // assume 0 by default; don't use way.layer() since we account for structures here
55311 var layer1 = tags1.layer || '0';
55312 var layer2 = tags2.layer || '0';
55314 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
55315 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
55316 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
55318 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
55319 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
55321 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
55322 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
55323 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
55325 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
55326 } 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
55329 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
55330 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
55332 if (featureType1 === 'building' || featureType2 === 'building') {
55333 // for building crossings, different layers are enough
55334 if (layer1 !== layer2) return true;
55338 } // highway values for which we shouldn't recommend connecting to waterways
55341 var highwaysDisallowingFords = {
55343 motorway_link: true,
55347 primary_link: true,
55349 secondary_link: true
55351 var nonCrossingHighways = {
55355 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
55356 var featureType1 = getFeatureType(entity1, graph);
55357 var featureType2 = getFeatureType(entity2, graph);
55358 var geometry1 = entity1.geometry(graph);
55359 var geometry2 = entity2.geometry(graph);
55360 var bothLines = geometry1 === 'line' && geometry2 === 'line';
55362 if (featureType1 === featureType2) {
55363 if (featureType1 === 'highway') {
55364 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
55365 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
55367 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
55368 // one feature is a path but not both
55369 var roadFeature = entity1IsPath ? entity2 : entity1;
55371 if (nonCrossingHighways[roadFeature.tags.highway]) {
55372 // don't mark path connections with certain roads as crossings
55376 var pathFeature = entity1IsPath ? entity1 : entity2;
55378 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
55379 // if the path is a crossing, match the crossing type
55380 return bothLines ? {
55381 highway: 'crossing',
55382 crossing: pathFeature.tags.crossing
55384 } // don't add a `crossing` subtag to ambiguous crossings
55387 return bothLines ? {
55388 highway: 'crossing'
55395 if (featureType1 === 'waterway') return {};
55396 if (featureType1 === 'railway') return {};
55398 var featureTypes = [featureType1, featureType2];
55400 if (featureTypes.indexOf('highway') !== -1) {
55401 if (featureTypes.indexOf('railway') !== -1) {
55402 if (!bothLines) return {};
55403 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
55405 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
55406 // path-tram connections use this tag
55407 if (isTram) return {
55408 railway: 'tram_crossing'
55409 }; // other path-rail connections use this tag
55412 railway: 'crossing'
55415 // path-tram connections use this tag
55416 if (isTram) return {
55417 railway: 'tram_level_crossing'
55418 }; // other road-rail connections use this tag
55421 railway: 'level_crossing'
55426 if (featureTypes.indexOf('waterway') !== -1) {
55427 // do not allow fords on structures
55428 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
55429 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
55431 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
55432 // do not allow fords on major highways
55436 return bothLines ? {
55446 function findCrossingsByWay(way1, graph, tree) {
55447 var edgeCrossInfos = [];
55448 if (way1.type !== 'way') return edgeCrossInfos;
55449 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
55450 var way1FeatureType = getFeatureType(taggedFeature1, graph);
55451 if (way1FeatureType === null) return edgeCrossInfos;
55452 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
55456 var n1, n2, nA, nB, nAId, nBId;
55457 var segment1, segment2;
55459 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
55460 var way1Nodes = graph.childNodes(way1);
55461 var comparedWays = {};
55463 for (i = 0; i < way1Nodes.length - 1; i++) {
55465 n2 = way1Nodes[i + 1];
55466 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
55467 // of overlapping ways
55469 segmentInfos = tree.waySegments(extent, graph);
55471 for (j = 0; j < segmentInfos.length; j++) {
55472 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
55474 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
55476 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
55478 comparedWays[segment2Info.wayId] = true;
55479 way2 = graph.hasEntity(segment2Info.wayId);
55480 if (!way2) continue;
55481 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
55483 way2FeatureType = getFeatureType(taggedFeature2, graph);
55485 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
55487 } // create only one issue for building crossings
55490 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
55491 nAId = segment2Info.nodes[0];
55492 nBId = segment2Info.nodes[1];
55494 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
55495 // n1 or n2 is a connection node; skip
55499 nA = graph.hasEntity(nAId);
55501 nB = graph.hasEntity(nBId);
55503 segment1 = [n1.loc, n2.loc];
55504 segment2 = [nA.loc, nB.loc];
55505 var point = geoLineIntersection(segment1, segment2);
55508 edgeCrossInfos.push({
55511 featureType: way1FeatureType,
55512 edge: [n1.id, n2.id]
55515 featureType: way2FeatureType,
55516 edge: [nA.id, nB.id]
55522 checkedSingleCrossingWays[way2.id] = true;
55529 return edgeCrossInfos;
55532 function waysToCheck(entity, graph) {
55533 var featureType = getFeatureType(entity, graph);
55534 if (!featureType) return [];
55536 if (entity.type === 'way') {
55538 } else if (entity.type === 'relation') {
55539 return entity.members.reduce(function (array, member) {
55540 if (member.type === 'way' && ( // only look at geometry ways
55541 !member.role || member.role === 'outer' || member.role === 'inner')) {
55542 var entity = graph.hasEntity(member.id); // don't add duplicates
55544 if (entity && array.indexOf(entity) === -1) {
55545 array.push(entity);
55556 var validation = function checkCrossingWays(entity, graph) {
55557 var tree = context.history().tree();
55558 var ways = waysToCheck(entity, graph);
55559 var issues = []; // declare these here to reduce garbage collection
55561 var wayIndex, crossingIndex, crossings;
55563 for (wayIndex in ways) {
55564 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
55566 for (crossingIndex in crossings) {
55567 issues.push(createIssue(crossings[crossingIndex], graph));
55574 function createIssue(crossing, graph) {
55575 // use the entities with the tags that define the feature type
55576 crossing.wayInfos.sort(function (way1Info, way2Info) {
55577 var type1 = way1Info.featureType;
55578 var type2 = way2Info.featureType;
55580 if (type1 === type2) {
55581 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
55582 } else if (type1 === 'waterway') {
55584 } else if (type2 === 'waterway') {
55588 return type1 < type2;
55590 var entities = crossing.wayInfos.map(function (wayInfo) {
55591 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
55593 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
55594 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
55595 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
55596 var featureType1 = crossing.wayInfos[0].featureType;
55597 var featureType2 = crossing.wayInfos[1].featureType;
55598 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
55599 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
55600 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
55601 var subtype = [featureType1, featureType2].sort().join('-');
55602 var crossingTypeID = subtype;
55604 if (isCrossingIndoors) {
55605 crossingTypeID = 'indoor-indoor';
55606 } else if (isCrossingTunnels) {
55607 crossingTypeID = 'tunnel-tunnel';
55608 } else if (isCrossingBridges) {
55609 crossingTypeID = 'bridge-bridge';
55612 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
55613 crossingTypeID += '_connectable';
55614 } // Differentiate based on the loc rounded to 4 digits, since two ways can cross multiple times.
55617 var uniqueID = '' + crossing.crossPoint[0].toFixed(4) + ',' + crossing.crossPoint[1].toFixed(4);
55618 return new validationIssue({
55621 severity: 'warning',
55622 message: function message(context) {
55623 var graph = context.graph();
55624 var entity1 = graph.hasEntity(this.entityIds[0]),
55625 entity2 = graph.hasEntity(this.entityIds[1]);
55626 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
55627 feature: utilDisplayLabel(entity1, graph),
55628 feature2: utilDisplayLabel(entity2, graph)
55631 reference: showReference,
55632 entityIds: entities.map(function (entity) {
55637 featureTypes: featureTypes,
55638 connectionTags: connectionTags
55641 loc: crossing.crossPoint,
55642 dynamicFixes: function dynamicFixes(context) {
55643 var mode = context.mode();
55644 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
55645 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
55646 var selectedFeatureType = this.data.featureTypes[selectedIndex];
55647 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
55650 if (connectionTags) {
55651 fixes.push(makeConnectWaysFix(this.data.connectionTags));
55654 if (isCrossingIndoors) {
55655 fixes.push(new validationIssueFix({
55656 icon: 'iD-icon-layers',
55657 title: _t.html('issues.fix.use_different_levels.title')
55659 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
55660 fixes.push(makeChangeLayerFix('higher'));
55661 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
55662 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
55663 // don't recommend adding bridges to waterways since they're uncommon
55664 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
55665 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
55666 } // don't recommend adding tunnels under waterways since they're uncommon
55669 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
55671 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
55672 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
55674 } // repositioning the features is always an option
55677 fixes.push(new validationIssueFix({
55678 icon: 'iD-operation-move',
55679 title: _t.html('issues.fix.reposition_features.title')
55685 function showReference(selection) {
55686 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
55690 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
55691 return new validationIssueFix({
55693 title: _t.html('issues.fix.' + fixTitleID + '.title'),
55694 onClick: function onClick(context) {
55695 var mode = context.mode();
55696 if (!mode || mode.id !== 'select') return;
55697 var selectedIDs = mode.selectedIDs();
55698 if (selectedIDs.length !== 1) return;
55699 var selectedWayID = selectedIDs[0];
55700 if (!context.hasEntity(selectedWayID)) return;
55701 var resultWayIDs = [selectedWayID];
55702 var edge, crossedEdge, crossedWayID;
55704 if (this.issue.entityIds[0] === selectedWayID) {
55705 edge = this.issue.data.edges[0];
55706 crossedEdge = this.issue.data.edges[1];
55707 crossedWayID = this.issue.entityIds[1];
55709 edge = this.issue.data.edges[1];
55710 crossedEdge = this.issue.data.edges[0];
55711 crossedWayID = this.issue.entityIds[0];
55714 var crossingLoc = this.issue.loc;
55715 var projection = context.projection;
55717 var action = function actionAddStructure(graph) {
55718 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55719 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
55721 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
55723 if (!structLengthMeters) {
55724 // if no explicit width is set, approximate the width based on the tags
55725 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
55728 if (structLengthMeters) {
55729 if (getFeatureType(crossedWay, graph) === 'railway') {
55730 // bridges over railways are generally much longer than the rail bed itself, compensate
55731 structLengthMeters *= 2;
55734 // should ideally never land here since all rail/water/road tags should have an implied width
55735 structLengthMeters = 8;
55738 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
55739 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
55740 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
55741 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
55743 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
55745 structLengthMeters += 4; // clamp the length to a reasonable range
55747 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
55749 function geomToProj(geoPoint) {
55750 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
55753 function projToGeom(projPoint) {
55754 var lat = geoMetersToLat(projPoint[1]);
55755 return [geoMetersToLon(projPoint[0], lat), lat];
55758 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
55759 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
55760 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
55761 var projectedCrossingLoc = geomToProj(crossingLoc);
55762 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
55764 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
55765 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
55766 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
55769 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
55770 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
55773 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
55774 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
55775 }; // avoid creating very short edges from splitting too close to another node
55778 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
55780 function determineEndpoint(edge, endNode, locGetter) {
55782 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
55783 // the maximum length of this side of the structure
55785 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
55787 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
55788 // the edge is long enough to insert a new node
55789 // the loc that would result in the full expected length
55790 var idealNodeLoc = locGetter(idealLengthMeters);
55791 newNode = osmNode();
55792 graph = actionAddMidpoint({
55795 }, newNode)(graph);
55798 endNode.parentIntersectionWays(graph).forEach(function (way) {
55799 way.nodes.forEach(function (nodeID) {
55800 if (nodeID === endNode.id) {
55801 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
55810 if (edgeCount >= 3) {
55811 // the end node is a junction, try to leave a segment
55812 // between it and the structure - #7202
55813 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
55815 if (insetLength > minEdgeLengthMeters) {
55816 var insetNodeLoc = locGetter(insetLength);
55817 newNode = osmNode();
55818 graph = actionAddMidpoint({
55821 }, newNode)(graph);
55824 } // if the edge is too short to subdivide as desired, then
55825 // just bound the structure at the existing end node
55828 if (!newNode) newNode = endNode;
55829 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
55832 graph = splitAction(graph);
55834 if (splitAction.getCreatedWayIDs().length) {
55835 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
55841 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
55842 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
55843 var structureWay = resultWayIDs.map(function (id) {
55844 return graph.entity(id);
55845 }).find(function (way) {
55846 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
55848 var tags = Object.assign({}, structureWay.tags); // copy tags
55850 if (bridgeOrTunnel === 'bridge') {
55851 tags.bridge = 'yes';
55854 var tunnelValue = 'yes';
55856 if (getFeatureType(structureWay, graph) === 'waterway') {
55857 // use `tunnel=culvert` for waterways by default
55858 tunnelValue = 'culvert';
55861 tags.tunnel = tunnelValue;
55863 } // apply the structure tags to the way
55866 graph = actionChangeTags(structureWay.id, tags)(graph);
55870 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
55871 context.enter(modeSelect(context, resultWayIDs));
55876 function makeConnectWaysFix(connectionTags) {
55877 var fixTitleID = 'connect_features';
55879 if (connectionTags.ford) {
55880 fixTitleID = 'connect_using_ford';
55883 return new validationIssueFix({
55884 icon: 'iD-icon-crossing',
55885 title: _t.html('issues.fix.' + fixTitleID + '.title'),
55886 onClick: function onClick(context) {
55887 var loc = this.issue.loc;
55888 var connectionTags = this.issue.data.connectionTags;
55889 var edges = this.issue.data.edges;
55890 context.perform(function actionConnectCrossingWays(graph) {
55891 // create the new node for the points
55892 var node = osmNode({
55894 tags: connectionTags
55896 graph = graph.replace(node);
55897 var nodesToMerge = [node.id];
55898 var mergeThresholdInMeters = 0.75;
55899 edges.forEach(function (edge) {
55900 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
55901 var nearby = geoSphericalClosestNode(edgeNodes, loc); // if there is already a suitable node nearby, use that
55902 // use the node if node has no interesting tags or if it is a crossing node #8326
55904 if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) {
55905 nodesToMerge.push(nearby.node.id); // else add the new node to the way
55907 graph = actionAddMidpoint({
55914 if (nodesToMerge.length > 1) {
55915 // if we're using nearby nodes, merge them with the new node
55916 graph = actionMergeNodes(nodesToMerge, loc)(graph);
55920 }, _t('issues.fix.connect_crossing_features.annotation'));
55925 function makeChangeLayerFix(higherOrLower) {
55926 return new validationIssueFix({
55927 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
55928 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
55929 onClick: function onClick(context) {
55930 var mode = context.mode();
55931 if (!mode || mode.id !== 'select') return;
55932 var selectedIDs = mode.selectedIDs();
55933 if (selectedIDs.length !== 1) return;
55934 var selectedID = selectedIDs[0];
55935 if (!this.issue.entityIds.some(function (entityId) {
55936 return entityId === selectedID;
55938 var entity = context.hasEntity(selectedID);
55939 if (!entity) return;
55940 var tags = Object.assign({}, entity.tags); // shallow copy
55942 var layer = tags.layer && Number(tags.layer);
55944 if (layer && !isNaN(layer)) {
55945 if (higherOrLower === 'higher') {
55951 if (higherOrLower === 'higher') {
55958 tags.layer = layer.toString();
55959 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
55964 validation.type = type;
55968 function behaviorDrawWay(context, wayID, mode, startGraph) {
55969 var dispatch = dispatch$8('rejectedSelfIntersection');
55970 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
55982 var _pointerHasMoved = false; // The osmNode to be placed.
55983 // This is temporary and just follows the mouse cursor until an "add" event occurs.
55987 var _didResolveTempEdit = false;
55989 function createDrawNode(loc) {
55990 // don't make the draw node until we actually need it
55991 _drawNode = osmNode({
55994 context.pauseChangeDispatch();
55995 context.replace(function actionAddDrawNode(graph) {
55996 // add the draw node to the graph and insert it into the way
55997 var way = graph.entity(wayID);
55998 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
56000 context.resumeChangeDispatch();
56001 setActiveElements();
56004 function removeDrawNode() {
56005 context.pauseChangeDispatch();
56006 context.replace(function actionDeleteDrawNode(graph) {
56007 var way = graph.entity(wayID);
56008 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56010 _drawNode = undefined;
56011 context.resumeChangeDispatch();
56014 function keydown(d3_event) {
56015 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56016 if (context.surface().classed('nope')) {
56017 context.surface().classed('nope-suppressed', true);
56020 context.surface().classed('nope', false).classed('nope-disabled', true);
56024 function keyup(d3_event) {
56025 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56026 if (context.surface().classed('nope-suppressed')) {
56027 context.surface().classed('nope', true);
56030 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56034 function allowsVertex(d) {
56035 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56037 // - `mode/drag_node.js` `doMove()`
56038 // - `behavior/draw.js` `click()`
56039 // - `behavior/draw_way.js` `move()`
56042 function move(d3_event, datum) {
56043 var loc = context.map().mouseCoordinates();
56044 if (!_drawNode) createDrawNode(loc);
56045 context.surface().classed('nope-disabled', d3_event.altKey);
56046 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56047 var targetNodes = datum && datum.properties && datum.properties.nodes;
56050 // snap to node/vertex - a point target with `.loc`
56052 } else if (targetNodes) {
56053 // snap to way - a line target with `.nodes`
56054 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56061 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56062 _drawNode = context.entity(_drawNode.id);
56064 /* includeDrawNode */
56066 } // Check whether this edit causes the geometry to break.
56067 // If so, class the surface with a nope cursor.
56068 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56071 function checkGeometry(includeDrawNode) {
56072 var nopeDisabled = context.surface().classed('nope-disabled');
56073 var isInvalid = isInvalidGeometry(includeDrawNode);
56075 if (nopeDisabled) {
56076 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56078 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56082 function isInvalidGeometry(includeDrawNode) {
56083 var testNode = _drawNode; // we only need to test the single way we're drawing
56085 var parentWay = context.graph().entity(wayID);
56086 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56088 if (includeDrawNode) {
56089 if (parentWay.isClosed()) {
56090 // don't test the last segment for closed ways - #4655
56091 // (still test the first segment)
56095 // discount the draw node
56096 if (parentWay.isClosed()) {
56097 if (nodes.length < 3) return false;
56098 if (_drawNode) nodes.splice(-2, 1);
56099 testNode = nodes[nodes.length - 2];
56101 // there's nothing we need to test if we ignore the draw node on open ways
56106 return testNode && geoHasSelfIntersections(nodes, testNode.id);
56109 function undone() {
56110 // undoing removed the temp edit
56111 _didResolveTempEdit = true;
56112 context.pauseChangeDispatch();
56115 if (context.graph() === startGraph) {
56116 // We've undone back to the initial state before we started drawing.
56117 // Just exit the draw mode without undoing whatever we did before
56118 // we entered the draw mode.
56119 nextMode = modeSelect(context, [wayID]);
56121 // The `undo` only removed the temporary edit, so here we have to
56122 // manually undo to actually remove the last node we added. We can't
56123 // use the `undo` function since the initial "add" graph doesn't have
56124 // an annotation and so cannot be undone to.
56125 context.pop(1); // continue drawing
56128 } // clear the redo stack by adding and removing a blank edit
56131 context.perform(actionNoop());
56133 context.resumeChangeDispatch();
56134 context.enter(nextMode);
56137 function setActiveElements() {
56138 if (!_drawNode) return;
56139 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56142 function resetToStartGraph() {
56143 while (context.graph() !== startGraph) {
56148 var drawWay = function drawWay(surface) {
56149 _drawNode = undefined;
56150 _didResolveTempEdit = false;
56151 _origWay = context.entity(wayID);
56153 if (typeof _nodeIndex === 'number') {
56154 _headNodeID = _origWay.nodes[_nodeIndex];
56155 } else if (_origWay.isClosed()) {
56156 _headNodeID = _origWay.nodes[_origWay.nodes.length - 2];
56158 _headNodeID = _origWay.nodes[_origWay.nodes.length - 1];
56161 _wayGeometry = _origWay.geometry(context.graph());
56162 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56163 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56164 // We must make sure to replace or remove it later.
56166 context.pauseChangeDispatch();
56167 context.perform(actionNoop(), _annotation);
56168 context.resumeChangeDispatch();
56169 behavior.hover().initialNodeID(_headNodeID);
56170 behavior.on('move', function () {
56171 _pointerHasMoved = true;
56172 move.apply(this, arguments);
56173 }).on('down', function () {
56174 move.apply(this, arguments);
56175 }).on('downcancel', function () {
56176 if (_drawNode) removeDrawNode();
56177 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56178 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56179 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56180 setActiveElements();
56181 surface.call(behavior);
56182 context.history().on('undone.draw', undone);
56185 drawWay.off = function (surface) {
56186 if (!_didResolveTempEdit) {
56187 // Drawing was interrupted unexpectedly.
56188 // This can happen if the user changes modes,
56189 // clicks geolocate button, a hashchange event occurs, etc.
56190 context.pauseChangeDispatch();
56191 resetToStartGraph();
56192 context.resumeChangeDispatch();
56195 _drawNode = undefined;
56196 _nodeIndex = undefined;
56197 context.map().on('drawn.draw', null);
56198 surface.call(behavior.off).selectAll('.active').classed('active', false);
56199 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56200 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56201 context.history().on('undone.draw', null);
56204 function attemptAdd(d, loc, doAdd) {
56206 // move the node to the final loc in case move wasn't called
56207 // consistently (e.g. on touch devices)
56208 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56209 _drawNode = context.entity(_drawNode.id);
56211 createDrawNode(loc);
56215 /* includeDrawNode */
56218 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56219 if (!_pointerHasMoved) {
56220 // prevent the temporary draw node from appearing on touch devices
56224 dispatch.call('rejectedSelfIntersection', this);
56225 return; // can't click here
56228 context.pauseChangeDispatch();
56229 doAdd(); // we just replaced the temporary edit with the real one
56231 _didResolveTempEdit = true;
56232 context.resumeChangeDispatch();
56233 context.enter(mode);
56234 } // Accept the current position of the drawing node
56237 drawWay.add = function (loc, d) {
56238 attemptAdd(d, loc, function () {// don't need to do anything extra
56240 }; // Connect the way to an existing way
56243 drawWay.addWay = function (loc, edge, d) {
56244 attemptAdd(d, loc, function () {
56245 context.replace(actionAddMidpoint({
56248 }, _drawNode), _annotation);
56250 }; // Connect the way to an existing node
56253 drawWay.addNode = function (node, d) {
56254 // finish drawing if the mapper targets the prior node
56255 if (node.id === _headNodeID || // or the first node when drawing an area
56256 _origWay.isClosed() && node.id === _origWay.first()) {
56261 attemptAdd(d, node.loc, function () {
56262 context.replace(function actionReplaceDrawNode(graph) {
56263 // remove the temporary draw node and insert the existing node
56264 // at the same index
56265 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56266 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56269 }; // Finish the draw operation, removing the temporary edit.
56270 // If the way has enough nodes to be valid, it's selected.
56271 // Otherwise, delete everything and return to browse mode.
56274 drawWay.finish = function () {
56275 checkGeometry(false
56276 /* includeDrawNode */
56279 if (context.surface().classed('nope')) {
56280 dispatch.call('rejectedSelfIntersection', this);
56281 return; // can't click here
56284 context.pauseChangeDispatch(); // remove the temporary edit
56287 _didResolveTempEdit = true;
56288 context.resumeChangeDispatch();
56289 var way = context.hasEntity(wayID);
56291 if (!way || way.isDegenerate()) {
56296 window.setTimeout(function () {
56297 context.map().dblclickZoomEnable(true);
56299 var isNewFeature = !mode.isContinuing;
56300 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56301 }; // Cancel the draw operation, delete everything, and return to browse mode.
56304 drawWay.cancel = function () {
56305 context.pauseChangeDispatch();
56306 resetToStartGraph();
56307 context.resumeChangeDispatch();
56308 window.setTimeout(function () {
56309 context.map().dblclickZoomEnable(true);
56311 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56312 context.enter(modeBrowse(context));
56315 drawWay.nodeIndex = function (val) {
56316 if (!arguments.length) return _nodeIndex;
56321 drawWay.activeID = function () {
56322 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56327 return utilRebind(drawWay, dispatch, 'on');
56330 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56335 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56336 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56338 mode.wayID = wayID;
56339 mode.isContinuing = continuing;
56341 mode.enter = function () {
56342 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56343 context.install(behavior);
56346 mode.exit = function () {
56347 context.uninstall(behavior);
56350 mode.selectedIDs = function () {
56354 mode.activeID = function () {
56355 return behavior && behavior.activeID() || [];
56361 function validationDisconnectedWay() {
56362 var type = 'disconnected_way';
56364 function isTaggedAsHighway(entity) {
56365 return osmRoutableHighwayTagValues[entity.tags.highway];
56368 var validation = function checkDisconnectedWay(entity, graph) {
56369 var routingIslandWays = routingIslandForEntity(entity);
56370 if (!routingIslandWays) return [];
56371 return [new validationIssue({
56373 subtype: 'highway',
56374 severity: 'warning',
56375 message: function message(context) {
56376 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
56377 var label = entity && utilDisplayLabel(entity, context.graph());
56378 return _t.html('issues.disconnected_way.routable.message', {
56379 count: this.entityIds.length,
56383 reference: showReference,
56384 entityIds: Array.from(routingIslandWays).map(function (way) {
56387 dynamicFixes: makeFixes
56390 function makeFixes(context) {
56392 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
56394 if (singleEntity) {
56395 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
56396 var textDirection = _mainLocalizer.textDirection();
56397 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
56398 if (startFix) fixes.push(startFix);
56399 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
56400 if (endFix) fixes.push(endFix);
56403 if (!fixes.length) {
56404 fixes.push(new validationIssueFix({
56405 title: _t.html('issues.fix.connect_feature.title')
56409 fixes.push(new validationIssueFix({
56410 icon: 'iD-operation-delete',
56411 title: _t.html('issues.fix.delete_feature.title'),
56412 entityIds: [singleEntity.id],
56413 onClick: function onClick(context) {
56414 var id = this.issue.entityIds[0];
56415 var operation = operationDelete(context, [id]);
56417 if (!operation.disabled()) {
56423 fixes.push(new validationIssueFix({
56424 title: _t.html('issues.fix.connect_features.title')
56431 function showReference(selection) {
56432 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
56435 function routingIslandForEntity(entity) {
56436 var routingIsland = new Set(); // the interconnected routable features
56438 var waysToCheck = []; // the queue of remaining routable ways to traverse
56440 function queueParentWays(node) {
56441 graph.parentWays(node).forEach(function (parentWay) {
56442 if (!routingIsland.has(parentWay) && // only check each feature once
56443 isRoutableWay(parentWay, false)) {
56444 // only check routable features
56445 routingIsland.add(parentWay);
56446 waysToCheck.push(parentWay);
56451 if (entity.type === 'way' && isRoutableWay(entity, true)) {
56452 routingIsland.add(entity);
56453 waysToCheck.push(entity);
56454 } else if (entity.type === 'node' && isRoutableNode(entity)) {
56455 routingIsland.add(entity);
56456 queueParentWays(entity);
56458 // this feature isn't routable, cannot be a routing island
56462 while (waysToCheck.length) {
56463 var wayToCheck = waysToCheck.pop();
56464 var childNodes = graph.childNodes(wayToCheck);
56466 for (var i in childNodes) {
56467 var vertex = childNodes[i];
56469 if (isConnectedVertex(vertex)) {
56470 // found a link to the wider network, not a routing island
56474 if (isRoutableNode(vertex)) {
56475 routingIsland.add(vertex);
56478 queueParentWays(vertex);
56480 } // no network link found, this is a routing island, return its members
56483 return routingIsland;
56486 function isConnectedVertex(vertex) {
56487 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
56488 var osm = services.osm;
56489 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
56491 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
56492 if (vertex.tags.amenity === 'parking_entrance') return true;
56496 function isRoutableNode(node) {
56497 // treat elevators as distinct features in the highway network
56498 if (node.tags.highway === 'elevator') return true;
56502 function isRoutableWay(way, ignoreInnerWays) {
56503 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
56504 return graph.parentRelations(way).some(function (parentRelation) {
56505 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
56506 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
56511 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
56512 var vertex = graph.hasEntity(vertexID);
56513 if (!vertex || vertex.tags.noexit === 'yes') return null;
56514 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
56515 return new validationIssueFix({
56516 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56517 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
56518 entityIds: [vertexID],
56519 onClick: function onClick(context) {
56520 var wayId = this.issue.entityIds[0];
56521 var way = context.hasEntity(wayId);
56522 var vertexId = this.entityIds[0];
56523 var vertex = context.hasEntity(vertexId);
56524 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
56526 var map = context.map();
56528 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56529 map.zoomToEase(vertex);
56532 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
56538 validation.type = type;
56542 function validationFormatting() {
56543 var type = 'invalid_format';
56545 var validation = function validation(entity) {
56548 function isValidEmail(email) {
56549 // Emails in OSM are going to be official so they should be pretty simple
56550 // Using negated lists to better support all possible unicode characters (#6494)
56551 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
56553 return !email || valid_email.test(email);
56556 function isSchemePresent(url) {
56557 var valid_scheme = /^https?:\/\//i;
56558 return (!url || valid_scheme.test(url));
56563 function showReferenceEmail(selection) {
56564 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
56567 function showReferenceWebsite(selection) {
56568 selection.selectAll('.issue-reference')
56572 .attr('class', 'issue-reference')
56573 .html(t.html('issues.invalid_format.website.reference'));
56575 if (entity.tags.website) {
56576 // Multiple websites are possible
56577 // If ever we support ES6, arrow functions make this nicer
56578 var websites = entity.tags.website
56580 .map(function(s) { return s.trim(); })
56581 .filter(function(x) { return !isSchemePresent(x); });
56582 if (websites.length) {
56583 issues.push(new validationIssue({
56585 subtype: 'website',
56586 severity: 'warning',
56587 message: function(context) {
56588 var entity = context.hasEntity(this.entityIds[0]);
56589 return entity ? t.html('issues.invalid_format.website.message' + this.data,
56590 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
56592 reference: showReferenceWebsite,
56593 entityIds: [entity.id],
56594 hash: websites.join(),
56595 data: (websites.length > 1) ? '_multi' : ''
56602 if (entity.tags.email) {
56603 // Multiple emails are possible
56604 var emails = entity.tags.email.split(';').map(function (s) {
56606 }).filter(function (x) {
56607 return !isValidEmail(x);
56610 if (emails.length) {
56611 issues.push(new validationIssue({
56614 severity: 'warning',
56615 message: function message(context) {
56616 var entity = context.hasEntity(this.entityIds[0]);
56617 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
56618 feature: utilDisplayLabel(entity, context.graph()),
56619 email: emails.join(', ')
56622 reference: showReferenceEmail,
56623 entityIds: [entity.id],
56624 hash: emails.join(),
56625 data: emails.length > 1 ? '_multi' : ''
56633 validation.type = type;
56637 function validationHelpRequest(context) {
56638 var type = 'help_request';
56640 var validation = function checkFixmeTag(entity) {
56641 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
56643 if (entity.version === undefined) return [];
56645 if (entity.v !== undefined) {
56646 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
56648 if (!baseEntity || !baseEntity.tags.fixme) return [];
56651 return [new validationIssue({
56653 subtype: 'fixme_tag',
56654 severity: 'warning',
56655 message: function message(context) {
56656 var entity = context.hasEntity(this.entityIds[0]);
56657 return entity ? _t.html('issues.fixme_tag.message', {
56658 feature: utilDisplayLabel(entity, context.graph(), true
56663 dynamicFixes: function dynamicFixes() {
56664 return [new validationIssueFix({
56665 title: _t.html('issues.fix.address_the_concern.title')
56668 reference: showReference,
56669 entityIds: [entity.id]
56672 function showReference(selection) {
56673 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
56677 validation.type = type;
56681 function validationImpossibleOneway() {
56682 var type = 'impossible_oneway';
56684 var validation = function checkImpossibleOneway(entity, graph) {
56685 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
56686 if (entity.isClosed()) return [];
56687 if (!typeForWay(entity)) return [];
56688 if (!isOneway(entity)) return [];
56689 var firstIssues = issuesForNode(entity, entity.first());
56690 var lastIssues = issuesForNode(entity, entity.last());
56691 return firstIssues.concat(lastIssues);
56693 function typeForWay(way) {
56694 if (way.geometry(graph) !== 'line') return null;
56695 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
56696 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
56700 function isOneway(way) {
56701 if (way.tags.oneway === 'yes') return true;
56702 if (way.tags.oneway) return false;
56704 for (var key in way.tags) {
56705 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
56713 function nodeOccursMoreThanOnce(way, nodeID) {
56714 var occurrences = 0;
56716 for (var index in way.nodes) {
56717 if (way.nodes[index] === nodeID) {
56719 if (occurrences > 1) return true;
56726 function isConnectedViaOtherTypes(way, node) {
56727 var wayType = typeForWay(way);
56729 if (wayType === 'highway') {
56730 // entrances are considered connected
56731 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
56732 if (node.tags.amenity === 'parking_entrance') return true;
56733 } else if (wayType === 'waterway') {
56734 if (node.id === way.first()) {
56735 // multiple waterways may start at the same spring
56736 if (node.tags.natural === 'spring') return true;
56738 // multiple waterways may end at the same drain
56739 if (node.tags.manhole === 'drain') return true;
56743 return graph.parentWays(node).some(function (parentWay) {
56744 if (parentWay.id === way.id) return false;
56746 if (wayType === 'highway') {
56747 // allow connections to highway areas
56748 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
56750 if (parentWay.tags.route === 'ferry') return true;
56751 return graph.parentRelations(parentWay).some(function (parentRelation) {
56752 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
56754 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
56756 } else if (wayType === 'waterway') {
56757 // multiple waterways may start or end at a water body at the same node
56758 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
56765 function issuesForNode(way, nodeID) {
56766 var isFirst = nodeID === way.first();
56767 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
56769 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
56770 var osm = services.osm;
56771 if (!osm) return [];
56772 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
56774 if (!node || !osm.isDataLoaded(node.loc)) return [];
56775 if (isConnectedViaOtherTypes(way, node)) return [];
56776 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
56777 if (parentWay.id === way.id) return false;
56778 return typeForWay(parentWay) === wayType;
56779 }); // assume it's okay for waterways to start or end disconnected for now
56781 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
56782 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
56783 return isOneway(attachedWay);
56784 }); // ignore if the way is connected to some non-oneway features
56786 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
56788 if (attachedOneways.length) {
56789 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
56790 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
56791 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
56794 if (connectedEndpointsOkay) return [];
56797 var placement = isFirst ? 'start' : 'end',
56798 messageID = wayType + '.',
56799 referenceID = wayType + '.';
56801 if (wayType === 'waterway') {
56802 messageID += 'connected.' + placement;
56803 referenceID += 'connected';
56805 messageID += placement;
56806 referenceID += placement;
56809 return [new validationIssue({
56812 severity: 'warning',
56813 message: function message(context) {
56814 var entity = context.hasEntity(this.entityIds[0]);
56815 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
56816 feature: utilDisplayLabel(entity, context.graph())
56819 reference: getReference(referenceID),
56820 entityIds: [way.id, node.id],
56821 dynamicFixes: function dynamicFixes() {
56824 if (attachedOneways.length) {
56825 fixes.push(new validationIssueFix({
56826 icon: 'iD-operation-reverse',
56827 title: _t.html('issues.fix.reverse_feature.title'),
56828 entityIds: [way.id],
56829 onClick: function onClick(context) {
56830 var id = this.issue.entityIds[0];
56831 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
56838 if (node.tags.noexit !== 'yes') {
56839 var textDirection = _mainLocalizer.textDirection();
56840 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
56841 fixes.push(new validationIssueFix({
56842 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
56843 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
56844 onClick: function onClick(context) {
56845 var entityID = this.issue.entityIds[0];
56846 var vertexID = this.issue.entityIds[1];
56847 var way = context.entity(entityID);
56848 var vertex = context.entity(vertexID);
56849 continueDrawing(way, vertex, context);
56859 function getReference(referenceID) {
56860 return function showReference(selection) {
56861 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
56867 function continueDrawing(way, vertex, context) {
56868 // make sure the vertex is actually visible and editable
56869 var map = context.map();
56871 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
56872 map.zoomToEase(vertex);
56875 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
56878 validation.type = type;
56882 function validationIncompatibleSource() {
56883 var type = 'incompatible_source';
56884 var invalidSources = [{
56887 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
56890 var validation = function checkIncompatibleSource(entity) {
56891 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
56892 if (!entitySources) return [];
56894 invalidSources.forEach(function (invalidSource) {
56895 var hasInvalidSource = entitySources.some(function (source) {
56896 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
56897 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
56900 if (!hasInvalidSource) return;
56901 issues.push(new validationIssue({
56903 severity: 'warning',
56904 message: function message(context) {
56905 var entity = context.hasEntity(this.entityIds[0]);
56906 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
56907 feature: utilDisplayLabel(entity, context.graph(), true
56912 reference: getReference(invalidSource.id),
56913 entityIds: [entity.id],
56914 dynamicFixes: function dynamicFixes() {
56915 return [new validationIssueFix({
56916 title: _t.html('issues.fix.remove_proprietary_data.title')
56923 function getReference(id) {
56924 return function showReference(selection) {
56925 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
56930 validation.type = type;
56934 function validationMaprules() {
56935 var type = 'maprules';
56937 var validation = function checkMaprules(entity, graph) {
56938 if (!services.maprules) return [];
56939 var rules = services.maprules.validationRules();
56942 for (var i = 0; i < rules.length; i++) {
56943 var rule = rules[i];
56944 rule.findIssues(entity, graph, issues);
56950 validation.type = type;
56954 function validationMismatchedGeometry() {
56955 var type = 'mismatched_geometry';
56957 function tagSuggestingLineIsArea(entity) {
56958 if (entity.type !== 'way' || entity.isClosed()) return null;
56959 var tagSuggestingArea = entity.tagSuggestingArea();
56961 if (!tagSuggestingArea) {
56965 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
56966 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
56968 if (asLine && asArea && asLine === asArea) {
56969 // these tags also allow lines and making this an area wouldn't matter
56973 return tagSuggestingArea;
56976 function makeConnectEndpointsFixOnClick(way, graph) {
56977 // must have at least three nodes to close this automatically
56978 if (way.nodes.length < 3) return null;
56979 var nodes = graph.childNodes(way),
56981 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
56983 if (firstToLastDistanceMeters < 0.75) {
56984 testNodes = nodes.slice(); // shallow copy
56987 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
56989 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
56990 return function (context) {
56991 var way = context.entity(this.issue.entityIds[0]);
56992 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
56995 } // if the points were not merged, attempt to close the way
56998 testNodes = nodes.slice(); // shallow copy
57000 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
57002 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
57003 return function (context) {
57004 var wayId = this.issue.entityIds[0];
57005 var way = context.entity(wayId);
57006 var nodeId = way.nodes[0];
57007 var index = way.nodes.length;
57008 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
57013 function lineTaggedAsAreaIssue(entity) {
57014 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
57015 if (!tagSuggestingArea) return null;
57016 return new validationIssue({
57018 subtype: 'area_as_line',
57019 severity: 'warning',
57020 message: function message(context) {
57021 var entity = context.hasEntity(this.entityIds[0]);
57022 return entity ? _t.html('issues.tag_suggests_area.message', {
57023 feature: utilDisplayLabel(entity, 'area', true
57027 tags: tagSuggestingArea
57031 reference: showReference,
57032 entityIds: [entity.id],
57033 hash: JSON.stringify(tagSuggestingArea),
57034 dynamicFixes: function dynamicFixes(context) {
57036 var entity = context.entity(this.entityIds[0]);
57037 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
57038 fixes.push(new validationIssueFix({
57039 title: _t.html('issues.fix.connect_endpoints.title'),
57040 onClick: connectEndsOnClick
57042 fixes.push(new validationIssueFix({
57043 icon: 'iD-operation-delete',
57044 title: _t.html('issues.fix.remove_tag.title'),
57045 onClick: function onClick(context) {
57046 var entityId = this.issue.entityIds[0];
57047 var entity = context.entity(entityId);
57048 var tags = Object.assign({}, entity.tags); // shallow copy
57050 for (var key in tagSuggestingArea) {
57054 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
57061 function showReference(selection) {
57062 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
57066 function vertexPointIssue(entity, graph) {
57067 // we only care about nodes
57068 if (entity.type !== 'node') return null; // ignore tagless points
57070 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
57072 if (entity.isOnAddressLine(graph)) return null;
57073 var geometry = entity.geometry(graph);
57074 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
57076 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
57077 return new validationIssue({
57079 subtype: 'vertex_as_point',
57080 severity: 'warning',
57081 message: function message(context) {
57082 var entity = context.hasEntity(this.entityIds[0]);
57083 return entity ? _t.html('issues.vertex_as_point.message', {
57084 feature: utilDisplayLabel(entity, 'vertex', true
57089 reference: function showReference(selection) {
57090 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
57092 entityIds: [entity.id]
57094 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
57095 return new validationIssue({
57097 subtype: 'point_as_vertex',
57098 severity: 'warning',
57099 message: function message(context) {
57100 var entity = context.hasEntity(this.entityIds[0]);
57101 return entity ? _t.html('issues.point_as_vertex.message', {
57102 feature: utilDisplayLabel(entity, 'point', true
57107 reference: function showReference(selection) {
57108 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
57110 entityIds: [entity.id],
57111 dynamicFixes: extractPointDynamicFixes
57118 function otherMismatchIssue(entity, graph) {
57119 // ignore boring features
57120 if (!entity.hasInterestingTags()) return null;
57121 if (entity.type !== 'node' && entity.type !== 'way') return null; // address lines are special so just ignore them
57123 if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;
57124 var sourceGeom = entity.geometry(graph);
57125 var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];
57126 if (sourceGeom === 'area') targetGeoms.unshift('line');
57127 var targetGeom = targetGeoms.find(function (nodeGeom) {
57128 var asSource = _mainPresetIndex.matchTags(entity.tags, sourceGeom);
57129 var asTarget = _mainPresetIndex.matchTags(entity.tags, nodeGeom);
57130 if (!asSource || !asTarget || asSource === asTarget || // sometimes there are two presets with the same tags for different geometries
57131 fastDeepEqual(asSource.tags, asTarget.tags)) return false;
57132 if (asTarget.isFallback()) return false;
57133 var primaryKey = Object.keys(asTarget.tags)[0]; // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them
57135 if (primaryKey === 'building') return false;
57136 if (asTarget.tags[primaryKey] === '*') return false;
57137 return asSource.isFallback() || asSource.tags[primaryKey] === '*';
57139 if (!targetGeom) return null;
57140 var subtype = targetGeom + '_as_' + sourceGeom;
57141 if (targetGeom === 'vertex') targetGeom = 'point';
57142 if (sourceGeom === 'vertex') sourceGeom = 'point';
57143 var referenceId = targetGeom + '_as_' + sourceGeom;
57146 if (targetGeom === 'point') {
57147 dynamicFixes = extractPointDynamicFixes;
57148 } else if (sourceGeom === 'area' && targetGeom === 'line') {
57149 dynamicFixes = lineToAreaDynamicFixes;
57152 return new validationIssue({
57155 severity: 'warning',
57156 message: function message(context) {
57157 var entity = context.hasEntity(this.entityIds[0]);
57158 return entity ? _t.html('issues.' + referenceId + '.message', {
57159 feature: utilDisplayLabel(entity, targetGeom, true
57164 reference: function showReference(selection) {
57165 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.mismatched_geometry.reference'));
57167 entityIds: [entity.id],
57168 dynamicFixes: dynamicFixes
57172 function lineToAreaDynamicFixes(context) {
57173 var convertOnClick;
57174 var entityId = this.entityIds[0];
57175 var entity = context.entity(entityId);
57176 var tags = Object.assign({}, entity.tags); // shallow copy
57180 if (!osmTagSuggestingArea(tags)) {
57181 // if removing the area tag would make this a line, offer that as a quick fix
57182 convertOnClick = function convertOnClick(context) {
57183 var entityId = this.issue.entityIds[0];
57184 var entity = context.entity(entityId);
57185 var tags = Object.assign({}, entity.tags); // shallow copy
57191 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.convert_to_line.annotation'));
57195 return [new validationIssueFix({
57196 icon: 'iD-icon-line',
57197 title: _t.html('issues.fix.convert_to_line.title'),
57198 onClick: convertOnClick
57202 function extractPointDynamicFixes(context) {
57203 var entityId = this.entityIds[0];
57204 var extractOnClick = null;
57206 if (!context.hasHiddenConnections(entityId)) {
57207 extractOnClick = function extractOnClick(context) {
57208 var entityId = this.issue.entityIds[0];
57209 var action = actionExtract(entityId, context.projection);
57210 context.perform(action, _t('operations.extract.annotation', {
57212 })); // re-enter mode to trigger updates
57214 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
57218 return [new validationIssueFix({
57219 icon: 'iD-operation-extract',
57220 title: _t.html('issues.fix.extract_point.title'),
57221 onClick: extractOnClick
57225 function unclosedMultipolygonPartIssues(entity, graph) {
57226 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
57227 !entity.isComplete(graph)) return [];
57228 var sequences = osmJoinWays(entity.members, graph);
57231 for (var i in sequences) {
57232 var sequence = sequences[i];
57233 if (!sequence.nodes) continue;
57234 var firstNode = sequence.nodes[0];
57235 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
57237 if (firstNode === lastNode) continue;
57238 var issue = new validationIssue({
57240 subtype: 'unclosed_multipolygon_part',
57241 severity: 'warning',
57242 message: function message(context) {
57243 var entity = context.hasEntity(this.entityIds[0]);
57244 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
57245 feature: utilDisplayLabel(entity, context.graph(), true
57250 reference: showReference,
57251 loc: sequence.nodes[0].loc,
57252 entityIds: [entity.id],
57253 hash: sequence.map(function (way) {
57257 issues.push(issue);
57262 function showReference(selection) {
57263 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
57267 var validation = function checkMismatchedGeometry(entity, graph) {
57268 var vertexPoint = vertexPointIssue(entity, graph);
57269 if (vertexPoint) return [vertexPoint];
57270 var lineAsArea = lineTaggedAsAreaIssue(entity);
57271 if (lineAsArea) return [lineAsArea];
57272 var mismatch = otherMismatchIssue(entity, graph);
57273 if (mismatch) return [mismatch];
57274 return unclosedMultipolygonPartIssues(entity, graph);
57277 validation.type = type;
57281 function validationMissingRole() {
57282 var type = 'missing_role';
57284 var validation = function checkMissingRole(entity, graph) {
57287 if (entity.type === 'way') {
57288 graph.parentRelations(entity).forEach(function (relation) {
57289 if (!relation.isMultipolygon()) return;
57290 var member = relation.memberById(entity.id);
57292 if (member && isMissingRole(member)) {
57293 issues.push(makeIssue(entity, relation, member));
57296 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
57297 entity.indexedMembers().forEach(function (member) {
57298 var way = graph.hasEntity(member.id);
57300 if (way && isMissingRole(member)) {
57301 issues.push(makeIssue(way, entity, member));
57309 function isMissingRole(member) {
57310 return !member.role || !member.role.trim().length;
57313 function makeIssue(way, relation, member) {
57314 return new validationIssue({
57316 severity: 'warning',
57317 message: function message(context) {
57318 var member = context.hasEntity(this.entityIds[1]),
57319 relation = context.hasEntity(this.entityIds[0]);
57320 return member && relation ? _t.html('issues.missing_role.message', {
57321 member: utilDisplayLabel(member, context.graph()),
57322 relation: utilDisplayLabel(relation, context.graph())
57325 reference: showReference,
57326 entityIds: [relation.id, way.id],
57330 hash: member.index.toString(),
57331 dynamicFixes: function dynamicFixes() {
57332 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
57333 icon: 'iD-operation-delete',
57334 title: _t.html('issues.fix.remove_from_relation.title'),
57335 onClick: function onClick(context) {
57336 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
57344 function showReference(selection) {
57345 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
57349 function makeAddRoleFix(role) {
57350 return new validationIssueFix({
57351 title: _t.html('issues.fix.set_as_' + role + '.title'),
57352 onClick: function onClick(context) {
57353 var oldMember = this.issue.data.member;
57355 id: this.issue.entityIds[1],
57356 type: oldMember.type,
57359 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
57366 validation.type = type;
57370 function validationMissingTag(context) {
57371 var type = 'missing_tag';
57373 function hasDescriptiveTags(entity, graph) {
57374 var onlyAttributeKeys = ['description', 'name', 'note', 'start_date'];
57375 var entityDescriptiveKeys = Object.keys(entity.tags).filter(function (k) {
57376 if (k === 'area' || !osmIsInterestingTag(k)) return false;
57377 return !onlyAttributeKeys.some(function (attributeKey) {
57378 return k === attributeKey || k.indexOf(attributeKey + ':') === 0;
57382 if (entity.type === 'relation' && entityDescriptiveKeys.length === 1 && entity.tags.type === 'multipolygon') {
57383 // this relation's only interesting tag just says its a multipolygon,
57384 // which is not descriptive enough
57385 // It's okay for a simple multipolygon to have no descriptive tags
57386 // if its outer way has them (old model, see `outdated_tags.js`)
57387 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57390 return entityDescriptiveKeys.length > 0;
57393 function isUnknownRoad(entity) {
57394 return entity.type === 'way' && entity.tags.highway === 'road';
57397 function isUntypedRelation(entity) {
57398 return entity.type === 'relation' && !entity.tags.type;
57401 var validation = function checkMissingTag(entity, graph) {
57403 var osm = context.connection();
57404 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
57406 if (!isUnloadedNode && // allow untagged nodes that are part of ways
57407 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
57408 !entity.hasParentRelations(graph)) {
57409 if (Object.keys(entity.tags).length === 0) {
57411 } else if (!hasDescriptiveTags(entity, graph)) {
57412 subtype = 'descriptive';
57413 } else if (isUntypedRelation(entity)) {
57414 subtype = 'relation_type';
57416 } // flag an unknown road even if it's a member of a relation
57419 if (!subtype && isUnknownRoad(entity)) {
57420 subtype = 'highway_classification';
57423 if (!subtype) return [];
57424 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
57425 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
57427 var canDelete = entity.version === undefined || entity.v !== undefined;
57428 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
57429 return [new validationIssue({
57432 severity: severity,
57433 message: function message(context) {
57434 var entity = context.hasEntity(this.entityIds[0]);
57435 return entity ? _t.html('issues.' + messageID + '.message', {
57436 feature: utilDisplayLabel(entity, context.graph())
57439 reference: showReference,
57440 entityIds: [entity.id],
57441 dynamicFixes: function dynamicFixes(context) {
57443 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
57444 fixes.push(new validationIssueFix({
57445 icon: 'iD-icon-search',
57446 title: _t.html('issues.fix.' + selectFixType + '.title'),
57447 onClick: function onClick(context) {
57448 context.ui().sidebar.showPresetList();
57452 var id = this.entityIds[0];
57453 var operation = operationDelete(context, [id]);
57454 var disabledReasonID = operation.disabled();
57456 if (!disabledReasonID) {
57457 deleteOnClick = function deleteOnClick(context) {
57458 var id = this.issue.entityIds[0];
57459 var operation = operationDelete(context, [id]);
57461 if (!operation.disabled()) {
57467 fixes.push(new validationIssueFix({
57468 icon: 'iD-operation-delete',
57469 title: _t.html('issues.fix.delete_feature.title'),
57470 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
57471 onClick: deleteOnClick
57477 function showReference(selection) {
57478 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
57482 validation.type = type;
57486 function validationOutdatedTags() {
57487 var type = 'outdated_tags';
57488 var _waitingForDeprecated = true;
57490 var _dataDeprecated; // fetch deprecated tags
57493 _mainFileFetcher.get('deprecated').then(function (d) {
57494 return _dataDeprecated = d;
57495 })["catch"](function () {
57497 })["finally"](function () {
57498 return _waitingForDeprecated = false;
57501 function oldTagIssues(entity, graph) {
57502 var oldTags = Object.assign({}, entity.tags); // shallow copy
57504 var preset = _mainPresetIndex.match(entity, graph);
57505 var subtype = 'deprecated_tags';
57506 if (!preset) return [];
57507 if (!entity.hasInterestingTags()) return []; // Upgrade preset, if a replacement is available..
57509 if (preset.replacement) {
57510 var newPreset = _mainPresetIndex.item(preset.replacement);
57511 graph = actionChangePreset(entity.id, preset, newPreset, true
57512 /* skip field defaults */
57514 entity = graph.entity(entity.id);
57515 preset = newPreset;
57516 } // Upgrade deprecated tags..
57519 if (_dataDeprecated) {
57520 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
57522 if (deprecatedTags.length) {
57523 deprecatedTags.forEach(function (tag) {
57524 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
57526 entity = graph.entity(entity.id);
57528 } // Add missing addTags from the detected preset
57531 var newTags = Object.assign({}, entity.tags); // shallow copy
57533 if (preset.tags !== preset.addTags) {
57534 Object.keys(preset.addTags).forEach(function (k) {
57536 if (preset.addTags[k] === '*') {
57537 newTags[k] = 'yes';
57539 newTags[k] = preset.addTags[k];
57543 } // Attempt to match a canonical record in the name-suggestion-index.
57546 var nsi = services.nsi;
57547 var waitingForNsi = false;
57550 waitingForNsi = nsi.status() === 'loading';
57552 if (!waitingForNsi) {
57553 var loc = entity.extent(graph).center();
57554 var result = nsi.upgradeTags(newTags, loc);
57558 subtype = 'noncanonical_brand';
57564 issues.provisional = _waitingForDeprecated || waitingForNsi; // determine diff
57566 var tagDiff = utilTagDiff(oldTags, newTags);
57567 if (!tagDiff.length) return issues;
57568 var isOnlyAddingTags = tagDiff.every(function (d) {
57569 return d.type === '+';
57573 if (subtype === 'noncanonical_brand') {
57574 prefix = 'noncanonical_brand.';
57575 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
57576 subtype = 'incomplete_tags';
57577 prefix = 'incomplete.';
57578 } // don't allow autofixing brand tags
57581 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
57582 issues.push(new validationIssue({
57585 severity: 'warning',
57586 message: showMessage,
57587 reference: showReference,
57588 entityIds: [entity.id],
57589 hash: utilHashcode(JSON.stringify(tagDiff)),
57590 dynamicFixes: function dynamicFixes() {
57591 return [new validationIssueFix({
57592 autoArgs: autoArgs,
57593 title: _t.html('issues.fix.upgrade_tags.title'),
57594 onClick: function onClick(context) {
57595 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57602 function doUpgrade(graph) {
57603 var currEntity = graph.hasEntity(entity.id);
57604 if (!currEntity) return graph;
57605 var newTags = Object.assign({}, currEntity.tags); // shallow copy
57607 tagDiff.forEach(function (diff) {
57608 if (diff.type === '-') {
57609 delete newTags[diff.key];
57610 } else if (diff.type === '+') {
57611 newTags[diff.key] = diff.newVal;
57614 return actionChangeTags(currEntity.id, newTags)(graph);
57617 function showMessage(context) {
57618 var currEntity = context.hasEntity(entity.id);
57619 if (!currEntity) return '';
57620 var messageID = "issues.outdated_tags.".concat(prefix, "message");
57622 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
57623 messageID += '_incomplete';
57626 return _t.html(messageID, {
57627 feature: utilDisplayLabel(currEntity, context.graph(), true
57633 function showReference(selection) {
57634 var enter = selection.selectAll('.issue-reference').data([0]).enter();
57635 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
57636 enter.append('strong').html(_t.html('issues.suggested'));
57637 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) {
57638 var klass = d.type === '+' ? 'add' : 'remove';
57639 return "tagDiff-cell tagDiff-cell-".concat(klass);
57640 }).html(function (d) {
57646 function oldMultipolygonIssues(entity, graph) {
57647 var multipolygon, outerWay;
57649 if (entity.type === 'relation') {
57650 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
57651 multipolygon = entity;
57652 } else if (entity.type === 'way') {
57653 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
57659 if (!multipolygon || !outerWay) return [];
57660 return [new validationIssue({
57662 subtype: 'old_multipolygon',
57663 severity: 'warning',
57664 message: showMessage,
57665 reference: showReference,
57666 entityIds: [outerWay.id, multipolygon.id],
57667 dynamicFixes: function dynamicFixes() {
57668 return [new validationIssueFix({
57669 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
57670 title: _t.html('issues.fix.move_tags.title'),
57671 onClick: function onClick(context) {
57672 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
57678 function doUpgrade(graph) {
57679 var currMultipolygon = graph.hasEntity(multipolygon.id);
57680 var currOuterWay = graph.hasEntity(outerWay.id);
57681 if (!currMultipolygon || !currOuterWay) return graph;
57682 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
57683 graph = graph.replace(currMultipolygon);
57684 return actionChangeTags(currOuterWay.id, {})(graph);
57687 function showMessage(context) {
57688 var currMultipolygon = context.hasEntity(multipolygon.id);
57689 if (!currMultipolygon) return '';
57690 return _t.html('issues.old_multipolygon.message', {
57691 multipolygon: utilDisplayLabel(currMultipolygon, context.graph(), true
57697 function showReference(selection) {
57698 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
57702 var validation = function checkOutdatedTags(entity, graph) {
57703 var issues = oldMultipolygonIssues(entity, graph);
57704 if (!issues.length) issues = oldTagIssues(entity, graph);
57708 validation.type = type;
57712 function validationPrivateData() {
57713 var type = 'private_data'; // assume that some buildings are private
57715 var privateBuildingValues = {
57721 semidetached_house: true,
57722 static_caravan: true
57723 }; // but they might be public if they have one of these other tags
57733 }; // these tags may contain personally identifying info
57735 var personalTags = {
57736 'contact:email': true,
57737 'contact:fax': true,
57738 'contact:phone': true,
57744 var validation = function checkPrivateData(entity) {
57745 var tags = entity.tags;
57746 if (!tags.building || !privateBuildingValues[tags.building]) return [];
57749 for (var k in tags) {
57750 if (publicKeys[k]) return []; // probably a public feature
57752 if (!personalTags[k]) {
57753 keepTags[k] = tags[k];
57757 var tagDiff = utilTagDiff(tags, keepTags);
57758 if (!tagDiff.length) return [];
57759 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
57760 return [new validationIssue({
57762 severity: 'warning',
57763 message: showMessage,
57764 reference: showReference,
57765 entityIds: [entity.id],
57766 dynamicFixes: function dynamicFixes() {
57767 return [new validationIssueFix({
57768 icon: 'iD-operation-delete',
57769 title: _t.html('issues.fix.' + fixID + '.title'),
57770 onClick: function onClick(context) {
57771 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
57777 function doUpgrade(graph) {
57778 var currEntity = graph.hasEntity(entity.id);
57779 if (!currEntity) return graph;
57780 var newTags = Object.assign({}, currEntity.tags); // shallow copy
57782 tagDiff.forEach(function (diff) {
57783 if (diff.type === '-') {
57784 delete newTags[diff.key];
57785 } else if (diff.type === '+') {
57786 newTags[diff.key] = diff.newVal;
57789 return actionChangeTags(currEntity.id, newTags)(graph);
57792 function showMessage(context) {
57793 var currEntity = context.hasEntity(this.entityIds[0]);
57794 if (!currEntity) return '';
57795 return _t.html('issues.private_data.contact.message', {
57796 feature: utilDisplayLabel(currEntity, context.graph())
57800 function showReference(selection) {
57801 var enter = selection.selectAll('.issue-reference').data([0]).enter();
57802 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
57803 enter.append('strong').html(_t.html('issues.suggested'));
57804 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) {
57805 var klass = d.type === '+' ? 'add' : 'remove';
57806 return 'tagDiff-cell tagDiff-cell-' + klass;
57807 }).html(function (d) {
57813 validation.type = type;
57817 function validationSuspiciousName() {
57818 var type = 'suspicious_name';
57819 var keysToTestForGenericValues = ['aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'];
57820 var _waitingForNsi = false; // Attempt to match a generic record in the name-suggestion-index.
57822 function isGenericMatchInNsi(tags) {
57823 var nsi = services.nsi;
57826 _waitingForNsi = nsi.status() === 'loading';
57828 if (!_waitingForNsi) {
57829 return nsi.isGenericName(tags);
57834 } // Test if the name is just the key or tag value (e.g. "park")
57837 function nameMatchesRawTag(lowercaseName, tags) {
57838 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
57839 var key = keysToTestForGenericValues[i];
57840 var val = tags[key];
57843 val = val.toLowerCase();
57845 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
57854 function isGenericName(name, tags) {
57855 name = name.toLowerCase();
57856 return nameMatchesRawTag(name, tags) || isGenericMatchInNsi(tags);
57859 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
57860 return new validationIssue({
57862 subtype: 'generic_name',
57863 severity: 'warning',
57864 message: function message(context) {
57865 var entity = context.hasEntity(this.entityIds[0]);
57866 if (!entity) return '';
57867 var preset = _mainPresetIndex.match(entity, context.graph());
57868 var langName = langCode && _mainLocalizer.languageName(langCode);
57869 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
57870 feature: preset.name(),
57875 reference: showReference,
57876 entityIds: [entityId],
57877 hash: "".concat(nameKey, "=").concat(genericName),
57878 dynamicFixes: function dynamicFixes() {
57879 return [new validationIssueFix({
57880 icon: 'iD-operation-delete',
57881 title: _t.html('issues.fix.remove_the_name.title'),
57882 onClick: function onClick(context) {
57883 var entityId = this.issue.entityIds[0];
57884 var entity = context.entity(entityId);
57885 var tags = Object.assign({}, entity.tags); // shallow copy
57887 delete tags[nameKey];
57888 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
57894 function showReference(selection) {
57895 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57899 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
57900 return new validationIssue({
57902 subtype: 'not_name',
57903 severity: 'warning',
57904 message: function message(context) {
57905 var entity = context.hasEntity(this.entityIds[0]);
57906 if (!entity) return '';
57907 var preset = _mainPresetIndex.match(entity, context.graph());
57908 var langName = langCode && _mainLocalizer.languageName(langCode);
57909 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
57910 feature: preset.name(),
57911 name: incorrectName,
57915 reference: showReference,
57916 entityIds: [entityId],
57917 hash: "".concat(nameKey, "=").concat(incorrectName),
57918 dynamicFixes: function dynamicFixes() {
57919 return [new validationIssueFix({
57920 icon: 'iD-operation-delete',
57921 title: _t.html('issues.fix.remove_the_name.title'),
57922 onClick: function onClick(context) {
57923 var entityId = this.issue.entityIds[0];
57924 var entity = context.entity(entityId);
57925 var tags = Object.assign({}, entity.tags); // shallow copy
57927 delete tags[nameKey];
57928 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
57934 function showReference(selection) {
57935 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
57939 var validation = function checkGenericName(entity) {
57940 var tags = entity.tags; // a generic name is allowed if it's a known brand or entity
57942 var hasWikidata = !!tags.wikidata || !!tags['brand:wikidata'] || !!tags['operator:wikidata'];
57943 if (hasWikidata) return [];
57945 var notNames = (tags['not:name'] || '').split(';');
57947 for (var key in tags) {
57948 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
57950 var langCode = m.length >= 2 ? m[1] : null;
57951 var value = tags[key];
57953 if (notNames.length) {
57954 for (var i in notNames) {
57955 var notName = notNames[i];
57957 if (notName && value === notName) {
57958 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
57964 if (isGenericName(value, tags)) {
57965 issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading
57967 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
57974 validation.type = type;
57978 function validationUnsquareWay(context) {
57979 var type = 'unsquare_way';
57980 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
57981 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
57983 var epsilon = 0.05;
57984 var nodeThreshold = 10;
57986 function isBuilding(entity, graph) {
57987 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
57988 return entity.tags.building && entity.tags.building !== 'no';
57991 var validation = function checkUnsquareWay(entity, graph) {
57992 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
57994 if (entity.tags.nonsquare === 'yes') return [];
57995 var isClosed = entity.isClosed();
57996 if (!isClosed) return []; // this building has bigger problems
57997 // don't flag ways with lots of nodes since they are likely detail-mapped
57999 var nodes = graph.childNodes(entity).slice(); // shallow copy
58001 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
58002 // ignore if not all nodes are fully downloaded
58004 var osm = services.osm;
58005 if (!osm || nodes.some(function (node) {
58006 return !osm.isDataLoaded(node.loc);
58007 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
58009 var hasConnectedSquarableWays = nodes.some(function (node) {
58010 return graph.parentWays(node).some(function (way) {
58011 if (way.id === entity.id) return false;
58012 if (isBuilding(way, graph)) return true;
58013 return graph.parentRelations(way).some(function (parentRelation) {
58014 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
58018 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
58020 var storedDegreeThreshold = corePreferences('validate-square-degrees');
58021 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
58022 var points = nodes.map(function (node) {
58023 return context.projection(node.loc);
58025 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
58026 var autoArgs; // don't allow autosquaring features linked to wikidata
58028 if (!entity.tags.wikidata) {
58029 // use same degree threshold as for detection
58030 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
58031 autoAction.transitionable = false; // when autofixing, do it instantly
58033 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
58038 return [new validationIssue({
58040 subtype: 'building',
58041 severity: 'warning',
58042 message: function message(context) {
58043 var entity = context.hasEntity(this.entityIds[0]);
58044 return entity ? _t.html('issues.unsquare_way.message', {
58045 feature: utilDisplayLabel(entity, context.graph())
58048 reference: showReference,
58049 entityIds: [entity.id],
58050 hash: degreeThreshold,
58051 dynamicFixes: function dynamicFixes() {
58052 return [new validationIssueFix({
58053 icon: 'iD-operation-orthogonalize',
58054 title: _t.html('issues.fix.square_feature.title'),
58055 autoArgs: autoArgs,
58056 onClick: function onClick(context, completionHandler) {
58057 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
58059 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
58061 })); // run after the squaring transition (currently 150ms)
58063 window.setTimeout(function () {
58064 completionHandler();
58069 new validationIssueFix({
58070 title: t.html('issues.fix.tag_as_unsquare.title'),
58071 onClick: function(context) {
58072 var entityId = this.issue.entityIds[0];
58073 var entity = context.entity(entityId);
58074 var tags = Object.assign({}, entity.tags); // shallow copy
58075 tags.nonsquare = 'yes';
58077 actionChangeTags(entityId, tags),
58078 t('issues.fix.tag_as_unsquare.annotation')
58087 function showReference(selection) {
58088 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
58092 validation.type = type;
58096 var Validations = /*#__PURE__*/Object.freeze({
58098 validationAlmostJunction: validationAlmostJunction,
58099 validationCloseNodes: validationCloseNodes,
58100 validationCrossingWays: validationCrossingWays,
58101 validationDisconnectedWay: validationDisconnectedWay,
58102 validationFormatting: validationFormatting,
58103 validationHelpRequest: validationHelpRequest,
58104 validationImpossibleOneway: validationImpossibleOneway,
58105 validationIncompatibleSource: validationIncompatibleSource,
58106 validationMaprules: validationMaprules,
58107 validationMismatchedGeometry: validationMismatchedGeometry,
58108 validationMissingRole: validationMissingRole,
58109 validationMissingTag: validationMissingTag,
58110 validationOutdatedTags: validationOutdatedTags,
58111 validationPrivateData: validationPrivateData,
58112 validationSuspiciousName: validationSuspiciousName,
58113 validationUnsquareWay: validationUnsquareWay
58116 function coreValidator(context) {
58119 var dispatch = dispatch$8('validated', 'focusedIssue');
58120 var validator = utilRebind({}, dispatch, 'on');
58122 var _disabledRules = {};
58124 var _ignoredIssueIDs = new Set();
58126 var _resolvedIssueIDs = new Set();
58128 var _baseCache = validationCache('base'); // issues before any user edits
58131 var _headCache = validationCache('head'); // issues after all user edits
58134 var _completeDiff = {}; // complete diff base -> head of what the user changed
58136 var _headIsCurrent = false;
58138 var _deferredRIC = new Set(); // Set( RequestIdleCallback handles )
58141 var _deferredST = new Set(); // Set( SetTimeout handles )
58144 var _headPromise; // Promise fulfilled when validation is performed up to headGraph snapshot
58147 var RETRY = 5000; // wait 5sec before revalidating provisional entities
58148 // Allow validation severity to be overridden by url queryparams...
58149 // See: https://github.com/openstreetmap/iD/pull/8243
58151 // Each param should contain a urlencoded comma separated list of
58152 // `type/subtype` rules. `*` may be used as a wildcard..
58154 // `validationError=disconnected_way/*`
58155 // `validationError=disconnected_way/highway`
58156 // `validationError=crossing_ways/bridge*`
58157 // `validationError=crossing_ways/bridge*,crossing_ways/tunnel*`
58159 var _errorOverrides = parseHashParam(context.initialHashParams.validationError);
58161 var _warningOverrides = parseHashParam(context.initialHashParams.validationWarning);
58163 var _disableOverrides = parseHashParam(context.initialHashParams.validationDisable); // `parseHashParam()` (private)
58164 // Checks hash parameters for severity overrides
58166 // `param` - a url hash parameter (`validationError`, `validationWarning`, or `validationDisable`)
58168 // Array of Objects like { type: RegExp, subtype: RegExp }
58172 function parseHashParam(param) {
58174 var rules = (param || '').split(',');
58175 rules.forEach(function (rule) {
58176 rule = rule.trim();
58177 var parts = rule.split('/', 2); // "type/subtype"
58179 var type = parts[0];
58180 var subtype = parts[1] || '*';
58181 if (!type || !subtype) return;
58183 type: makeRegExp(type),
58184 subtype: makeRegExp(subtype)
58189 function makeRegExp(str) {
58190 var escaped = str.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // escape all reserved chars except for the '*'
58191 .replace(/\*/g, '.*'); // treat a '*' like '.*'
58193 return new RegExp('^' + escaped + '$');
58196 // Initialize the validator, called once on iD startup
58200 validator.init = function () {
58201 Object.values(Validations).forEach(function (validation) {
58202 if (typeof validation !== 'function') return;
58203 var fn = validation(context);
58207 var disabledRules = corePreferences('validate-disabledRules');
58209 if (disabledRules) {
58210 disabledRules.split(',').forEach(function (k) {
58211 return _disabledRules[k] = true;
58214 }; // `reset()` (private)
58215 // Cancels deferred work and resets all caches
58218 // `resetIgnored` - `true` to clear the list of user-ignored issues
58222 function reset(resetIgnored) {
58223 // cancel deferred work
58224 _deferredRIC.forEach(window.cancelIdleCallback);
58226 _deferredRIC.clear();
58228 _deferredST.forEach(window.clearTimeout);
58230 _deferredST.clear(); // empty queues and resolve any pending promise
58233 _baseCache.queue = [];
58234 _headCache.queue = [];
58235 processQueue(_headCache);
58236 processQueue(_baseCache); // clear caches
58238 if (resetIgnored) _ignoredIssueIDs.clear();
58240 _resolvedIssueIDs.clear();
58242 _baseCache = validationCache('base');
58243 _headCache = validationCache('head');
58244 _completeDiff = {};
58245 _headIsCurrent = false;
58247 // clear caches, called whenever iD resets after a save or switches sources
58248 // (clears out the _ignoredIssueIDs set also)
58252 validator.reset = function () {
58254 }; // `resetIgnoredIssues()`
58255 // clears out the _ignoredIssueIDs Set
58259 validator.resetIgnoredIssues = function () {
58260 _ignoredIssueIDs.clear();
58262 dispatch.call('validated'); // redraw UI
58263 }; // `revalidateUnsquare()`
58264 // Called whenever the user changes the unsquare threshold
58265 // It reruns just the "unsquare_way" validation on all buildings.
58269 validator.revalidateUnsquare = function () {
58270 revalidateUnsquare(_headCache);
58271 revalidateUnsquare(_baseCache);
58272 dispatch.call('validated');
58275 function revalidateUnsquare(cache) {
58276 var checkUnsquareWay = _rules.unsquare_way;
58277 if (!cache.graph || typeof checkUnsquareWay !== 'function') return; // uncache existing
58279 cache.uncacheIssuesOfType('unsquare_way');
58280 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), cache.graph) // everywhere
58281 .filter(function (entity) {
58282 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
58283 }); // rerun for all buildings
58285 buildings.forEach(function (entity) {
58286 var detected = checkUnsquareWay(entity, cache.graph);
58287 if (!detected.length) return;
58288 cache.cacheIssues(detected);
58291 // Gets all issues that match the given options
58292 // This is called by many other places
58295 // `options` Object like:
58297 // what: 'all', // 'all' or 'edited'
58298 // where: 'all', // 'all' or 'visible'
58299 // includeIgnored: false, // true, false, or 'only'
58300 // includeDisabledRules: false // true, false, or 'only'
58304 // An Array containing the issues
58308 validator.getIssues = function (options) {
58309 var opts = Object.assign({
58312 includeIgnored: false,
58313 includeDisabledRules: false
58315 var view = context.map().extent();
58316 var seen = new Set();
58317 var results = []; // collect head issues - caused by user edits
58319 if (_headCache.graph && _headCache.graph !== _baseCache.graph) {
58320 Object.values(_headCache.issuesByIssueID).forEach(function (issue) {
58321 if (!filter(issue)) return;
58322 seen.add(issue.id);
58323 results.push(issue);
58325 } // collect base issues - not caused by user edits
58328 if (opts.what === 'all') {
58329 Object.values(_baseCache.issuesByIssueID).forEach(function (issue) {
58330 if (!filter(issue)) return;
58331 seen.add(issue.id);
58332 results.push(issue);
58336 return results; // Filter the issue set to include only what the calling code wants to see.
58337 // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`,
58338 // because that is the graph that the calling code will be using.
58340 function filter(issue) {
58341 if (!issue) return false;
58342 if (seen.has(issue.id)) return false;
58343 if (_resolvedIssueIDs.has(issue.id)) return false;
58344 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
58345 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
58346 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs.has(issue.id)) return false;
58347 if (!opts.includeIgnored && _ignoredIssueIDs.has(issue.id)) return false; // This issue may involve an entity that doesn't exist in context.graph()
58348 // This can happen because validation is async and rendering the issue lists is async.
58350 if ((issue.entityIds || []).some(function (id) {
58351 return !context.hasEntity(id);
58354 if (opts.where === 'visible') {
58355 var extent = issue.extent(context.graph());
58356 if (!view.intersects(extent)) return false;
58361 }; // `getResolvedIssues()`
58362 // Gets the issues that have been fixed by the user.
58364 // Resolved issues are tracked in the `_resolvedIssueIDs` Set,
58365 // and they should all be issues that exist in the _baseCache.
58368 // An Array containing the issues
58372 validator.getResolvedIssues = function () {
58373 return Array.from(_resolvedIssueIDs).map(function (issueID) {
58374 return _baseCache.issuesByIssueID[issueID];
58375 }).filter(Boolean);
58376 }; // `focusIssue()`
58377 // Adjusts the map to focus on the given issue.
58378 // (requires the issue to have a reasonable extent defined)
58381 // `issue` - the issue to focus on
58385 validator.focusIssue = function (issue) {
58386 // Note that we use `context.graph()`/`context.hasEntity()` here, not `cache.graph`,
58387 // because that is the graph that the calling code will be using.
58388 var graph = context.graph();
58390 var focusCenter; // Try to focus the map at the center of the issue..
58392 var issueExtent = issue.extent(graph);
58395 focusCenter = issueExtent.center();
58396 } // Try to select the first entity in the issue..
58399 if (issue.entityIds && issue.entityIds.length) {
58400 selectID = issue.entityIds[0]; // If a relation, focus on one of its members instead.
58401 // Otherwise we might be focusing on a part of map where the relation is not visible.
58403 if (selectID && selectID.charAt(0) === 'r') {
58405 var ids = utilEntityAndDeepMemberIDs([selectID], graph);
58406 var nodeID = ids.find(function (id) {
58407 return id.charAt(0) === 'n' && graph.hasEntity(id);
58411 // relation has no downloaded nodes to focus on
58412 var wayID = ids.find(function (id) {
58413 return id.charAt(0) === 'w' && graph.hasEntity(id);
58417 nodeID = graph.entity(wayID).first(); // focus on the first node of this way
58422 focusCenter = graph.entity(nodeID).loc;
58429 var setZoom = Math.max(context.map().zoom(), 19);
58430 context.map().unobscuredCenterZoomEase(focusCenter, setZoom);
58434 // Enter select mode
58435 window.setTimeout(function () {
58436 context.enter(modeSelect(context, [selectID]));
58437 dispatch.call('focusedIssue', _this, issue);
58438 }, 250); // after ease
58440 }; // `getIssuesBySeverity()`
58441 // Gets the issues then groups them by error/warning
58442 // (This just calls getIssues, then puts issues in groups)
58445 // `options` - (see `getIssues`)
58447 // Object result like:
58449 // error: Array of errors,
58450 // warning: Array of warnings
58455 validator.getIssuesBySeverity = function (options) {
58456 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
58457 groups.error = groups.error || [];
58458 groups.warning = groups.warning || [];
58460 }; // `getEntityIssues()`
58461 // Gets the issues that the given entity IDs have in common, matching the given options
58462 // (This just calls getIssues, then filters for the given entity IDs)
58463 // The issues are sorted for relevance
58466 // `entityIDs` - Array or Set of entityIDs to get issues for
58467 // `options` - (see `getIssues`)
58469 // An Array containing the issues
58473 validator.getSharedEntityIssues = function (entityIDs, options) {
58474 var orderedIssueTypes = [// Show some issue types in a particular order:
58475 'missing_tag', 'missing_role', // - missing data first
58476 'outdated_tags', 'mismatched_geometry', // - identity issues
58477 'crossing_ways', 'almost_junction', // - geometry issues where fixing them might solve connectivity issues
58478 'disconnected_way', 'impossible_oneway' // - finally connectivity issues
58480 var allIssues = validator.getIssues(options);
58481 var forEntityIDs = new Set(entityIDs);
58482 return allIssues.filter(function (issue) {
58483 return (issue.entityIds || []).some(function (entityID) {
58484 return forEntityIDs.has(entityID);
58486 }).sort(function (issue1, issue2) {
58487 if (issue1.type === issue2.type) {
58488 // issues of the same type, sort deterministically
58489 return issue1.id < issue2.id ? -1 : 1;
58492 var index1 = orderedIssueTypes.indexOf(issue1.type);
58493 var index2 = orderedIssueTypes.indexOf(issue2.type);
58495 if (index1 !== -1 && index2 !== -1) {
58496 // both issue types have explicit sort orders
58497 return index1 - index2;
58498 } else if (index1 === -1 && index2 === -1) {
58499 // neither issue type has an explicit sort order, sort by type
58500 return issue1.type < issue2.type ? -1 : 1;
58502 // order explicit types before everything else
58503 return index1 !== -1 ? -1 : 1;
58506 }; // `getEntityIssues()`
58507 // Get an array of detected issues for the given entityID.
58508 // (This just calls getSharedEntityIssues for a single entity)
58511 // `entityID` - the entity ID to get the issues for
58512 // `options` - (see `getIssues`)
58514 // An Array containing the issues
58518 validator.getEntityIssues = function (entityID, options) {
58519 return validator.getSharedEntityIssues([entityID], options);
58520 }; // `getRuleKeys()`
58523 // An Array containing the rule keys
58527 validator.getRuleKeys = function () {
58528 return Object.keys(_rules);
58529 }; // `isRuleEnabled()`
58532 // `key` - the rule to check (e.g. 'crossing_ways')
58538 validator.isRuleEnabled = function (key) {
58539 return !_disabledRules[key];
58540 }; // `toggleRule()`
58541 // Toggles a single validation rule,
58542 // then reruns the validation so that the user sees something happen in the UI
58545 // `key` - the rule to toggle (e.g. 'crossing_ways')
58549 validator.toggleRule = function (key) {
58550 if (_disabledRules[key]) {
58551 delete _disabledRules[key];
58553 _disabledRules[key] = true;
58556 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58557 validator.validate();
58558 }; // `disableRules()`
58559 // Disables given validation rules,
58560 // then reruns the validation so that the user sees something happen in the UI
58563 // `keys` - Array or Set containing rule keys to disable
58567 validator.disableRules = function (keys) {
58568 _disabledRules = {};
58569 keys.forEach(function (k) {
58570 return _disabledRules[k] = true;
58572 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
58573 validator.validate();
58574 }; // `ignoreIssue()`
58575 // Don't show the given issue in lists
58578 // `issueID` - the issueID
58582 validator.ignoreIssue = function (issueID) {
58583 _ignoredIssueIDs.add(issueID);
58585 // Validates anything that has changed in the head graph since the last time it was run.
58586 // (head graph contains user's edits)
58589 // A Promise fulfilled when the validation has completed and then dispatches a `validated` event.
58590 // This may take time but happen in the background during browser idle time.
58594 validator.validate = function () {
58595 // Make sure the caches have graphs assigned to them.
58596 // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then)
58597 var baseGraph = context.history().base();
58598 if (!_headCache.graph) _headCache.graph = baseGraph;
58599 if (!_baseCache.graph) _baseCache.graph = baseGraph;
58600 var prevGraph = _headCache.graph;
58601 var currGraph = context.graph();
58603 if (currGraph === prevGraph) {
58604 // _headCache.graph is current - we are caught up
58605 _headIsCurrent = true;
58606 dispatch.call('validated');
58607 return Promise.resolve();
58610 if (_headPromise) {
58611 // Validation already in process, but we aren't caught up to current
58612 _headIsCurrent = false; // We will need to catch up after the validation promise fulfills
58614 return _headPromise;
58615 } // If we get here, its time to start validating stuff.
58618 _headCache.graph = currGraph; // take snapshot
58620 _completeDiff = context.history().difference().complete();
58621 var incrementalDiff = coreDifference(prevGraph, currGraph);
58622 var entityIDs = Object.keys(incrementalDiff.complete()); // if (!entityIDs.size) {
58624 if (!entityIDs.length) {
58625 dispatch.call('validated');
58626 return Promise.resolve();
58629 _headPromise = validateEntitiesAsync(entityIDs, _headCache).then(function () {
58630 return updateResolvedIssues(entityIDs);
58631 }).then(function () {
58632 return dispatch.call('validated');
58633 })["catch"](function () {
58635 }).then(function () {
58636 _headPromise = null;
58638 if (!_headIsCurrent) {
58639 validator.validate(); // run it again to catch up to current graph
58642 return _headPromise;
58643 }; // register event handlers:
58644 // WHEN TO RUN VALIDATION:
58645 // When history changes:
58648 context.history().on('restore.validator', validator.validate) // on restore saved history
58649 .on('undone.validator', validator.validate) // on undo
58650 .on('redone.validator', validator.validate) // on redo
58651 .on('reset.validator', function () {
58652 // on history reset - happens after save, or enter/exit walkthrough
58653 reset(false); // cached issues aren't valid any longer if the history has been reset
58655 validator.validate();
58656 }); // but not on 'change' (e.g. while drawing)
58657 // When user changes editing modes (to catch recent changes e.g. drawing)
58659 context.on('exit.validator', validator.validate); // When merging fetched data, validate base graph:
58661 context.history().on('merge.validator', function (entities) {
58662 if (!entities) return; // Make sure the caches have graphs assigned to them.
58663 // (we don't do this in `reset` because context is still resetting things and `history.base()` is unstable then)
58665 var baseGraph = context.history().base();
58666 if (!_headCache.graph) _headCache.graph = baseGraph;
58667 if (!_baseCache.graph) _baseCache.graph = baseGraph;
58668 var entityIDs = entities.map(function (entity) {
58670 }); // entityIDs = entityIDsToValidate(entityIDs, baseGraph); // expand set
58672 validateEntitiesAsync(entityIDs, _baseCache);
58673 }); // `validateEntity()` (private)
58674 // Runs all validation rules on a single entity.
58675 // Some things to note:
58676 // - Graph is passed in from whenever the validation was started. Validators shouldn't use
58677 // `context.graph()` because this all happens async, and the graph might have changed
58678 // (for example, nodes getting deleted before the validation can run)
58679 // - Validator functions may still be waiting on something and return a "provisional" result.
58680 // In this situation, we will schedule to revalidate the entity sometime later.
58683 // `entity` - The entity
58684 // `graph` - graph containing the entity
58687 // Object result like:
58689 // issues: Array of detected issues
58690 // provisional: `true` if provisional result, `false` if final result
58694 function validateEntity(entity, graph) {
58699 Object.keys(_rules).forEach(runValidation); // run all rules
58701 return result; // runs validation and appends resulting issues
58703 function runValidation(key) {
58704 var fn = _rules[key];
58706 if (typeof fn !== 'function') {
58707 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
58712 var detected = fn(entity, graph);
58714 if (detected.provisional) {
58715 // this validation should be run again later
58716 result.provisional = true;
58719 detected = detected.filter(applySeverityOverrides);
58720 result.issues = result.issues.concat(detected); // If there are any override rules that match the issue type/subtype,
58721 // adjust severity (or disable it) and keep/discard as quickly as possible.
58723 function applySeverityOverrides(issue) {
58724 var type = issue.type;
58725 var subtype = issue.subtype || '';
58728 for (i = 0; i < _errorOverrides.length; i++) {
58729 if (_errorOverrides[i].type.test(type) && _errorOverrides[i].subtype.test(subtype)) {
58730 issue.severity = 'error';
58735 for (i = 0; i < _warningOverrides.length; i++) {
58736 if (_warningOverrides[i].type.test(type) && _warningOverrides[i].subtype.test(subtype)) {
58737 issue.severity = 'warning';
58742 for (i = 0; i < _disableOverrides.length; i++) {
58743 if (_disableOverrides[i].type.test(type) && _disableOverrides[i].subtype.test(subtype)) {
58751 } // `updateResolvedIssues()` (private)
58752 // Determine if any issues were resolved for the given entities.
58753 // This is called by `validate()` after validation of the head graph
58755 // Give the user credit for fixing an issue if:
58756 // - the issue is in the base cache
58757 // - the issue is not in the head cache
58758 // - the user did something to one of the entities involved in the issue
58761 // `entityIDs` - Array containing entity IDs.
58765 function updateResolvedIssues(entityIDs) {
58766 entityIDs.forEach(function (entityID) {
58767 var baseIssues = _baseCache.issuesByEntityID[entityID];
58768 if (!baseIssues) return;
58769 baseIssues.forEach(function (issueID) {
58770 // Check if the user did something to one of the entities involved in this issue.
58771 // (This issue could involve multiple entities, e.g. disconnected routable features)
58772 var issue = _baseCache.issuesByIssueID[issueID];
58773 var userModified = (issue.entityIds || []).some(function (id) {
58774 return _completeDiff.hasOwnProperty(id);
58777 if (userModified && !_headCache.issuesByIssueID[issueID]) {
58778 // issue seems fixed
58779 _resolvedIssueIDs.add(issueID);
58781 // issue still not resolved
58782 _resolvedIssueIDs["delete"](issueID); // (did undo, or possibly fixed and then re-caused the issue)
58787 } // `validateEntitiesAsync()` (private)
58788 // Schedule validation for many entities.
58791 // `entityIDs` - Array containing entity IDs.
58792 // `graph` - the graph to validate that contains those entities
58793 // `cache` - the cache to store results in (_headCache or _baseCache)
58796 // A Promise fulfilled when the validation has completed.
58797 // This may take time but happen in the background during browser idle time.
58801 function validateEntitiesAsync(entityIDs, cache) {
58802 // Enqueue the work
58803 var jobs = entityIDs.map(function (entityID) {
58804 if (cache.queuedEntityIDs.has(entityID)) return null; // queued already
58806 cache.queuedEntityIDs.add(entityID);
58807 return function () {
58808 // Clear caches for existing issues related to this entity
58809 cache.uncacheEntityID(entityID);
58810 cache.queuedEntityIDs["delete"](entityID);
58811 var graph = cache.graph;
58812 if (!graph) return; // was reset?
58814 var entity = graph.hasEntity(entityID); // Sanity check: don't validate deleted entities
58816 if (!entity) return; // In the head cache, only validate features that the user is responsible for - #8632
58817 // For example, a user can undo some work and an issue will still present in the
58818 // head graph, but we don't want to credit the user for causing that issue.
58820 if (cache.which === 'head' && !_completeDiff.hasOwnProperty(entityID)) return; // detect new issues and update caches
58822 var result = validateEntity(entity, graph);
58824 if (result.provisional) {
58825 // provisional result
58826 cache.provisionalEntityIDs.add(entityID); // we'll need to revalidate this entity again later
58829 cache.cacheIssues(result.issues); // update cache
58831 }).filter(Boolean); // Perform the work in chunks.
58832 // Because this will happen during idle callbacks, we want to choose a chunk size
58833 // that won't make the browser stutter too badly.
58835 cache.queue = cache.queue.concat(utilArrayChunk(jobs, 100)); // Perform the work
58837 if (cache.queuePromise) return cache.queuePromise;
58838 cache.queuePromise = processQueue(cache).then(function () {
58839 return revalidateProvisionalEntities(cache);
58840 })["catch"](function () {
58842 })["finally"](function () {
58843 return cache.queuePromise = null;
58845 return cache.queuePromise;
58846 } // `revalidateProvisionalEntities()` (private)
58847 // Sometimes a validator will return a "provisional" result.
58848 // In this situation, we'll need to revalidate the entity later.
58849 // This function waits a delay, then places them back into the validation queue.
58852 // `cache` - The cache (_headCache or _baseCache)
58856 function revalidateProvisionalEntities(cache) {
58857 if (!cache.provisionalEntityIDs.size) return; // nothing to do
58859 var handle = window.setTimeout(function () {
58860 _deferredST["delete"](handle);
58862 if (!cache.provisionalEntityIDs.size) return; // nothing to do
58864 validateEntitiesAsync(Array.from(cache.provisionalEntityIDs), cache);
58867 _deferredST.add(handle);
58868 } // `processQueue(queue)` (private)
58869 // Process the next chunk of deferred validation work
58872 // `cache` - The cache (_headCache or _baseCache)
58875 // A Promise fulfilled when the validation has completed.
58876 // This may take time but happen in the background during browser idle time.
58880 function processQueue(cache) {
58881 // console.log(`${cache.which} queue length ${cache.queue.length}`);
58882 if (!cache.queue.length) return Promise.resolve(); // we're done
58884 var chunk = cache.queue.pop();
58885 return new Promise(function (resolvePromise) {
58886 var handle = window.requestIdleCallback(function () {
58887 _deferredRIC["delete"](handle); // const t0 = performance.now();
58890 chunk.forEach(function (job) {
58892 }); // const t1 = performance.now();
58893 // console.log('chunk processed in ' + (t1 - t0) + ' ms');
58898 _deferredRIC.add(handle);
58899 }).then(function () {
58900 // dispatch an event sometimes to redraw various UI things
58901 if (cache.queue.length % 25 === 0) dispatch.call('validated');
58902 }).then(function () {
58903 return processQueue(cache);
58908 } // `validationCache()` (private)
58909 // Creates a cache to store validation state
58910 // We create 2 of these:
58911 // `_baseCache` for validation on the base graph (unedited)
58912 // `_headCache` for validation on the head graph (user edits applied)
58915 // `which` - just a String 'base' or 'head' to keep track of it
58918 function validationCache(which) {
58923 queuePromise: null,
58924 queuedEntityIDs: new Set(),
58925 provisionalEntityIDs: new Set(),
58926 issuesByIssueID: {},
58927 // issue.id -> issue
58928 issuesByEntityID: {} // entity.id -> Set(issue.id)
58932 cache.cacheIssues = function (issues) {
58933 issues.forEach(function (issue) {
58934 var entityIDs = issue.entityIds || [];
58935 entityIDs.forEach(function (entityID) {
58936 if (!cache.issuesByEntityID[entityID]) {
58937 cache.issuesByEntityID[entityID] = new Set();
58940 cache.issuesByEntityID[entityID].add(issue.id);
58942 cache.issuesByIssueID[issue.id] = issue;
58946 cache.uncacheIssue = function (issue) {
58947 // When multiple entities are involved (e.g. crossing_ways),
58948 // remove this issue from the other entity caches too..
58949 var entityIDs = issue.entityIds || [];
58950 entityIDs.forEach(function (entityID) {
58951 if (cache.issuesByEntityID[entityID]) {
58952 cache.issuesByEntityID[entityID]["delete"](issue.id);
58955 delete cache.issuesByIssueID[issue.id];
58958 cache.uncacheIssues = function (issues) {
58959 issues.forEach(cache.uncacheIssue);
58962 cache.uncacheIssuesOfType = function (type) {
58963 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
58964 return issue.type === type;
58966 cache.uncacheIssues(issuesOfType);
58967 }; // Remove a single entity and all its related issues from the caches
58970 cache.uncacheEntityID = function (entityID) {
58971 var issueIDs = cache.issuesByEntityID[entityID];
58974 issueIDs.forEach(function (issueID) {
58975 var issue = cache.issuesByIssueID[issueID];
58978 cache.uncacheIssue(issue);
58980 delete cache.issuesByIssueID[issueID];
58985 delete cache.issuesByEntityID[entityID];
58986 cache.provisionalEntityIDs["delete"](entityID);
58992 function coreUploader(context) {
58993 var dispatch = dispatch$8( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
58994 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
58995 'saveEnded', // dispatched after the result event has been dispatched
58996 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
58997 'progressChanged', // Each save results in one of these outcomes:
58998 'resultNoChanges', // upload wasn't attempted since there were no edits
58999 'resultErrors', // upload failed due to errors
59000 'resultConflicts', // upload failed due to data conflicts
59001 'resultSuccess' // upload completed without errors
59003 var _isSaving = false;
59004 var _conflicts = [];
59009 var _discardTags = {};
59010 _mainFileFetcher.get('discarded').then(function (d) {
59012 })["catch"](function () {
59015 var uploader = utilRebind({}, dispatch, 'on');
59017 uploader.isSaving = function () {
59021 uploader.save = function (changeset, tryAgain, checkConflicts) {
59022 // Guard against accidentally entering save code twice - #4641
59023 if (_isSaving && !tryAgain) {
59027 var osm = context.connection();
59028 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
59029 // This can happen if they were logged in from before, but the tokens are no longer valid.
59031 if (!osm.authenticated()) {
59032 osm.authenticate(function (err) {
59034 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
59042 dispatch.call('saveStarted', this);
59045 var history = context.history();
59047 _errors = []; // Store original changes, in case user wants to download them as an .osc file
59049 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
59050 // Any conflict resolutions will be done as `history.replace`
59051 // Remember to pop this later if needed
59054 history.perform(actionNoop());
59055 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
59058 if (!checkConflicts) {
59059 upload(changeset); // Do the full (slow) conflict check..
59061 performFullConflictCheck(changeset);
59065 function performFullConflictCheck(changeset) {
59066 var osm = context.connection();
59068 var history = context.history();
59069 var localGraph = context.graph();
59070 var remoteGraph = coreGraph(history.base(), true);
59071 var summary = history.difference().summary();
59074 for (var i = 0; i < summary.length; i++) {
59075 var item = summary[i];
59077 if (item.changeType === 'modified') {
59078 _toCheck.push(item.entity.id);
59082 var _toLoad = withChildNodes(_toCheck, localGraph);
59085 var _toLoadCount = 0;
59086 var _toLoadTotal = _toLoad.length;
59088 if (_toCheck.length) {
59089 dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59091 _toLoad.forEach(function (id) {
59092 _loaded[id] = false;
59095 osm.loadMultiple(_toLoad, loaded);
59102 function withChildNodes(ids, graph) {
59103 var s = new Set(ids);
59104 ids.forEach(function (id) {
59105 var entity = graph.entity(id);
59106 if (entity.type !== 'way') return;
59107 graph.childNodes(entity).forEach(function (child) {
59108 if (child.version !== undefined) {
59113 return Array.from(s);
59114 } // Reload modified entities into an alternate graph and check for conflicts..
59117 function loaded(err, result) {
59118 if (_errors.length) return;
59122 msg: err.message || err.responseText,
59123 details: [_t('save.status_code', {
59128 didResultInErrors();
59131 result.data.forEach(function (entity) {
59132 remoteGraph.replace(entity);
59133 _loaded[entity.id] = true;
59134 _toLoad = _toLoad.filter(function (val) {
59135 return val !== entity.id;
59137 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
59138 // need to also load children that aren't already being checked..
59142 if (entity.type === 'way') {
59143 for (i = 0; i < entity.nodes.length; i++) {
59144 id = entity.nodes[i];
59146 if (_loaded[id] === undefined) {
59147 _loaded[id] = false;
59151 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59152 for (i = 0; i < entity.members.length; i++) {
59153 id = entity.members[i].id;
59155 if (_loaded[id] === undefined) {
59156 _loaded[id] = false;
59162 _toLoadCount += result.data.length;
59163 _toLoadTotal += loadMore.length;
59164 dispatch.call('progressChanged', this, _toLoadCount, _toLoadTotal);
59166 if (loadMore.length) {
59167 _toLoad.push.apply(_toLoad, loadMore);
59169 osm.loadMultiple(loadMore, loaded);
59172 if (!_toLoad.length) {
59179 function detectConflicts() {
59180 function choice(id, text, _action) {
59184 action: function action() {
59185 history.replace(_action);
59190 function formatUser(d) {
59191 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
59194 function entityName(entity) {
59195 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
59198 function sameVersions(local, remote) {
59199 if (local.version !== remote.version) return false;
59201 if (local.type === 'way') {
59202 var children = utilArrayUnion(local.nodes, remote.nodes);
59204 for (var i = 0; i < children.length; i++) {
59205 var a = localGraph.hasEntity(children[i]);
59206 var b = remoteGraph.hasEntity(children[i]);
59207 if (a && b && a.version !== b.version) return false;
59214 _toCheck.forEach(function (id) {
59215 var local = localGraph.entity(id);
59216 var remote = remoteGraph.entity(id);
59217 if (sameVersions(local, remote)) return;
59218 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
59219 history.replace(merge);
59220 var mergeConflicts = merge.conflicts();
59221 if (!mergeConflicts.length) return; // merged safely
59223 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
59224 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
59225 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
59226 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
59230 name: entityName(local),
59231 details: mergeConflicts,
59233 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
59239 function upload(changeset) {
59240 var osm = context.connection();
59244 msg: 'No OSM Service'
59248 if (_conflicts.length) {
59249 didResultInConflicts(changeset);
59250 } else if (_errors.length) {
59251 didResultInErrors();
59253 var history = context.history();
59254 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
59256 if (changes.modified.length || changes.created.length || changes.deleted.length) {
59257 dispatch.call('willAttemptUpload', this);
59258 osm.putChangeset(changeset, changes, uploadCallback);
59260 // changes were insignificant or reverted by user
59261 didResultInNoChanges();
59266 function uploadCallback(err, changeset) {
59268 if (err.status === 409) {
59270 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
59273 msg: err.message || err.responseText,
59274 details: [_t('save.status_code', {
59279 didResultInErrors();
59282 didResultInSuccess(changeset);
59286 function didResultInNoChanges() {
59287 dispatch.call('resultNoChanges', this);
59289 context.flush(); // reset iD
59292 function didResultInErrors() {
59293 context.history().pop();
59294 dispatch.call('resultErrors', this, _errors);
59298 function didResultInConflicts(changeset) {
59299 _conflicts.sort(function (a, b) {
59300 return b.id.localeCompare(a.id);
59303 dispatch.call('resultConflicts', this, changeset, _conflicts, _origChanges);
59307 function didResultInSuccess(changeset) {
59308 // delete the edit stack cached to local storage
59309 context.history().clearSaved();
59310 dispatch.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
59312 window.setTimeout(function () {
59314 context.flush(); // reset iD
59318 function endSave() {
59320 dispatch.call('saveEnded', this);
59323 uploader.cancelConflictResolution = function () {
59324 context.history().pop();
59327 uploader.processResolvedConflicts = function (changeset) {
59328 var history = context.history();
59330 for (var i = 0; i < _conflicts.length; i++) {
59331 if (_conflicts[i].chosen === 1) {
59332 // user chose "use theirs"
59333 var entity = context.hasEntity(_conflicts[i].id);
59335 if (entity && entity.type === 'way') {
59336 var children = utilArrayUniq(entity.nodes);
59338 for (var j = 0; j < children.length; j++) {
59339 history.replace(actionRevert(children[j]));
59343 history.replace(actionRevert(_conflicts[i].id));
59347 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
59350 uploader.reset = function () {};
59355 var abs = Math.abs;
59356 var exp = Math.exp;
59359 var FORCED = fails(function () {
59360 // eslint-disable-next-line es/no-math-sinh -- required for testing
59361 return Math.sinh(-2e-17) != -2e-17;
59364 // `Math.sinh` method
59365 // https://tc39.es/ecma262/#sec-math.sinh
59366 // V8 near Chromium 38 has a problem with very small numbers
59367 _export({ target: 'Math', stat: true, forced: FORCED }, {
59368 sinh: function sinh(x) {
59369 return abs(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
59373 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
59375 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
59376 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
59379 function localeDateString(s) {
59380 if (!s) return null;
59386 var d = new Date(s);
59387 if (isNaN(d.getTime())) return null;
59388 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
59391 function vintageRange(vintage) {
59394 if (vintage.start || vintage.end) {
59395 s = vintage.start || '?';
59397 if (vintage.start !== vintage.end) {
59398 s += ' - ' + (vintage.end || '?');
59405 function rendererBackgroundSource(data) {
59406 var source = Object.assign({}, data); // shallow copy
59408 var _offset = [0, 0];
59409 var _name = source.name;
59410 var _description = source.description;
59412 var _best = !!source.best;
59414 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
59416 source.tileSize = data.tileSize || 256;
59417 source.zoomExtent = data.zoomExtent || [0, 22];
59418 source.overzoom = data.overzoom !== false;
59420 source.offset = function (val) {
59421 if (!arguments.length) return _offset;
59426 source.nudge = function (val, zoomlevel) {
59427 _offset[0] += val[0] / Math.pow(2, zoomlevel);
59428 _offset[1] += val[1] / Math.pow(2, zoomlevel);
59432 source.name = function () {
59433 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59434 return _t('imagery.' + id_safe + '.name', {
59439 source.label = function () {
59440 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59441 return _t.html('imagery.' + id_safe + '.name', {
59446 source.description = function () {
59447 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
59448 return _t.html('imagery.' + id_safe + '.description', {
59449 "default": _description
59453 source.best = function () {
59457 source.area = function () {
59458 if (!data.polygon) return Number.MAX_VALUE; // worldwide
59460 var area = d3_geoArea({
59461 type: 'MultiPolygon',
59462 coordinates: [data.polygon]
59464 return isNaN(area) ? 0 : area;
59467 source.imageryUsed = function () {
59468 return _name || source.id;
59471 source.template = function (val) {
59472 if (!arguments.length) return _template;
59474 if (source.id === 'custom' || source.id === 'Bing') {
59481 source.url = function (coord) {
59482 var result = _template;
59483 if (result === '') return result; // source 'none'
59484 // Guess a type based on the tokens present in the template
59485 // (This is for 'custom' source, where we don't know)
59487 if (!source.type) {
59488 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
59489 source.type = 'wms';
59490 source.projection = 'EPSG:3857'; // guess
59491 } else if (/\{(x|y)\}/.test(_template)) {
59492 source.type = 'tms';
59493 } else if (/\{u\}/.test(_template)) {
59494 source.type = 'bing';
59498 if (source.type === 'wms') {
59499 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
59500 //polyfill for IE11, PhantomJS
59501 var sinh = Math.sinh || function (x) {
59502 var y = Math.exp(x);
59503 return (y - 1 / y) / 2;
59506 var zoomSize = Math.pow(2, z);
59507 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
59508 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
59510 switch (source.projection) {
59513 x: lon * 180 / Math.PI,
59514 y: lat * 180 / Math.PI
59518 // EPSG:3857 and synonyms
59519 var mercCoords = mercatorRaw(lon, lat);
59521 x: 20037508.34 / Math.PI * mercCoords[0],
59522 y: 20037508.34 / Math.PI * mercCoords[1]
59527 var tileSize = source.tileSize;
59528 var projection = source.projection;
59529 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
59530 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
59531 result = result.replace(/\{(\w+)\}/g, function (token, key) {
59541 return projection.replace(/^EPSG:/, '');
59544 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
59545 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
59546 /VERSION=1.3|CRS={proj}/.test(source.template().toUpperCase())) {
59547 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
59549 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
59568 } else if (source.type === 'tms') {
59569 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
59570 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
59571 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
59572 } else if (source.type === 'bing') {
59573 result = result.replace('{u}', function () {
59576 for (var zoom = coord[2]; zoom > 0; zoom--) {
59578 var mask = 1 << zoom - 1;
59579 if ((coord[0] & mask) !== 0) b++;
59580 if ((coord[1] & mask) !== 0) b += 2;
59586 } // these apply to any type..
59589 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
59590 var subdomains = r.split(',');
59591 return subdomains[(coord[0] + coord[1]) % subdomains.length];
59596 source.validZoom = function (z) {
59597 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
59600 source.isLocatorOverlay = function () {
59601 return source.id === 'mapbox_locator_overlay';
59603 /* hides a source from the list, but leaves it available for use */
59606 source.isHidden = function () {
59607 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
59610 source.copyrightNotices = function () {};
59612 source.getMetadata = function (center, tileCoord, callback) {
59614 start: localeDateString(source.startDate),
59615 end: localeDateString(source.endDate)
59617 vintage.range = vintageRange(vintage);
59621 callback(null, metadata);
59627 rendererBackgroundSource.Bing = function (data, dispatch) {
59628 // https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata
59629 // https://docs.microsoft.com/en-us/bingmaps/rest-services/directly-accessing-the-bing-maps-tiles
59630 //fallback url template
59631 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&n=z';
59632 var bing = rendererBackgroundSource(data); //var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
59634 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
59637 missing tile image strictness param (n=)
59638 • n=f -> (Fail) returns a 404
59639 • n=z -> (Empty) returns a 200 with 0 bytes (no content)
59640 • n=t -> (Transparent) returns a 200 with a transparent (png) tile
59643 var strictParam = 'n';
59644 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&uriScheme=https&key=' + key;
59647 var providers = [];
59648 d3_json(url).then(function (json) {
59649 var imageryResource = json.resourceSets[0].resources[0]; //retrieve and prepare up to date imagery template
59651 var template = imageryResource.imageUrl; //https://ecn.{subdomain}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=10339
59653 var subDomains = imageryResource.imageUrlSubdomains; //["t0, t1, t2, t3"]
59655 var subDomainNumbers = subDomains.map(function (subDomain) {
59656 return subDomain.substring(1);
59658 template = template.replace('{subdomain}', "t{switch:".concat(subDomainNumbers, "}")).replace('{quadkey}', '{u}');
59660 if (!new URLSearchParams(template).has(strictParam)) {
59661 template += "&".concat(strictParam, "=z");
59664 bing.template(template);
59665 providers = imageryResource.imageryProviders.map(function (provider) {
59667 attribution: provider.attribution,
59668 areas: provider.coverageAreas.map(function (area) {
59670 zoom: [area.zoomMin, area.zoomMax],
59671 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
59676 dispatch.call('change');
59677 })["catch"](function () {
59681 bing.copyrightNotices = function (zoom, extent) {
59682 zoom = Math.min(zoom, 21);
59683 return providers.filter(function (provider) {
59684 return provider.areas.some(function (area) {
59685 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
59687 }).map(function (provider) {
59688 return provider.attribution;
59692 bing.getMetadata = function (center, tileCoord, callback) {
59693 var tileID = tileCoord.slice(0, 3).join('/');
59694 var zoom = Math.min(tileCoord[2], 21);
59695 var centerPoint = center[1] + ',' + center[0]; // lat,lng
59697 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
59698 if (inflight[tileID]) return;
59700 if (!cache[tileID]) {
59701 cache[tileID] = {};
59704 if (cache[tileID] && cache[tileID].metadata) {
59705 return callback(null, cache[tileID].metadata);
59708 inflight[tileID] = true;
59709 d3_json(url).then(function (result) {
59710 delete inflight[tileID];
59713 throw new Error('Unknown Error');
59717 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
59718 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
59720 vintage.range = vintageRange(vintage);
59724 cache[tileID].metadata = metadata;
59725 if (callback) callback(null, metadata);
59726 })["catch"](function (err) {
59727 delete inflight[tileID];
59728 if (callback) callback(err.message);
59732 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
59736 rendererBackgroundSource.Esri = function (data) {
59737 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
59738 if (data.template.match(/blankTile/) === null) {
59739 data.template = data.template + '?blankTile=false';
59742 var esri = rendererBackgroundSource(data);
59746 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
59747 // https://developers.arcgis.com/documentation/tiled-elevation-service/
59750 esri.fetchTilemap = function (center) {
59751 // skip if we have already fetched a tilemap within 5km
59752 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
59753 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
59755 var z = 20; // first generate a random url using the template
59757 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
59759 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
59760 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
59762 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
59764 d3_json(tilemapUrl).then(function (tilemap) {
59766 throw new Error('Unknown Error');
59769 var hasTiles = true;
59771 for (var i = 0; i < tilemap.data.length; i++) {
59772 // 0 means an individual tile in the grid doesn't exist
59773 if (!tilemap.data[i]) {
59777 } // if any tiles are missing at level 20 we restrict maxZoom to 19
59780 esri.zoomExtent[1] = hasTiles ? 22 : 19;
59781 })["catch"](function () {
59786 esri.getMetadata = function (center, tileCoord, callback) {
59787 var tileID = tileCoord.slice(0, 3).join('/');
59788 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
59789 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
59791 var unknown = _t('info_panels.background.unknown');
59795 if (inflight[tileID]) return;
59798 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
59815 metadataLayer = 99;
59818 var url; // build up query using the layer appropriate to the current zoom
59820 if (esri.id === 'EsriWorldImagery') {
59821 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
59822 } else if (esri.id === 'EsriWorldImageryClarity') {
59823 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
59826 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
59828 if (!cache[tileID]) {
59829 cache[tileID] = {};
59832 if (cache[tileID] && cache[tileID].metadata) {
59833 return callback(null, cache[tileID].metadata);
59834 } // accurate metadata is only available >= 13
59837 if (metadataLayer === 99) {
59846 description: unknown,
59847 resolution: unknown,
59850 callback(null, metadata);
59852 inflight[tileID] = true;
59853 d3_json(url).then(function (result) {
59854 delete inflight[tileID];
59857 throw new Error('Unknown Error');
59858 } else if (result.features && result.features.length < 1) {
59859 throw new Error('No Results');
59860 } else if (result.error && result.error.message) {
59861 throw new Error(result.error.message);
59862 } // pass through the discrete capture date from metadata
59865 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
59867 start: captureDate,
59873 source: clean(result.features[0].attributes.NICE_NAME),
59874 description: clean(result.features[0].attributes.NICE_DESC),
59875 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
59876 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
59877 }; // append units - meters
59879 if (isFinite(metadata.resolution)) {
59880 metadata.resolution += ' m';
59883 if (isFinite(metadata.accuracy)) {
59884 metadata.accuracy += ' m';
59887 cache[tileID].metadata = metadata;
59888 if (callback) callback(null, metadata);
59889 })["catch"](function (err) {
59890 delete inflight[tileID];
59891 if (callback) callback(err.message);
59895 function clean(val) {
59896 return String(val).trim() || unknown;
59903 rendererBackgroundSource.None = function () {
59904 var source = rendererBackgroundSource({
59909 source.name = function () {
59910 return _t('background.none');
59913 source.label = function () {
59914 return _t.html('background.none');
59917 source.imageryUsed = function () {
59921 source.area = function () {
59922 return -1; // sources in background pane are sorted by area
59928 rendererBackgroundSource.Custom = function (template) {
59929 var source = rendererBackgroundSource({
59934 source.name = function () {
59935 return _t('background.custom');
59938 source.label = function () {
59939 return _t.html('background.custom');
59942 source.imageryUsed = function () {
59943 // sanitize personal connection tokens - #6801
59944 var cleaned = source.template(); // from query string parameters
59946 if (cleaned.indexOf('?') !== -1) {
59947 var parts = cleaned.split('?', 2);
59948 var qs = utilStringQs(parts[1]);
59949 ['access_token', 'connectId', 'token'].forEach(function (param) {
59951 qs[param] = '{apikey}';
59954 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
59955 } // from wms/wmts api path parameters
59958 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
59959 return 'Custom (' + cleaned + ' )';
59962 source.area = function () {
59963 return -2; // sources in background pane are sorted by area
59969 function rendererTileLayer(context) {
59970 var transformProp = utilPrefixCSSProperty('Transform');
59971 var tiler = utilTiler();
59972 var _tileSize = 256;
59984 function tileSizeAtZoom(d, z) {
59985 var EPSILON = 0.002; // close seams
59987 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
59990 function atZoom(t, distance) {
59991 var power = Math.pow(2, distance);
59992 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
59995 function lookUp(d) {
59996 for (var up = -1; up > -d[2]; up--) {
59997 var tile = atZoom(d, up);
59999 if (_cache[_source.url(tile)] !== false) {
60005 function uniqueBy(a, n) {
60009 for (var i = 0; i < a.length; i++) {
60010 if (seen[a[i][n]] === undefined) {
60012 seen[a[i][n]] = true;
60019 function addSource(d) {
60020 d.push(_source.url(d));
60022 } // Update tiles based on current state of `projection`.
60025 function background(selection) {
60026 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
60030 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
60032 pixelOffset = [0, 0];
60035 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
60036 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
60037 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
60039 } // Derive the tiles onscreen, remove those offscreen and position them.
60040 // Important that this part not depend on `_projection` because it's
60041 // rentered when tiles load/error (see #644).
60044 function render(selection) {
60045 if (!_source) return;
60047 var showDebug = context.getDebug('tile') && !_source.overlay;
60049 if (_source.validZoom(_zoom)) {
60050 tiler.skipNullIsland(!!_source.overlay);
60051 tiler().forEach(function (d) {
60053 if (d[3] === '') return;
60054 if (typeof d[3] !== 'string') return; // Workaround for #2295
60058 if (_cache[d[3]] === false && lookUp(d)) {
60059 requests.push(addSource(lookUp(d)));
60062 requests = uniqueBy(requests, 3).filter(function (r) {
60063 // don't re-request tiles which have failed in the past
60064 return _cache[r[3]] !== false;
60068 function load(d3_event, d) {
60069 _cache[d[3]] = true;
60070 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
60074 function error(d3_event, d) {
60075 _cache[d[3]] = false;
60076 select(this).on('error', null).on('load', null).remove();
60080 function imageTransform(d) {
60081 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60083 var scale = tileSizeAtZoom(d, _zoom);
60084 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
60087 function tileCenter(d) {
60088 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
60090 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
60093 function debugTransform(d) {
60094 var coord = tileCenter(d);
60095 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
60096 } // Pick a representative tile near the center of the viewport
60097 // (This is useful for sampling the imagery vintage)
60100 var dims = tiler.size();
60101 var mapCenter = [dims[0] / 2, dims[1] / 2];
60102 var minDist = Math.max(dims[0], dims[1]);
60104 requests.forEach(function (d) {
60105 var c = tileCenter(d);
60106 var dist = geoVecLength(c, mapCenter);
60108 if (dist < minDist) {
60113 var image = selection.selectAll('img').data(requests, function (d) {
60116 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
60117 var tile = select(this);
60118 window.setTimeout(function () {
60119 if (tile.classed('tile-removing')) {
60124 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
60126 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
60127 return d === nearCenter;
60129 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
60132 debug.exit().remove();
60135 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
60136 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
60137 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
60138 debug = debug.merge(debugEnter);
60139 debug.style(transformProp, debugTransform);
60140 debug.selectAll('.tile-label-debug-coord').html(function (d) {
60141 return d[2] + ' / ' + d[0] + ' / ' + d[1];
60143 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
60144 var span = select(this);
60145 var center = context.projection.invert(tileCenter(d));
60147 _source.getMetadata(center, d, function (err, result) {
60148 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
60154 background.projection = function (val) {
60155 if (!arguments.length) return _projection;
60160 background.dimensions = function (val) {
60161 if (!arguments.length) return tiler.size();
60166 background.source = function (val) {
60167 if (!arguments.length) return _source;
60169 _tileSize = _source.tileSize;
60171 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
60178 var _imageryIndex = null;
60179 function rendererBackground(context) {
60180 var dispatch = dispatch$8('change');
60181 var detected = utilDetect();
60182 var baseLayer = rendererTileLayer(context).projection(context.projection);
60183 var _isValid = true;
60184 var _overlayLayers = [];
60185 var _brightness = 1;
60187 var _saturation = 1;
60188 var _sharpness = 1;
60190 function ensureImageryIndex() {
60191 return _mainFileFetcher.get('imagery').then(function (sources) {
60192 if (_imageryIndex) return _imageryIndex;
60196 }; // use which-polygon to support efficient index and querying for imagery
60198 var features = sources.map(function (source) {
60199 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
60200 // Add an extra array nest to each element in `source.polygon`
60201 // so the rings are not treated as a bunch of holes:
60202 // what we have: [ [[outer],[hole],[hole]] ]
60203 // what we want: [ [[outer]],[[outer]],[[outer]] ]
60205 var rings = source.polygon.map(function (ring) {
60214 type: 'MultiPolygon',
60218 _imageryIndex.features[source.id] = feature;
60220 }).filter(Boolean);
60221 _imageryIndex.query = whichPolygon_1({
60222 type: 'FeatureCollection',
60224 }); // Instantiate `rendererBackgroundSource` objects for each source
60226 _imageryIndex.backgrounds = sources.map(function (source) {
60227 if (source.type === 'bing') {
60228 return rendererBackgroundSource.Bing(source, dispatch);
60229 } else if (/^EsriWorldImagery/.test(source.id)) {
60230 return rendererBackgroundSource.Esri(source);
60232 return rendererBackgroundSource(source);
60236 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
60239 var template = corePreferences('background-custom-template') || '';
60240 var custom = rendererBackgroundSource.Custom(template);
60242 _imageryIndex.backgrounds.unshift(custom);
60244 return _imageryIndex;
60248 function background(selection) {
60249 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
60250 // check its tilemap to see how high the zoom can go
60252 if (context.map().zoom() > 18) {
60253 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
60254 var center = context.map().center();
60255 currSource.fetchTilemap(center);
60257 } // Is the imagery valid here? - #4827
60260 var sources = background.sources(context.map().extent());
60261 var wasValid = _isValid;
60262 _isValid = !!sources.filter(function (d) {
60263 return d === currSource;
60266 if (wasValid !== _isValid) {
60267 // change in valid status
60268 background.updateImagery();
60271 var baseFilter = '';
60273 if (detected.cssfilters) {
60274 if (_brightness !== 1) {
60275 baseFilter += " brightness(".concat(_brightness, ")");
60278 if (_contrast !== 1) {
60279 baseFilter += " contrast(".concat(_contrast, ")");
60282 if (_saturation !== 1) {
60283 baseFilter += " saturate(".concat(_saturation, ")");
60286 if (_sharpness < 1) {
60288 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
60289 baseFilter += " blur(".concat(blur, "px)");
60293 var base = selection.selectAll('.layer-background').data([0]);
60294 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
60296 if (detected.cssfilters) {
60297 base.style('filter', baseFilter || null);
60299 base.style('opacity', _brightness);
60302 var imagery = base.selectAll('.layer-imagery').data([0]);
60303 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
60304 var maskFilter = '';
60305 var mixBlendMode = '';
60307 if (detected.cssfilters && _sharpness > 1) {
60308 // apply unsharp mask
60309 mixBlendMode = 'overlay';
60310 maskFilter = 'saturate(0) blur(3px) invert(1)';
60311 var contrast = _sharpness - 1;
60312 maskFilter += " contrast(".concat(contrast, ")");
60313 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
60314 maskFilter += " brightness(".concat(brightness, ")");
60317 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
60318 mask.exit().remove();
60319 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);
60320 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
60321 return d.source().name();
60323 overlays.exit().remove();
60324 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
60325 return select(nodes[i]).call(layer);
60329 background.updateImagery = function () {
60330 var currSource = baseLayer.source();
60331 if (context.inIntro() || !currSource) return;
60333 var o = _overlayLayers.filter(function (d) {
60334 return !d.source().isLocatorOverlay() && !d.source().isHidden();
60335 }).map(function (d) {
60336 return d.source().id;
60339 var meters = geoOffsetToMeters(currSource.offset());
60340 var EPSILON = 0.01;
60341 var x = +meters[0].toFixed(2);
60342 var y = +meters[1].toFixed(2);
60343 var hash = utilStringQs(window.location.hash);
60344 var id = currSource.id;
60346 if (id === 'custom') {
60347 id = "custom:".concat(currSource.template());
60351 hash.background = id;
60353 delete hash.background;
60359 delete hash.overlays;
60362 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
60363 hash.offset = "".concat(x, ",").concat(y);
60365 delete hash.offset;
60368 if (!window.mocha) {
60369 window.location.replace('#' + utilQsString(hash, true));
60372 var imageryUsed = [];
60373 var photoOverlaysUsed = [];
60374 var currUsed = currSource.imageryUsed();
60376 if (currUsed && _isValid) {
60377 imageryUsed.push(currUsed);
60380 _overlayLayers.filter(function (d) {
60381 return !d.source().isLocatorOverlay() && !d.source().isHidden();
60382 }).forEach(function (d) {
60383 return imageryUsed.push(d.source().imageryUsed());
60386 var dataLayer = context.layers().layer('data');
60388 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
60389 imageryUsed.push(dataLayer.getSrc());
60392 var photoOverlayLayers = {
60393 streetside: 'Bing Streetside',
60394 mapillary: 'Mapillary Images',
60395 'mapillary-map-features': 'Mapillary Map Features',
60396 'mapillary-signs': 'Mapillary Signs',
60397 openstreetcam: 'OpenStreetCam Images'
60400 for (var layerID in photoOverlayLayers) {
60401 var layer = context.layers().layer(layerID);
60403 if (layer && layer.enabled()) {
60404 photoOverlaysUsed.push(layerID);
60405 imageryUsed.push(photoOverlayLayers[layerID]);
60409 context.history().imageryUsed(imageryUsed);
60410 context.history().photoOverlaysUsed(photoOverlaysUsed);
60413 var _checkedBlocklists;
60415 background.sources = function (extent, zoom, includeCurrent) {
60416 if (!_imageryIndex) return []; // called before init()?
60419 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
60420 return visible[d.id] = true;
60422 var currSource = baseLayer.source();
60423 var osm = context.connection();
60424 var blocklists = osm && osm.imageryBlocklists();
60426 if (blocklists && blocklists !== _checkedBlocklists) {
60427 _imageryIndex.backgrounds.forEach(function (source) {
60428 source.isBlocked = blocklists.some(function (blocklist) {
60429 return blocklist.test(source.template());
60433 _checkedBlocklists = blocklists;
60436 return _imageryIndex.backgrounds.filter(function (source) {
60437 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
60439 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
60441 if (!source.polygon) return true; // always include imagery with worldwide coverage
60443 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
60445 return visible[source.id]; // include imagery visible in given extent
60449 background.dimensions = function (val) {
60451 baseLayer.dimensions(val);
60453 _overlayLayers.forEach(function (layer) {
60454 return layer.dimensions(val);
60458 background.baseLayerSource = function (d) {
60459 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
60461 var osm = context.connection();
60462 if (!osm) return background;
60463 var blocklists = osm.imageryBlocklists();
60464 var template = d.template();
60469 for (var i = 0; i < blocklists.length; i++) {
60470 regex = blocklists[i];
60471 fail = regex.test(template);
60474 } // ensure at least one test was run.
60478 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
60479 fail = regex.test(template);
60482 baseLayer.source(!fail ? d : background.findSource('none'));
60483 dispatch.call('change');
60484 background.updateImagery();
60488 background.findSource = function (id) {
60489 if (!id || !_imageryIndex) return null; // called before init()?
60491 return _imageryIndex.backgrounds.find(function (d) {
60492 return d.id && d.id === id;
60496 background.bing = function () {
60497 background.baseLayerSource(background.findSource('Bing'));
60500 background.showsLayer = function (d) {
60501 var currSource = baseLayer.source();
60502 if (!d || !currSource) return false;
60503 return d.id === currSource.id || _overlayLayers.some(function (layer) {
60504 return d.id === layer.source().id;
60508 background.overlayLayerSources = function () {
60509 return _overlayLayers.map(function (layer) {
60510 return layer.source();
60514 background.toggleOverlayLayer = function (d) {
60517 for (var i = 0; i < _overlayLayers.length; i++) {
60518 layer = _overlayLayers[i];
60520 if (layer.source() === d) {
60521 _overlayLayers.splice(i, 1);
60523 dispatch.call('change');
60524 background.updateImagery();
60529 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
60531 _overlayLayers.push(layer);
60533 dispatch.call('change');
60534 background.updateImagery();
60537 background.nudge = function (d, zoom) {
60538 var currSource = baseLayer.source();
60541 currSource.nudge(d, zoom);
60542 dispatch.call('change');
60543 background.updateImagery();
60549 background.offset = function (d) {
60550 var currSource = baseLayer.source();
60552 if (!arguments.length) {
60553 return currSource && currSource.offset() || [0, 0];
60557 currSource.offset(d);
60558 dispatch.call('change');
60559 background.updateImagery();
60565 background.brightness = function (d) {
60566 if (!arguments.length) return _brightness;
60568 if (context.mode()) dispatch.call('change');
60572 background.contrast = function (d) {
60573 if (!arguments.length) return _contrast;
60575 if (context.mode()) dispatch.call('change');
60579 background.saturation = function (d) {
60580 if (!arguments.length) return _saturation;
60582 if (context.mode()) dispatch.call('change');
60586 background.sharpness = function (d) {
60587 if (!arguments.length) return _sharpness;
60589 if (context.mode()) dispatch.call('change');
60595 background.ensureLoaded = function () {
60596 if (_loadPromise) return _loadPromise;
60598 function parseMapParams(qmap) {
60599 if (!qmap) return false;
60600 var params = qmap.split('/').map(Number);
60601 if (params.length < 3 || params.some(isNaN)) return false;
60602 return geoExtent([params[2], params[1]]); // lon,lat
60605 var hash = utilStringQs(window.location.hash);
60606 var requested = hash.background || hash.layer;
60607 var extent = parseMapParams(hash.map);
60608 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
60609 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
60612 if (!requested && extent) {
60613 best = background.sources(extent).find(function (s) {
60616 } // Decide which background layer to display
60619 if (requested && requested.indexOf('custom:') === 0) {
60620 var template = requested.replace(/^custom:/, '');
60621 var custom = background.findSource('custom');
60622 background.baseLayerSource(custom.template(template));
60623 corePreferences('background-custom-template', template);
60625 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
60628 var locator = imageryIndex.backgrounds.find(function (d) {
60629 return d.overlay && d["default"];
60633 background.toggleOverlayLayer(locator);
60636 var overlays = (hash.overlays || '').split(',');
60637 overlays.forEach(function (overlay) {
60638 overlay = background.findSource(overlay);
60641 background.toggleOverlayLayer(overlay);
60646 var gpx = context.layers().layer('data');
60649 gpx.url(hash.gpx, '.gpx');
60654 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
60655 return !isNaN(n) && n;
60658 if (offset.length === 2) {
60659 background.offset(geoMetersToOffset(offset));
60662 })["catch"](function () {
60667 return utilRebind(background, dispatch, 'on');
60670 function rendererFeatures(context) {
60671 var dispatch = dispatch$8('change', 'redraw');
60672 var features = utilRebind({}, dispatch, 'on');
60674 var _deferred = new Set();
60676 var traffic_roads = {
60678 'motorway_link': true,
60680 'trunk_link': true,
60682 'primary_link': true,
60684 'secondary_link': true,
60686 'tertiary_link': true,
60687 'residential': true,
60688 'unclassified': true,
60689 'living_street': true
60691 var service_roads = {
60704 var past_futures = {
60706 'construction': true,
60708 'dismantled': true,
60711 'demolished': true,
60712 'obliterated': true
60714 var _cullFactor = 1;
60720 var _forceVisible = {};
60722 function update() {
60723 if (!window.mocha) {
60724 var hash = utilStringQs(window.location.hash);
60725 var disabled = features.disabled();
60727 if (disabled.length) {
60728 hash.disable_features = disabled.join(',');
60730 delete hash.disable_features;
60733 window.location.replace('#' + utilQsString(hash, true));
60734 corePreferences('disabled-features', disabled.join(','));
60737 _hidden = features.hidden();
60738 dispatch.call('change');
60739 dispatch.call('redraw');
60742 function defineRule(k, filter, max) {
60743 var isEnabled = true;
60749 enabled: isEnabled,
60750 // whether the user wants it enabled..
60752 currentMax: max || Infinity,
60753 defaultMax: max || Infinity,
60754 enable: function enable() {
60755 this.enabled = true;
60756 this.currentMax = this.defaultMax;
60758 disable: function disable() {
60759 this.enabled = false;
60760 this.currentMax = 0;
60762 hidden: function hidden() {
60763 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
60765 autoHidden: function autoHidden() {
60766 return this.hidden() && this.currentMax > 0;
60771 defineRule('points', function isPoint(tags, geometry) {
60772 return geometry === 'point';
60774 defineRule('traffic_roads', function isTrafficRoad(tags) {
60775 return traffic_roads[tags.highway];
60777 defineRule('service_roads', function isServiceRoad(tags) {
60778 return service_roads[tags.highway];
60780 defineRule('paths', function isPath(tags) {
60781 return paths[tags.highway];
60783 defineRule('buildings', function isBuilding(tags) {
60784 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
60786 defineRule('building_parts', function isBuildingPart(tags) {
60787 return tags['building:part'];
60789 defineRule('indoor', function isIndoor(tags) {
60790 return tags.indoor;
60792 defineRule('landuse', function isLanduse(tags, geometry) {
60793 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
60795 defineRule('boundaries', function isBoundary(tags) {
60796 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);
60798 defineRule('water', function isWater(tags) {
60799 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';
60801 defineRule('rail', function isRail(tags) {
60802 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
60804 defineRule('pistes', function isPiste(tags) {
60805 return tags['piste:type'];
60807 defineRule('aerialways', function isPiste(tags) {
60808 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
60810 defineRule('power', function isPower(tags) {
60811 return !!tags.power;
60812 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
60814 defineRule('past_future', function isPastFuture(tags) {
60815 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
60819 var strings = Object.keys(tags);
60821 for (var i = 0; i < strings.length; i++) {
60822 var s = strings[i];
60824 if (past_futures[s] || past_futures[tags[s]]) {
60830 }); // Lines or areas that don't match another feature filter.
60831 // IMPORTANT: The 'others' feature must be the last one defined,
60832 // so that code in getMatches can skip this test if `hasMatch = true`
60834 defineRule('others', function isOther(tags, geometry) {
60835 return geometry === 'line' || geometry === 'area';
60838 features.features = function () {
60842 features.keys = function () {
60846 features.enabled = function (k) {
60847 if (!arguments.length) {
60848 return _keys.filter(function (k) {
60849 return _rules[k].enabled;
60853 return _rules[k] && _rules[k].enabled;
60856 features.disabled = function (k) {
60857 if (!arguments.length) {
60858 return _keys.filter(function (k) {
60859 return !_rules[k].enabled;
60863 return _rules[k] && !_rules[k].enabled;
60866 features.hidden = function (k) {
60867 if (!arguments.length) {
60868 return _keys.filter(function (k) {
60869 return _rules[k].hidden();
60873 return _rules[k] && _rules[k].hidden();
60876 features.autoHidden = function (k) {
60877 if (!arguments.length) {
60878 return _keys.filter(function (k) {
60879 return _rules[k].autoHidden();
60883 return _rules[k] && _rules[k].autoHidden();
60886 features.enable = function (k) {
60887 if (_rules[k] && !_rules[k].enabled) {
60888 _rules[k].enable();
60894 features.enableAll = function () {
60895 var didEnable = false;
60897 for (var k in _rules) {
60898 if (!_rules[k].enabled) {
60901 _rules[k].enable();
60905 if (didEnable) update();
60908 features.disable = function (k) {
60909 if (_rules[k] && _rules[k].enabled) {
60910 _rules[k].disable();
60916 features.disableAll = function () {
60917 var didDisable = false;
60919 for (var k in _rules) {
60920 if (_rules[k].enabled) {
60923 _rules[k].disable();
60927 if (didDisable) update();
60930 features.toggle = function (k) {
60933 return f.enabled ? f.disable() : f.enable();
60940 features.resetStats = function () {
60941 for (var i = 0; i < _keys.length; i++) {
60942 _rules[_keys[i]].count = 0;
60945 dispatch.call('change');
60948 features.gatherStats = function (d, resolver, dimensions) {
60949 var needsRedraw = false;
60950 var types = utilArrayGroupBy(d, 'type');
60951 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
60952 var currHidden, geometry, matches, i, j;
60954 for (i = 0; i < _keys.length; i++) {
60955 _rules[_keys[i]].count = 0;
60956 } // adjust the threshold for point/building culling based on viewport size..
60957 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
60960 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
60962 for (i = 0; i < entities.length; i++) {
60963 geometry = entities[i].geometry(resolver);
60964 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
60966 for (j = 0; j < matches.length; j++) {
60967 _rules[matches[j]].count++;
60971 currHidden = features.hidden();
60973 if (currHidden !== _hidden) {
60974 _hidden = currHidden;
60975 needsRedraw = true;
60976 dispatch.call('change');
60979 return needsRedraw;
60982 features.stats = function () {
60983 for (var i = 0; i < _keys.length; i++) {
60984 _stats[_keys[i]] = _rules[_keys[i]].count;
60990 features.clear = function (d) {
60991 for (var i = 0; i < d.length; i++) {
60992 features.clearEntity(d[i]);
60996 features.clearEntity = function (entity) {
60997 delete _cache[osmEntity.key(entity)];
61000 features.reset = function () {
61001 Array.from(_deferred).forEach(function (handle) {
61002 window.cancelIdleCallback(handle);
61004 _deferred["delete"](handle);
61007 }; // only certain relations are worth checking
61010 function relationShouldBeChecked(relation) {
61011 // multipolygon features have `area` geometry and aren't checked here
61012 return relation.tags.type === 'boundary';
61015 features.getMatches = function (entity, resolver, geometry) {
61016 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
61017 var ent = osmEntity.key(entity);
61019 if (!_cache[ent]) {
61023 if (!_cache[ent].matches) {
61025 var hasMatch = false;
61027 for (var i = 0; i < _keys.length; i++) {
61028 if (_keys[i] === 'others') {
61029 if (hasMatch) continue; // If an entity...
61030 // 1. is a way that hasn't matched other 'interesting' feature rules,
61032 if (entity.type === 'way') {
61033 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
61035 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
61036 parents.length > 0 && parents.every(function (parent) {
61037 return parent.tags.type === 'boundary';
61039 // ...then match whatever feature rules the parent relation has matched.
61040 // see #2548, #2887
61043 // For this to work, getMatches must be called on relations before ways.
61045 var pkey = osmEntity.key(parents[0]);
61047 if (_cache[pkey] && _cache[pkey].matches) {
61048 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
61056 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
61057 matches[_keys[i]] = hasMatch = true;
61061 _cache[ent].matches = matches;
61064 return _cache[ent].matches;
61067 features.getParents = function (entity, resolver, geometry) {
61068 if (geometry === 'point') return [];
61069 var ent = osmEntity.key(entity);
61071 if (!_cache[ent]) {
61075 if (!_cache[ent].parents) {
61078 if (geometry === 'vertex') {
61079 parents = resolver.parentWays(entity);
61081 // 'line', 'area', 'relation'
61082 parents = resolver.parentRelations(entity);
61085 _cache[ent].parents = parents;
61088 return _cache[ent].parents;
61091 features.isHiddenPreset = function (preset, geometry) {
61092 if (!_hidden.length) return false;
61093 if (!preset.tags) return false;
61094 var test = preset.setTags({}, geometry);
61096 for (var key in _rules) {
61097 if (_rules[key].filter(test, geometry)) {
61098 if (_hidden.indexOf(key) !== -1) {
61109 features.isHiddenFeature = function (entity, resolver, geometry) {
61110 if (!_hidden.length) return false;
61111 if (!entity.version) return false;
61112 if (_forceVisible[entity.id]) return false;
61113 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
61114 return matches.length && matches.every(function (k) {
61115 return features.hidden(k);
61119 features.isHiddenChild = function (entity, resolver, geometry) {
61120 if (!_hidden.length) return false;
61121 if (!entity.version || geometry === 'point') return false;
61122 if (_forceVisible[entity.id]) return false;
61123 var parents = features.getParents(entity, resolver, geometry);
61124 if (!parents.length) return false;
61126 for (var i = 0; i < parents.length; i++) {
61127 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
61135 features.hasHiddenConnections = function (entity, resolver) {
61136 if (!_hidden.length) return false;
61137 var childNodes, connections;
61139 if (entity.type === 'midpoint') {
61140 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
61143 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
61144 connections = features.getParents(entity, resolver, entity.geometry(resolver));
61145 } // gather ways connected to child nodes..
61148 connections = childNodes.reduce(function (result, e) {
61149 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
61151 return connections.some(function (e) {
61152 return features.isHidden(e, resolver, e.geometry(resolver));
61156 features.isHidden = function (entity, resolver, geometry) {
61157 if (!_hidden.length) return false;
61158 if (!entity.version) return false;
61159 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
61160 return fn(entity, resolver, geometry);
61163 features.filter = function (d, resolver) {
61164 if (!_hidden.length) return d;
61167 for (var i = 0; i < d.length; i++) {
61170 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
61171 result.push(entity);
61178 features.forceVisible = function (entityIDs) {
61179 if (!arguments.length) return Object.keys(_forceVisible);
61180 _forceVisible = {};
61182 for (var i = 0; i < entityIDs.length; i++) {
61183 _forceVisible[entityIDs[i]] = true;
61184 var entity = context.hasEntity(entityIDs[i]);
61186 if (entity && entity.type === 'relation') {
61187 // also show relation members (one level deep)
61188 for (var j in entity.members) {
61189 _forceVisible[entity.members[j].id] = true;
61197 features.init = function () {
61198 var storage = corePreferences('disabled-features');
61201 var storageDisabled = storage.replace(/;/g, ',').split(',');
61202 storageDisabled.forEach(features.disable);
61205 var hash = utilStringQs(window.location.hash);
61207 if (hash.disable_features) {
61208 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
61209 hashDisabled.forEach(features.disable);
61211 }; // warm up the feature matching cache upon merging fetched data
61214 context.history().on('merge.features', function (newEntities) {
61215 if (!newEntities) return;
61216 var handle = window.requestIdleCallback(function () {
61217 var graph = context.graph();
61218 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
61220 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
61222 for (var i = 0; i < entities.length; i++) {
61223 var geometry = entities[i].geometry(graph);
61224 features.getMatches(entities[i], graph, geometry);
61228 _deferred.add(handle);
61233 /** Error message constants. */
61235 var FUNC_ERROR_TEXT = 'Expected a function';
61237 * Creates a throttled function that only invokes `func` at most once per
61238 * every `wait` milliseconds. The throttled function comes with a `cancel`
61239 * method to cancel delayed `func` invocations and a `flush` method to
61240 * immediately invoke them. Provide `options` to indicate whether `func`
61241 * should be invoked on the leading and/or trailing edge of the `wait`
61242 * timeout. The `func` is invoked with the last arguments provided to the
61243 * throttled function. Subsequent calls to the throttled function return the
61244 * result of the last `func` invocation.
61246 * **Note:** If `leading` and `trailing` options are `true`, `func` is
61247 * invoked on the trailing edge of the timeout only if the throttled function
61248 * is invoked more than once during the `wait` timeout.
61250 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
61251 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
61253 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
61254 * for details over the differences between `_.throttle` and `_.debounce`.
61259 * @category Function
61260 * @param {Function} func The function to throttle.
61261 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
61262 * @param {Object} [options={}] The options object.
61263 * @param {boolean} [options.leading=true]
61264 * Specify invoking on the leading edge of the timeout.
61265 * @param {boolean} [options.trailing=true]
61266 * Specify invoking on the trailing edge of the timeout.
61267 * @returns {Function} Returns the new throttled function.
61270 * // Avoid excessively updating the position while scrolling.
61271 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
61273 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
61274 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
61275 * jQuery(element).on('click', throttled);
61277 * // Cancel the trailing throttled invocation.
61278 * jQuery(window).on('popstate', throttled.cancel);
61281 function throttle(func, wait, options) {
61282 var leading = true,
61285 if (typeof func != 'function') {
61286 throw new TypeError(FUNC_ERROR_TEXT);
61289 if (isObject$2(options)) {
61290 leading = 'leading' in options ? !!options.leading : leading;
61291 trailing = 'trailing' in options ? !!options.trailing : trailing;
61294 return debounce(func, wait, {
61295 'leading': leading,
61297 'trailing': trailing
61302 // - the activeID - nope
61303 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
61304 // - 2 away from the activeID - nope (would create a self intersecting segment)
61305 // - all others on a linear way - yes
61306 // - all others on a closed way - nope (would create a self intersecting polygon)
61309 // 0 = active vertex - no touch/connect
61310 // 1 = passive vertex - yes touch/connect
61311 // 2 = adjacent vertex - yes but pay attention segmenting a line here
61314 function svgPassiveVertex(node, graph, activeID) {
61315 if (!activeID) return 1;
61316 if (activeID === node.id) return 0;
61317 var parents = graph.parentWays(node);
61318 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
61320 for (i = 0; i < parents.length; i++) {
61321 nodes = parents[i].nodes;
61322 isClosed = parents[i].isClosed();
61324 for (j = 0; j < nodes.length; j++) {
61325 // find this vertex, look nearby
61326 if (nodes[j] === node.id) {
61333 // wraparound if needed
61334 max = nodes.length - 1;
61335 if (ix1 < 0) ix1 = max + ix1;
61336 if (ix2 < 0) ix2 = max + ix2;
61337 if (ix3 > max) ix3 = ix3 - max;
61338 if (ix4 > max) ix4 = ix4 - max;
61341 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
61342 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
61343 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
61344 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
61345 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
61352 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
61353 return function (entity) {
61357 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
61358 var coordinates = graph.childNodes(entity).map(function (n) {
61363 if (shouldReverse(entity)) {
61364 coordinates.reverse();
61368 type: 'LineString',
61369 coordinates: coordinates
61370 }, projection.stream(clip({
61371 lineStart: function lineStart() {},
61372 lineEnd: function lineEnd() {
61375 point: function point(x, y) {
61379 var span = geoVecLength(a, b) - offset;
61382 var heading = geoVecAngle(a, b);
61383 var dx = dt * Math.cos(heading);
61384 var dy = dt * Math.sin(heading);
61385 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
61387 var coord = [a, p];
61389 for (span -= dt; span >= 0; span -= dt) {
61390 p = geoVecAdd(p, [dx, dy]);
61394 coord.push(b); // generate svg paths
61399 for (j = 0; j < coord.length; j++) {
61400 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61409 if (bothDirections(entity)) {
61412 for (j = coord.length - 1; j >= 0; j--) {
61413 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
61433 function svgPath(projection, graph, isArea) {
61434 // Explanation of magic numbers:
61435 // "padding" here allows space for strokes to extend beyond the viewport,
61436 // so that the stroke isn't drawn along the edge of the viewport when
61437 // the shape is clipped.
61439 // When drawing lines, pad viewport by 5px.
61440 // When drawing areas, pad viewport by 65px in each direction to allow
61441 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
61443 var padding = isArea ? 65 : 5;
61444 var viewport = projection.clipExtent();
61445 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
61446 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
61447 var project = projection.stream;
61448 var path = d3_geoPath().projection({
61449 stream: function stream(output) {
61450 return project(clip(output));
61454 var svgpath = function svgpath(entity) {
61455 if (entity.id in cache) {
61456 return cache[entity.id];
61458 return cache[entity.id] = path(entity.asGeoJSON(graph));
61462 svgpath.geojson = function (d) {
61463 if (d.__featurehash__ !== undefined) {
61464 if (d.__featurehash__ in cache) {
61465 return cache[d.__featurehash__];
61467 return cache[d.__featurehash__] = path(d);
61476 function svgPointTransform(projection) {
61477 var svgpoint = function svgpoint(entity) {
61478 // http://jsperf.com/short-array-join
61479 var pt = projection(entity.loc);
61480 return 'translate(' + pt[0] + ',' + pt[1] + ')';
61483 svgpoint.geojson = function (d) {
61484 return svgpoint(d.properties.entity);
61489 function svgRelationMemberTags(graph) {
61490 return function (entity) {
61491 var tags = entity.tags;
61492 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
61493 graph.parentRelations(entity).forEach(function (relation) {
61494 var type = relation.tags.type;
61496 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
61497 tags = Object.assign({}, relation.tags, tags);
61503 function svgSegmentWay(way, graph, activeID) {
61504 // When there is no activeID, we can memoize this expensive computation
61505 if (activeID === undefined) {
61506 return graph["transient"](way, 'waySegments', getWaySegments);
61508 return getWaySegments();
61511 function getWaySegments() {
61512 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
61521 for (var i = 0; i < way.nodes.length; i++) {
61522 node = graph.entity(way.nodes[i]);
61523 type = svgPassiveVertex(node, graph, activeID);
61529 if (start.type !== undefined) {
61530 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
61531 // one adjacent vertex
61532 pushActive(start, end, i);
61533 } else if (start.type === 0 && end.type === 0) {
61534 // both active vertices
61535 pushActive(start, end, i);
61537 pushPassive(start, end, i);
61546 function pushActive(start, end, index) {
61547 features.active.push({
61549 id: way.id + '-' + index + '-nope',
61554 nodes: [start.node, end.node],
61558 type: 'LineString',
61559 coordinates: [start.node.loc, end.node.loc]
61564 function pushPassive(start, end, index) {
61565 features.passive.push({
61567 id: way.id + '-' + index,
61571 nodes: [start.node, end.node],
61575 type: 'LineString',
61576 coordinates: [start.node.loc, end.node.loc]
61583 function svgTagClasses() {
61584 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'];
61585 var statuses = [// nonexistent, might be built
61586 'proposed', 'planned', // under maintentance or between groundbreaking and opening
61587 'construction', // existent but not functional
61588 'disused', // dilapidated to nonexistent
61589 'abandoned', // nonexistent, still may appear in imagery
61590 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
61592 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
61594 var _tags = function _tags(entity) {
61595 return entity.tags;
61598 var tagClasses = function tagClasses(selection) {
61599 selection.each(function tagClassesEach(entity) {
61600 var value = this.className;
61602 if (value.baseVal !== undefined) {
61603 value = value.baseVal;
61606 var t = _tags(entity);
61608 var computed = tagClasses.getClassesString(t, value);
61610 if (computed !== value) {
61611 select(this).attr('class', computed);
61616 tagClasses.getClassesString = function (t, value) {
61617 var primary, status;
61618 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
61620 var overrideGeometry;
61622 if (/\bstroke\b/.test(value)) {
61623 if (!!t.barrier && t.barrier !== 'no') {
61624 overrideGeometry = 'line';
61626 } // preserve base classes (nothing with `tag-`)
61629 var classes = value.trim().split(/\s+/).filter(function (klass) {
61630 return klass.length && !/^tag-/.test(klass);
61631 }).map(function (klass) {
61632 // special overrides for some perimeter strokes
61633 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
61634 }); // pick at most one primary classification tag..
61636 for (i = 0; i < primaries.length; i++) {
61639 if (!v || v === 'no') continue;
61641 if (k === 'piste:type') {
61642 // avoid a ':' in the class name
61644 } else if (k === 'building:part') {
61645 // avoid a ':' in the class name
61646 k = 'building_part';
61651 if (statuses.indexOf(v) !== -1) {
61652 // e.g. `railway=abandoned`
61654 classes.push('tag-' + k);
61656 classes.push('tag-' + k);
61657 classes.push('tag-' + k + '-' + v);
61664 for (i = 0; i < statuses.length; i++) {
61665 for (j = 0; j < primaries.length; j++) {
61666 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
61669 if (!v || v === 'no') continue;
61670 status = statuses[i];
61674 } // add at most one status tag, only if relates to primary tag..
61678 for (i = 0; i < statuses.length; i++) {
61681 if (!v || v === 'no') continue;
61684 // e.g. `railway=rail + abandoned=yes`
61686 } else if (primary && primary === v) {
61687 // e.g. `railway=rail + abandoned=railway`
61689 } else if (!primary && primaries.indexOf(v) !== -1) {
61690 // e.g. `abandoned=railway`
61693 classes.push('tag-' + v);
61694 } // else ignore e.g. `highway=path + abandoned=railway`
61702 classes.push('tag-status');
61703 classes.push('tag-status-' + status);
61704 } // add any secondary tags
61707 for (i = 0; i < secondaries.length; i++) {
61708 k = secondaries[i];
61710 if (!v || v === 'no' || k === primary) continue;
61711 classes.push('tag-' + k);
61712 classes.push('tag-' + k + '-' + v);
61713 } // For highways, look for surface tagging..
61716 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
61717 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
61722 if (k in osmPavedTags) {
61723 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
61726 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
61727 surface = 'semipaved';
61731 classes.push('tag-' + surface);
61732 } // If this is a wikidata-tagged item, add a class for that..
61735 var qid = t.wikidata || t['flag:wikidata'] || t['brand:wikidata'] || t['network:wikidata'] || t['operator:wikidata'];
61738 classes.push('tag-wikidata');
61741 return classes.join(' ').trim();
61744 tagClasses.tags = function (val) {
61745 if (!arguments.length) return _tags;
61753 // Patterns only work in Firefox when set directly on element.
61754 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
61756 // tag - pattern name
61758 // tag - value - pattern name
61760 // tag - value - rules (optional tag-values, pattern name)
61761 // (matches earlier rules first, so fallback should be last entry)
61763 grave_yard: 'cemetery',
61764 fountain: 'water_standing'
61768 religion: 'christian',
61769 pattern: 'cemetery_christian'
61771 religion: 'buddhist',
61772 pattern: 'cemetery_buddhist'
61774 religion: 'muslim',
61775 pattern: 'cemetery_muslim'
61777 religion: 'jewish',
61778 pattern: 'cemetery_jewish'
61780 pattern: 'cemetery'
61782 construction: 'construction',
61783 farmland: 'farmland',
61784 farmyard: 'farmyard',
61786 leaf_type: 'broadleaved',
61787 pattern: 'forest_broadleaved'
61789 leaf_type: 'needleleaved',
61790 pattern: 'forest_needleleaved'
61792 leaf_type: 'leafless',
61793 pattern: 'forest_leafless'
61796 } // same as 'leaf_type:mixed'
61798 grave_yard: 'cemetery',
61801 pattern: 'golf_green'
61805 landfill: 'landfill',
61807 military: 'construction',
61808 orchard: 'orchard',
61810 vineyard: 'vineyard'
61814 grassland: 'grass',
61821 water: 'reservoir',
61822 pattern: 'water_standing'
61828 pattern: 'wetland_marsh'
61831 pattern: 'wetland_swamp'
61834 pattern: 'wetland_bog'
61836 wetland: 'reedbed',
61837 pattern: 'wetland_reedbed'
61842 leaf_type: 'broadleaved',
61843 pattern: 'forest_broadleaved'
61845 leaf_type: 'needleleaved',
61846 pattern: 'forest_needleleaved'
61848 leaf_type: 'leafless',
61849 pattern: 'forest_leafless'
61852 } // same as 'leaf_type:mixed'
61870 function svgTagPattern(tags) {
61871 // Skip pattern filling if this is a building (buildings don't get patterns applied)
61872 if (tags.building && tags.building !== 'no') {
61876 for (var tag in patterns) {
61877 var entityValue = tags[tag];
61878 if (!entityValue) continue;
61880 if (typeof patterns[tag] === 'string') {
61881 // extra short syntax (just tag) - pattern name
61882 return 'pattern-' + patterns[tag];
61884 var values = patterns[tag];
61886 for (var value in values) {
61887 if (entityValue !== value) continue;
61888 var rules = values[value];
61890 if (typeof rules === 'string') {
61891 // short syntax - pattern name
61892 return 'pattern-' + rules;
61893 } // long syntax - rule array
61896 for (var ruleKey in rules) {
61897 var rule = rules[ruleKey];
61900 for (var criterion in rule) {
61901 if (criterion !== 'pattern') {
61902 // reserved for pattern name
61903 // The only rule is a required tag-value pair
61904 var v = tags[criterion];
61906 if (!v || v !== rule[criterion]) {
61914 return 'pattern-' + rule.pattern;
61924 function svgAreas(projection, context) {
61925 function getPatternStyle(tags) {
61926 var imageID = svgTagPattern(tags);
61929 return 'url("#ideditor-' + imageID + '")';
61935 function drawTargets(selection, graph, entities, filter) {
61936 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
61937 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
61938 var getPath = svgPath(projection).geojson;
61939 var activeID = context.activeID();
61940 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
61946 entities.forEach(function (way) {
61947 var features = svgSegmentWay(way, graph, activeID);
61948 data.targets.push.apply(data.targets, features.passive);
61949 data.nopes.push.apply(data.nopes, features.active);
61950 }); // Targets allow hover and vertex snapping
61952 var targetData = data.targets.filter(getPath);
61953 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
61954 return filter(d.properties.entity);
61955 }).data(targetData, function key(d) {
61959 targets.exit().remove();
61961 var segmentWasEdited = function segmentWasEdited(d) {
61962 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
61964 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
61968 return d.properties.nodes.some(function (n) {
61969 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
61974 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
61975 return 'way area target target-allowed ' + targetClass + d.id;
61976 }).classed('segment-edited', segmentWasEdited); // NOPE
61978 var nopeData = data.nopes.filter(getPath);
61979 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
61980 return filter(d.properties.entity);
61981 }).data(nopeData, function key(d) {
61985 nopes.exit().remove(); // enter/update
61987 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
61988 return 'way area target target-nope ' + nopeClass + d.id;
61989 }).classed('segment-edited', segmentWasEdited);
61992 function drawAreas(selection, graph, entities, filter) {
61993 var path = svgPath(projection, graph, true);
61996 var base = context.history().base();
61998 for (var i = 0; i < entities.length; i++) {
61999 var entity = entities[i];
62000 if (entity.geometry(graph) !== 'area') continue;
62001 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
62003 if (multipolygon) {
62004 areas[multipolygon.id] = {
62005 entity: multipolygon.mergeTags(entity.tags),
62006 area: Math.abs(entity.area(graph))
62008 } else if (!areas[entity.id]) {
62009 areas[entity.id] = {
62011 area: Math.abs(entity.area(graph))
62016 var fills = Object.values(areas).filter(function hasPath(a) {
62017 return path(a.entity);
62019 fills.sort(function areaSort(a, b) {
62020 return b.area - a.area;
62022 fills = fills.map(function (a) {
62025 var strokes = fills.filter(function (area) {
62026 return area.type === 'way';
62034 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
62035 clipPaths.exit().remove();
62036 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
62037 return 'ideditor-' + entity.id + '-clippath';
62039 clipPathsEnter.append('path');
62040 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
62041 var drawLayer = selection.selectAll('.layer-osm.areas');
62042 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
62044 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
62045 areagroup = areagroup.enter().append('g').attr('class', function (d) {
62046 return 'areagroup area-' + d;
62047 }).merge(areagroup);
62048 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
62049 return data[layer];
62051 paths.exit().remove();
62052 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
62053 var bisect = d3_bisector(function (node) {
62054 return -node.__data__.area(graph);
62057 function sortedByArea(entity) {
62058 if (this._parent.__data__ === 'fill') {
62059 return fillpaths[bisect(fillpaths, -entity.area(graph))];
62063 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
62064 var layer = this.parentNode.__data__;
62065 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
62067 if (layer === 'fill') {
62068 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
62069 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
62071 }).classed('added', function (d) {
62072 return !base.entities[d.id];
62073 }).classed('geometry-edited', function (d) {
62074 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
62075 }).classed('retagged', function (d) {
62076 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
62077 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
62079 touchLayer.call(drawTargets, graph, data.stroke, filter);
62085 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
62086 if (!opts) opts = {};
62087 if (typeof opts === 'function') opts = {
62090 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
62092 var cmp = opts.cmp && function (f) {
62093 return function (node) {
62094 return function (a, b) {
62103 return f(aobj, bobj);
62109 return function stringify(node) {
62110 if (node && node.toJSON && typeof node.toJSON === 'function') {
62111 node = node.toJSON();
62114 if (node === undefined) return;
62115 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
62116 if (_typeof(node) !== 'object') return JSON.stringify(node);
62119 if (Array.isArray(node)) {
62122 for (i = 0; i < node.length; i++) {
62124 out += stringify(node[i]) || 'null';
62130 if (node === null) return 'null';
62132 if (seen.indexOf(node) !== -1) {
62133 if (cycles) return JSON.stringify('__cycle__');
62134 throw new TypeError('Converting circular structure to JSON');
62137 var seenIndex = seen.push(node) - 1;
62138 var keys = Object.keys(node).sort(cmp && cmp(node));
62141 for (i = 0; i < keys.length; i++) {
62143 var value = stringify(node[key]);
62144 if (!value) continue;
62145 if (out) out += ',';
62146 out += JSON.stringify(key) + ':' + value;
62149 seen.splice(seenIndex, 1);
62150 return '{' + out + '}';
62154 var $entries = objectToArray.entries;
62156 // `Object.entries` method
62157 // https://tc39.es/ecma262/#sec-object.entries
62158 _export({ target: 'Object', stat: true }, {
62159 entries: function entries(O) {
62160 return $entries(O);
62164 var _marked = /*#__PURE__*/regeneratorRuntime.mark(gpxGen),
62165 _marked3 = /*#__PURE__*/regeneratorRuntime.mark(kmlGen);
62167 // cast array x into numbers
62168 // get the content of a text node, if any
62169 function nodeVal(x) {
62170 if (x && x.normalize) {
62174 return x && x.textContent || "";
62175 } // one Y child of X, if any, otherwise null
62178 function get1(x, y) {
62179 var n = x.getElementsByTagName(y);
62180 return n.length ? n[0] : null;
62183 function getLineStyle(extensions) {
62187 var lineStyle = get1(extensions, "line");
62190 var color = nodeVal(get1(lineStyle, "color")),
62191 opacity = parseFloat(nodeVal(get1(lineStyle, "opacity"))),
62192 width = parseFloat(nodeVal(get1(lineStyle, "width")));
62193 if (color) style.stroke = color;
62194 if (!isNaN(opacity)) style["stroke-opacity"] = opacity; // GPX width is in mm, convert to px with 96 px per inch
62196 if (!isNaN(width)) style["stroke-width"] = width * 96 / 25.4;
62201 } // get the contents of multiple text nodes, if present
62204 function getMulti(x, ys) {
62209 for (k = 0; k < ys.length; k++) {
62210 n = get1(x, ys[k]);
62211 if (n) o[ys[k]] = nodeVal(n);
62217 function getProperties$1(node) {
62218 var prop = getMulti(node, ["name", "cmt", "desc", "type", "time", "keywords"]); // Parse additional data from our Garmin extension(s)
62220 var extensions = node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*");
62222 for (var i = 0; i < extensions.length; i++) {
62223 var extension = extensions[i]; // Ignore nested extensions, like those on routepoints or trackpoints
62225 if (extension.parentNode.parentNode === node) {
62226 prop[extension.tagName.replace(":", "_")] = nodeVal(extension);
62230 var links = node.getElementsByTagName("link");
62231 if (links.length) prop.links = [];
62233 for (var _i = 0; _i < links.length; _i++) {
62234 prop.links.push(Object.assign({
62235 href: links[_i].getAttribute("href")
62236 }, getMulti(links[_i], ["text", "type"])));
62242 function coordPair$1(x) {
62243 var ll = [parseFloat(x.getAttribute("lon")), parseFloat(x.getAttribute("lat"))];
62244 var ele = get1(x, "ele"); // handle namespaced attribute in browser
62246 var heart = get1(x, "gpxtpx:hr") || get1(x, "hr");
62247 var time = get1(x, "time");
62251 e = parseFloat(nodeVal(ele));
62260 time: time ? nodeVal(time) : null,
62265 result.extendedValues.push(["heart", parseFloat(nodeVal(heart))]);
62268 var extensions = get1(x, "extensions");
62270 if (extensions !== null) {
62271 for (var _i2 = 0, _arr = ["speed", "course", "hAcc", "vAcc"]; _i2 < _arr.length; _i2++) {
62272 var name = _arr[_i2];
62273 var v = parseFloat(nodeVal(get1(extensions, name)));
62276 result.extendedValues.push([name, v]);
62284 function getRoute(node) {
62285 var line = getPoints$1(node, "rtept");
62289 properties: Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), {
62293 type: "LineString",
62294 coordinates: line.line
62299 function getPoints$1(node, pointname) {
62300 var pts = node.getElementsByTagName(pointname);
62301 if (pts.length < 2) return; // Invalid line in GeoJSON
62305 var extendedValues = {};
62307 for (var i = 0; i < pts.length; i++) {
62308 var c = coordPair$1(pts[i]);
62309 line.push(c.coordinates);
62310 if (c.time) times.push(c.time);
62312 for (var j = 0; j < c.extendedValues.length; j++) {
62313 var _c$extendedValues$j = _slicedToArray(c.extendedValues[j], 2),
62314 name = _c$extendedValues$j[0],
62315 val = _c$extendedValues$j[1];
62317 var plural = name === "heart" ? name : name + "s";
62319 if (!extendedValues[plural]) {
62320 extendedValues[plural] = Array(pts.length).fill(null);
62323 extendedValues[plural][i] = val;
62330 extendedValues: extendedValues
62334 function getTrack(node) {
62335 var segments = node.getElementsByTagName("trkseg");
62338 var extractedLines = [];
62340 for (var i = 0; i < segments.length; i++) {
62341 var line = getPoints$1(segments[i], "trkpt");
62344 extractedLines.push(line);
62345 if (line.times && line.times.length) times.push(line.times);
62349 if (extractedLines.length === 0) return;
62350 var multi = extractedLines.length > 1;
62351 var properties = Object.assign(getProperties$1(node), getLineStyle(get1(node, "extensions")), {
62353 }, times.length ? {
62354 coordinateProperties: {
62355 times: multi ? times : times[0]
62359 for (var _i3 = 0; _i3 < extractedLines.length; _i3++) {
62360 var _line = extractedLines[_i3];
62361 track.push(_line.line);
62363 for (var _i4 = 0, _Object$entries = Object.entries(_line.extendedValues); _i4 < _Object$entries.length; _i4++) {
62364 var _Object$entries$_i = _slicedToArray(_Object$entries[_i4], 2),
62365 name = _Object$entries$_i[0],
62366 val = _Object$entries$_i[1];
62368 var props = properties;
62370 if (name === "heart") {
62371 if (!properties.coordinateProperties) {
62372 properties.coordinateProperties = {};
62375 props = properties.coordinateProperties;
62379 if (!props[name]) props[name] = extractedLines.map(function (line) {
62380 return new Array(line.line.length).fill(null);
62382 props[name][_i3] = val;
62391 properties: properties,
62392 geometry: multi ? {
62393 type: "MultiLineString",
62396 type: "LineString",
62397 coordinates: track[0]
62402 function getPoint(node) {
62405 properties: Object.assign(getProperties$1(node), getMulti(node, ["sym"])),
62408 coordinates: coordPair$1(node).coordinates
62413 function gpxGen(doc) {
62414 var tracks, routes, waypoints, i, feature, _i5, _feature, _i6;
62416 return regeneratorRuntime.wrap(function gpxGen$(_context) {
62418 switch (_context.prev = _context.next) {
62420 tracks = doc.getElementsByTagName("trk");
62421 routes = doc.getElementsByTagName("rte");
62422 waypoints = doc.getElementsByTagName("wpt");
62426 if (!(i < tracks.length)) {
62427 _context.next = 12;
62431 feature = getTrack(tracks[i]);
62450 if (!(_i5 < routes.length)) {
62451 _context.next = 21;
62455 _feature = getRoute(routes[_i5]);
62458 _context.next = 18;
62462 _context.next = 18;
62467 _context.next = 13;
62474 if (!(_i6 < waypoints.length)) {
62475 _context.next = 28;
62479 _context.next = 25;
62480 return getPoint(waypoints[_i6]);
62484 _context.next = 22;
62489 return _context.stop();
62495 function gpx(doc) {
62497 type: "FeatureCollection",
62498 features: Array.from(gpxGen(doc))
62502 var removeSpace = /\s*/g;
62503 var trimSpace = /^\s*|\s*$/g;
62504 var splitSpace = /\s+/; // generate a short, numeric hash of a string
62506 function okhash(x) {
62507 if (!x || !x.length) return 0;
62510 for (var i = 0; i < x.length; i++) {
62511 h = (h << 5) - h + x.charCodeAt(i) | 0;
62515 } // get one coordinate from a coordinate array, if any
62518 function coord1(v) {
62519 return v.replace(removeSpace, "").split(",").map(parseFloat);
62520 } // get all coordinates from a coordinate array as [[],[]]
62523 function coord(v) {
62524 return v.replace(trimSpace, "").split(splitSpace).map(coord1);
62527 function xml2str(node) {
62528 if (node.xml !== undefined) return node.xml;
62530 if (node.tagName) {
62531 var output = node.tagName;
62533 for (var i = 0; i < node.attributes.length; i++) {
62534 output += node.attributes[i].name + node.attributes[i].value;
62537 for (var _i9 = 0; _i9 < node.childNodes.length; _i9++) {
62538 output += xml2str(node.childNodes[_i9]);
62544 if (node.nodeName === "#text") {
62545 return (node.nodeValue || node.value || "").trim();
62548 if (node.nodeName === "#cdata-section") {
62549 return node.nodeValue;
62555 var geotypes = ["Polygon", "LineString", "Point", "Track", "gx:Track"];
62557 function kmlColor(properties, elem, prefix) {
62558 var v = nodeVal(get1(elem, "color")) || "";
62559 var colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
62561 if (v.substr(0, 1) === "#") {
62565 if (v.length === 6 || v.length === 3) {
62566 properties[colorProp] = v;
62567 } else if (v.length === 8) {
62568 properties[prefix + "-opacity"] = parseInt(v.substr(0, 2), 16) / 255;
62569 properties[colorProp] = "#" + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
62573 function numericProperty(properties, elem, source, target) {
62574 var val = parseFloat(nodeVal(get1(elem, source)));
62575 if (!isNaN(val)) properties[target] = val;
62578 function gxCoords(root) {
62579 var elems = root.getElementsByTagName("coord");
62582 if (elems.length === 0) elems = root.getElementsByTagName("gx:coord");
62584 for (var i = 0; i < elems.length; i++) {
62585 coords.push(nodeVal(elems[i]).split(" ").map(parseFloat));
62588 var timeElems = root.getElementsByTagName("when");
62590 for (var j = 0; j < timeElems.length; j++) {
62591 times.push(nodeVal(timeElems[j]));
62600 function getGeometry(root) {
62607 var coordTimes = [];
62609 if (get1(root, "MultiGeometry")) {
62610 return getGeometry(get1(root, "MultiGeometry"));
62613 if (get1(root, "MultiTrack")) {
62614 return getGeometry(get1(root, "MultiTrack"));
62617 if (get1(root, "gx:MultiTrack")) {
62618 return getGeometry(get1(root, "gx:MultiTrack"));
62621 for (i = 0; i < geotypes.length; i++) {
62622 geomNodes = root.getElementsByTagName(geotypes[i]);
62625 for (j = 0; j < geomNodes.length; j++) {
62626 geomNode = geomNodes[j];
62628 if (geotypes[i] === "Point") {
62631 coordinates: coord1(nodeVal(get1(geomNode, "coordinates")))
62633 } else if (geotypes[i] === "LineString") {
62635 type: "LineString",
62636 coordinates: coord(nodeVal(get1(geomNode, "coordinates")))
62638 } else if (geotypes[i] === "Polygon") {
62639 var rings = geomNode.getElementsByTagName("LinearRing"),
62642 for (k = 0; k < rings.length; k++) {
62643 coords.push(coord(nodeVal(get1(rings[k], "coordinates"))));
62648 coordinates: coords
62650 } else if (geotypes[i] === "Track" || geotypes[i] === "gx:Track") {
62651 var track = gxCoords(geomNode);
62653 type: "LineString",
62654 coordinates: track.coords
62656 if (track.times.length) coordTimes.push(track.times);
62664 coordTimes: coordTimes
62668 function getPlacemark(root, styleIndex, styleMapIndex, styleByHash) {
62669 var geomsAndTimes = getGeometry(root);
62671 var properties = {};
62672 var name = nodeVal(get1(root, "name"));
62673 var address = nodeVal(get1(root, "address"));
62674 var styleUrl = nodeVal(get1(root, "styleUrl"));
62675 var description = nodeVal(get1(root, "description"));
62676 var timeSpan = get1(root, "TimeSpan");
62677 var timeStamp = get1(root, "TimeStamp");
62678 var extendedData = get1(root, "ExtendedData");
62679 var iconStyle = get1(root, "IconStyle");
62680 var labelStyle = get1(root, "LabelStyle");
62681 var lineStyle = get1(root, "LineStyle");
62682 var polyStyle = get1(root, "PolyStyle");
62683 var visibility = get1(root, "visibility");
62684 if (name) properties.name = name;
62685 if (address) properties.address = address;
62688 if (styleUrl[0] !== "#") {
62689 styleUrl = "#" + styleUrl;
62692 properties.styleUrl = styleUrl;
62694 if (styleIndex[styleUrl]) {
62695 properties.styleHash = styleIndex[styleUrl];
62698 if (styleMapIndex[styleUrl]) {
62699 properties.styleMapHash = styleMapIndex[styleUrl];
62700 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
62701 } // Try to populate the lineStyle or polyStyle since we got the style hash
62704 var style = styleByHash[properties.styleHash];
62707 if (!iconStyle) iconStyle = get1(style, "IconStyle");
62708 if (!labelStyle) labelStyle = get1(style, "LabelStyle");
62709 if (!lineStyle) lineStyle = get1(style, "LineStyle");
62710 if (!polyStyle) polyStyle = get1(style, "PolyStyle");
62714 if (description) properties.description = description;
62717 var begin = nodeVal(get1(timeSpan, "begin"));
62718 var end = nodeVal(get1(timeSpan, "end"));
62719 properties.timespan = {
62726 properties.timestamp = nodeVal(get1(timeStamp, "when"));
62730 kmlColor(properties, iconStyle, "icon");
62731 numericProperty(properties, iconStyle, "scale", "icon-scale");
62732 numericProperty(properties, iconStyle, "heading", "icon-heading");
62733 var hotspot = get1(iconStyle, "hotSpot");
62736 var left = parseFloat(hotspot.getAttribute("x"));
62737 var top = parseFloat(hotspot.getAttribute("y"));
62738 if (!isNaN(left) && !isNaN(top)) properties["icon-offset"] = [left, top];
62741 var icon = get1(iconStyle, "Icon");
62744 var href = nodeVal(get1(icon, "href"));
62745 if (href) properties.icon = href;
62750 kmlColor(properties, labelStyle, "label");
62751 numericProperty(properties, labelStyle, "scale", "label-scale");
62755 kmlColor(properties, lineStyle, "stroke");
62756 numericProperty(properties, lineStyle, "width", "stroke-width");
62760 kmlColor(properties, polyStyle, "fill");
62761 var fill = nodeVal(get1(polyStyle, "fill"));
62762 var outline = nodeVal(get1(polyStyle, "outline"));
62763 if (fill) properties["fill-opacity"] = fill === "1" ? properties["fill-opacity"] || 1 : 0;
62764 if (outline) properties["stroke-opacity"] = outline === "1" ? properties["stroke-opacity"] || 1 : 0;
62767 if (extendedData) {
62768 var datas = extendedData.getElementsByTagName("Data"),
62769 simpleDatas = extendedData.getElementsByTagName("SimpleData");
62771 for (i = 0; i < datas.length; i++) {
62772 properties[datas[i].getAttribute("name")] = nodeVal(get1(datas[i], "value"));
62775 for (i = 0; i < simpleDatas.length; i++) {
62776 properties[simpleDatas[i].getAttribute("name")] = nodeVal(simpleDatas[i]);
62781 properties.visibility = nodeVal(visibility);
62784 if (geomsAndTimes.coordTimes.length) {
62785 properties.coordinateProperties = {
62786 times: geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes
62792 geometry: geomsAndTimes.geoms.length === 0 ? null : geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
62793 type: "GeometryCollection",
62794 geometries: geomsAndTimes.geoms
62796 properties: properties
62798 if (root.getAttribute("id")) feature.id = root.getAttribute("id");
62802 function kmlGen(doc) {
62803 var styleIndex, styleByHash, styleMapIndex, placemarks, styles, styleMaps, k, hash, l, pairs, pairsMap, m, j, feature;
62804 return regeneratorRuntime.wrap(function kmlGen$(_context3) {
62806 switch (_context3.prev = _context3.next) {
62808 // styleindex keeps track of hashed styles in order to match feature
62810 styleByHash = {}; // stylemapindex keeps track of style maps to expose in properties
62812 styleMapIndex = {}; // atomic geospatial types supported by KML - MultiGeometry is
62813 // handled separately
62814 // all root placemarks in the file
62816 placemarks = doc.getElementsByTagName("Placemark");
62817 styles = doc.getElementsByTagName("Style");
62818 styleMaps = doc.getElementsByTagName("StyleMap");
62820 for (k = 0; k < styles.length; k++) {
62821 hash = okhash(xml2str(styles[k])).toString(16);
62822 styleIndex["#" + styles[k].getAttribute("id")] = hash;
62823 styleByHash[hash] = styles[k];
62826 for (l = 0; l < styleMaps.length; l++) {
62827 styleIndex["#" + styleMaps[l].getAttribute("id")] = okhash(xml2str(styleMaps[l])).toString(16);
62828 pairs = styleMaps[l].getElementsByTagName("Pair");
62831 for (m = 0; m < pairs.length; m++) {
62832 pairsMap[nodeVal(get1(pairs[m], "key"))] = nodeVal(get1(pairs[m], "styleUrl"));
62835 styleMapIndex["#" + styleMaps[l].getAttribute("id")] = pairsMap;
62841 if (!(j < placemarks.length)) {
62842 _context3.next = 17;
62846 feature = getPlacemark(placemarks[j], styleIndex, styleMapIndex, styleByHash);
62849 _context3.next = 14;
62853 _context3.next = 14;
62858 _context3.next = 9;
62863 return _context3.stop();
62869 function kml(doc) {
62871 type: "FeatureCollection",
62872 features: Array.from(kmlGen(doc))
62876 var _initialized = false;
62877 var _enabled = false;
62881 function svgData(projection, context, dispatch) {
62882 var throttledRedraw = throttle(function () {
62883 dispatch.call('change');
62886 var _showLabels = true;
62887 var detected = utilDetect();
62888 var layer = select(null);
62899 if (_initialized) return; // run once
62904 function over(d3_event) {
62905 d3_event.stopPropagation();
62906 d3_event.preventDefault();
62907 d3_event.dataTransfer.dropEffect = 'copy';
62910 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
62911 d3_event.stopPropagation();
62912 d3_event.preventDefault();
62913 if (!detected.filedrop) return;
62914 drawData.fileList(d3_event.dataTransfer.files);
62915 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
62916 _initialized = true;
62919 function getService() {
62920 if (services.vectorTile && !_vtService) {
62921 _vtService = services.vectorTile;
62923 _vtService.event.on('loadedData', throttledRedraw);
62924 } else if (!services.vectorTile && _vtService) {
62931 function showLayer() {
62933 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
62934 dispatch.call('change');
62938 function hideLayer() {
62939 throttledRedraw.cancel();
62940 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
62943 function layerOn() {
62944 layer.style('display', 'block');
62947 function layerOff() {
62948 layer.selectAll('.viewfield-group').remove();
62949 layer.style('display', 'none');
62950 } // ensure that all geojson features in a collection have IDs
62953 function ensureIDs(gj) {
62954 if (!gj) return null;
62956 if (gj.type === 'FeatureCollection') {
62957 for (var i = 0; i < gj.features.length; i++) {
62958 ensureFeatureID(gj.features[i]);
62961 ensureFeatureID(gj);
62965 } // ensure that each single Feature object has a unique ID
62968 function ensureFeatureID(feature) {
62969 if (!feature) return;
62970 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
62972 } // Prefer an array of Features instead of a FeatureCollection
62975 function getFeatures(gj) {
62976 if (!gj) return [];
62978 if (gj.type === 'FeatureCollection') {
62979 return gj.features;
62985 function featureKey(d) {
62986 return d.__featurehash__;
62989 function isPolygon(d) {
62990 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
62993 function clipPathID(d) {
62994 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
62997 function featureClasses(d) {
62998 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
63001 function drawData(selection) {
63002 var vtService = getService();
63003 var getPath = svgPath(projection).geojson;
63004 var getAreaPath = svgPath(projection, null, true).geojson;
63005 var hasData = drawData.hasData();
63006 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
63007 layer.exit().remove();
63008 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
63009 var surface = context.surface();
63010 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
63013 var geoData, polygonData;
63015 if (_template && vtService) {
63016 // fetch data from vector tile service
63017 var sourceID = _template;
63018 vtService.loadTiles(sourceID, _template, projection);
63019 geoData = vtService.data(sourceID, projection);
63021 geoData = getFeatures(_geojson);
63024 geoData = geoData.filter(getPath);
63025 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
63027 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
63028 clipPaths.exit().remove();
63029 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
63030 clipPathsEnter.append('path');
63031 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
63033 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
63034 datagroups = datagroups.enter().append('g').attr('class', function (d) {
63035 return 'datagroup datagroup-' + d;
63036 }).merge(datagroups); // Draw paths
63043 var paths = datagroups.selectAll('path').data(function (layer) {
63044 return pathData[layer];
63045 }, featureKey); // exit
63047 paths.exit().remove(); // enter/update
63049 paths = paths.enter().append('path').attr('class', function (d) {
63050 var datagroup = this.parentNode.__data__;
63051 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
63052 }).attr('clip-path', function (d) {
63053 var datagroup = this.parentNode.__data__;
63054 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
63055 }).merge(paths).attr('d', function (d) {
63056 var datagroup = this.parentNode.__data__;
63057 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
63060 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
63062 function drawLabels(selection, textClass, data) {
63063 var labelPath = d3_geoPath(projection);
63064 var labelData = data.filter(function (d) {
63065 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
63067 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
63069 labels.exit().remove(); // enter/update
63071 labels = labels.enter().append('text').attr('class', function (d) {
63072 return textClass + ' ' + featureClasses(d);
63073 }).merge(labels).text(function (d) {
63074 return d.properties.desc || d.properties.name;
63075 }).attr('x', function (d) {
63076 var centroid = labelPath.centroid(d);
63077 return centroid[0] + 11;
63078 }).attr('y', function (d) {
63079 var centroid = labelPath.centroid(d);
63080 return centroid[1];
63085 function getExtension(fileName) {
63086 if (!fileName) return;
63087 var re = /\.(gpx|kml|(geo)?json)$/i;
63088 var match = fileName.toLowerCase().match(re);
63089 return match && match.length && match[0];
63092 function xmlToDom(textdata) {
63093 return new DOMParser().parseFromString(textdata, 'text/xml');
63096 drawData.setFile = function (extension, data) {
63103 switch (extension) {
63105 gj = gpx(xmlToDom(data));
63109 gj = kml(xmlToDom(data));
63114 gj = JSON.parse(data);
63120 if (Object.keys(gj).length) {
63121 _geojson = ensureIDs(gj);
63122 _src = extension + ' data file';
63126 dispatch.call('change');
63130 drawData.showLabels = function (val) {
63131 if (!arguments.length) return _showLabels;
63136 drawData.enabled = function (val) {
63137 if (!arguments.length) return _enabled;
63146 dispatch.call('change');
63150 drawData.hasData = function () {
63151 var gj = _geojson || {};
63152 return !!(_template || Object.keys(gj).length);
63155 drawData.template = function (val, src) {
63156 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
63158 var osm = context.connection();
63161 var blocklists = osm.imageryBlocklists();
63166 for (var i = 0; i < blocklists.length; i++) {
63167 regex = blocklists[i];
63168 fail = regex.test(val);
63171 } // ensure at least one test was run.
63175 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
63176 fail = regex.test(val);
63182 _geojson = null; // strip off the querystring/hash from the template,
63183 // it often includes the access token
63185 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
63186 dispatch.call('change');
63190 drawData.geojson = function (gj, src) {
63191 if (!arguments.length) return _geojson;
63198 if (Object.keys(gj).length) {
63199 _geojson = ensureIDs(gj);
63200 _src = src || 'unknown.geojson';
63203 dispatch.call('change');
63207 drawData.fileList = function (fileList) {
63208 if (!arguments.length) return _fileList;
63210 _fileList = fileList;
63213 if (!fileList || !fileList.length) return this;
63214 var f = fileList[0];
63215 var extension = getExtension(f.name);
63216 var reader = new FileReader();
63218 reader.onload = function () {
63219 return function (e) {
63220 drawData.setFile(extension, e.target.result);
63224 reader.readAsText(f);
63228 drawData.url = function (url, defaultExtension) {
63232 _src = null; // strip off any querystring/hash from the url before checking extension
63234 var testUrl = url.split(/[?#]/)[0];
63235 var extension = getExtension(testUrl) || defaultExtension;
63239 d3_text(url).then(function (data) {
63240 drawData.setFile(extension, data);
63241 })["catch"](function () {
63245 drawData.template(url);
63251 drawData.getSrc = function () {
63255 drawData.fitZoom = function () {
63256 var features = getFeatures(_geojson);
63257 if (!features.length) return;
63258 var map = context.map();
63259 var viewport = map.trimmedExtent().polygon();
63260 var coords = features.reduce(function (coords, feature) {
63261 var geom = feature.geometry;
63262 if (!geom) return coords;
63263 var c = geom.coordinates;
63264 /* eslint-disable no-fallthrough */
63266 switch (geom.type) {
63274 case 'MultiPolygon':
63275 c = utilArrayFlatten(c);
63278 case 'MultiLineString':
63279 c = utilArrayFlatten(c);
63282 /* eslint-enable no-fallthrough */
63285 return utilArrayUnion(coords, c);
63288 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
63289 var extent = geoExtent(d3_geoBounds({
63290 type: 'LineString',
63291 coordinates: coords
63293 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
63303 function svgDebug(projection, context) {
63304 function drawDebug(selection) {
63305 var showTile = context.getDebug('tile');
63306 var showCollision = context.getDebug('collision');
63307 var showImagery = context.getDebug('imagery');
63308 var showTouchTargets = context.getDebug('target');
63309 var showDownloaded = context.getDebug('downloaded');
63310 var debugData = [];
63319 if (showCollision) {
63333 if (showTouchTargets) {
63336 label: 'touchTargets'
63340 if (showDownloaded) {
63343 label: 'downloaded'
63347 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
63348 legend.exit().remove();
63349 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
63350 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
63353 legendItems.exit().remove();
63354 legendItems.enter().append('span').attr('class', function (d) {
63355 return "debug-legend-item ".concat(d["class"]);
63356 }).text(function (d) {
63359 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
63360 layer.exit().remove();
63361 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
63363 var extent = context.map().extent();
63364 _mainFileFetcher.get('imagery').then(function (d) {
63365 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
63366 var features = hits.map(function (d) {
63367 return d.features[d.id];
63369 var imagery = layer.selectAll('path.debug-imagery').data(features);
63370 imagery.exit().remove();
63371 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
63372 })["catch"](function () {
63376 var osm = context.connection();
63377 var dataDownloaded = [];
63379 if (osm && showDownloaded) {
63380 var rtree = osm.caches('get').tile.rtree;
63381 dataDownloaded = rtree.all().map(function (bbox) {
63389 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
63395 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
63396 downloaded.exit().remove();
63397 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
63399 layer.selectAll('path').attr('d', svgPath(projection).geojson);
63400 } // This looks strange because `enabled` methods on other layers are
63401 // chainable getter/setters, and this one is just a getter.
63404 drawDebug.enabled = function () {
63405 if (!arguments.length) {
63406 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
63416 A standalone SVG element that contains only a `defs` sub-element. To be
63417 used once globally, since defs IDs must be unique within a document.
63420 function svgDefs(context) {
63421 var _defsSelection = select(null);
63423 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
63425 function drawDefs(selection) {
63426 _defsSelection = selection.append('defs'); // add markers
63428 _defsSelection.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
63429 // (they can't inherit it from the line they're attached to),
63430 // so we need to manually define markers for each color of tag
63431 // (also, it's slightly nicer if we can control the
63432 // positioning for different tags)
63435 function addSidedMarker(name, color, offset) {
63436 _defsSelection.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);
63439 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
63440 // the water side, so let's color them blue (with a gap) to
63441 // give a stronger indication
63443 addSidedMarker('coastline', '#77dede', 1);
63444 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
63445 // from the line visually suits that
63447 addSidedMarker('barrier', '#ddd', 1);
63448 addSidedMarker('man_made', '#fff', 0);
63450 _defsSelection.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');
63452 _defsSelection.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
63455 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
63456 ['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) {
63457 return 'ideditor-pattern-' + d[0];
63458 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
63460 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
63461 return 'pattern-color-' + d[0];
63463 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
63464 return context.imagePath('pattern/' + d[1] + '.png');
63465 }); // add clip paths
63467 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
63468 return 'ideditor-clip-square-' + d;
63469 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
63471 }).attr('height', function (d) {
63473 }); // add symbol spritesheets
63476 addSprites(_spritesheetIds, true);
63479 function addSprites(ids, overrideColors) {
63480 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
63482 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
63484 spritesheets.enter().append('g').attr('class', function (d) {
63485 return 'spritesheet spritesheet-' + d;
63486 }).each(function (d) {
63487 var url = context.imagePath(d + '.svg');
63488 var node = select(this).node();
63489 svg(url).then(function (svg) {
63490 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
63492 if (overrideColors && d !== 'iD-sprite') {
63493 // allow icon colors to be overridden..
63494 select(node).selectAll('path').attr('fill', 'currentColor');
63496 })["catch"](function () {
63500 spritesheets.exit().remove();
63503 drawDefs.addSprites = addSprites;
63507 var _layerEnabled$2 = false;
63511 function svgKeepRight(projection, context, dispatch) {
63512 var throttledRedraw = throttle(function () {
63513 return dispatch.call('change');
63517 var touchLayer = select(null);
63518 var drawLayer = select(null);
63519 var layerVisible = false;
63521 function markerPath(selection, klass) {
63522 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');
63523 } // Loosely-coupled keepRight service for fetching issues.
63526 function getService() {
63527 if (services.keepRight && !_qaService$2) {
63528 _qaService$2 = services.keepRight;
63530 _qaService$2.on('loaded', throttledRedraw);
63531 } else if (!services.keepRight && _qaService$2) {
63532 _qaService$2 = null;
63535 return _qaService$2;
63536 } // Show the markers
63539 function editOn() {
63540 if (!layerVisible) {
63541 layerVisible = true;
63542 drawLayer.style('display', 'block');
63544 } // Immediately remove the markers and their touch targets
63547 function editOff() {
63548 if (layerVisible) {
63549 layerVisible = false;
63550 drawLayer.style('display', 'none');
63551 drawLayer.selectAll('.qaItem.keepRight').remove();
63552 touchLayer.selectAll('.qaItem.keepRight').remove();
63554 } // Enable the layer. This shows the markers and transitions them to visible.
63557 function layerOn() {
63559 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
63560 return dispatch.call('change');
63562 } // Disable the layer. This transitions the layer invisible and then hides the markers.
63565 function layerOff() {
63566 throttledRedraw.cancel();
63567 drawLayer.interrupt();
63568 touchLayer.selectAll('.qaItem.keepRight').remove();
63569 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
63571 dispatch.call('change');
63573 } // Update the issue markers
63576 function updateMarkers() {
63577 if (!layerVisible || !_layerEnabled$2) return;
63578 var service = getService();
63579 var selectedID = context.selectedErrorID();
63580 var data = service ? service.getItems(projection) : [];
63581 var getTransform = svgPointTransform(projection); // Draw markers..
63583 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
63587 markers.exit().remove(); // enter
63589 var markersEnter = markers.enter().append('g').attr('class', function (d) {
63590 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
63592 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
63593 markersEnter.append('path').call(markerPath, 'shadow');
63594 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
63596 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
63597 return d.id === selectedID;
63598 }).attr('transform', getTransform); // Draw targets..
63600 if (touchLayer.empty()) return;
63601 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
63602 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
63606 targets.exit().remove(); // enter/update
63608 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
63609 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
63610 }).attr('transform', getTransform);
63612 function sortY(a, b) {
63613 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];
63615 } // Draw the keepRight layer and schedule loading issues and updating markers.
63618 function drawKeepRight(selection) {
63619 var service = getService();
63620 var surface = context.surface();
63622 if (surface && !surface.empty()) {
63623 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
63626 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
63627 drawLayer.exit().remove();
63628 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
63630 if (_layerEnabled$2) {
63631 if (service && ~~context.map().zoom() >= minZoom) {
63633 service.loadIssues(projection);
63639 } // Toggles the layer on and off
63642 drawKeepRight.enabled = function (val) {
63643 if (!arguments.length) return _layerEnabled$2;
63644 _layerEnabled$2 = val;
63646 if (_layerEnabled$2) {
63651 if (context.selectedErrorID()) {
63652 context.enter(modeBrowse(context));
63656 dispatch.call('change');
63660 drawKeepRight.supported = function () {
63661 return !!getService();
63664 return drawKeepRight;
63667 function svgGeolocate(projection) {
63668 var layer = select(null);
63673 if (svgGeolocate.initialized) return; // run once
63675 svgGeolocate.enabled = false;
63676 svgGeolocate.initialized = true;
63679 function showLayer() {
63680 layer.style('display', 'block');
63683 function hideLayer() {
63684 layer.transition().duration(250).style('opacity', 0);
63687 function layerOn() {
63688 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
63691 function layerOff() {
63692 layer.style('display', 'none');
63695 function transform(d) {
63696 return svgPointTransform(projection)(d);
63699 function accuracy(accuracy, loc) {
63700 // converts accuracy to pixels...
63701 var degreesRadius = geoMetersToLat(accuracy),
63702 tangentLoc = [loc[0], loc[1] + degreesRadius],
63703 projectedTangent = projection(tangentLoc),
63704 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
63706 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
63709 function update() {
63710 var geolocation = {
63711 loc: [_position.coords.longitude, _position.coords.latitude]
63713 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
63714 groups.exit().remove();
63715 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
63716 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');
63717 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');
63718 groups.merge(pointsEnter).attr('transform', transform);
63719 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
63722 function drawLocation(selection) {
63723 var enabled = svgGeolocate.enabled;
63724 layer = selection.selectAll('.layer-geolocate').data([0]);
63725 layer.exit().remove();
63726 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
63727 layerEnter.append('g').attr('class', 'geolocations');
63728 layer = layerEnter.merge(layer);
63737 drawLocation.enabled = function (position, enabled) {
63738 if (!arguments.length) return svgGeolocate.enabled;
63739 _position = position;
63740 svgGeolocate.enabled = enabled;
63742 if (svgGeolocate.enabled) {
63753 return drawLocation;
63756 function svgLabels(projection, context) {
63757 var path = d3_geoPath(projection);
63758 var detected = utilDetect();
63759 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
63761 var _rdrawn = new RBush();
63763 var _rskipped = new RBush();
63765 var _textWidthCache = {};
63766 var _entitybboxes = {}; // Listed from highest to lowest priority
63768 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]];
63770 function shouldSkipIcon(preset) {
63771 var noIcons = ['building', 'landuse', 'natural'];
63772 return noIcons.some(function (s) {
63773 return preset.id.indexOf(s) >= 0;
63777 function get(array, prop) {
63778 return function (d, i) {
63779 return array[i][prop];
63783 function textWidth(text, size, elem) {
63784 var c = _textWidthCache[size];
63785 if (!c) c = _textWidthCache[size] = {};
63790 c[text] = elem.getComputedTextLength();
63793 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
63795 if (str === null) {
63796 return size / 3 * 2 * text.length;
63798 return size / 3 * (2 * text.length + str.length);
63803 function drawLinePaths(selection, entities, filter, classes, labels) {
63804 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
63806 paths.exit().remove(); // enter/update
63808 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
63809 return 'ideditor-labelpath-' + d.id;
63810 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
63813 function drawLineLabels(selection, entities, filter, classes, labels) {
63814 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63816 texts.exit().remove(); // enter
63818 texts.enter().append('text').attr('class', function (d, i) {
63819 return classes + ' ' + labels[i].classes + ' ' + d.id;
63820 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
63822 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
63823 return '#ideditor-labelpath-' + d.id;
63824 }).text(utilDisplayNameForPath);
63827 function drawPointLabels(selection, entities, filter, classes, labels) {
63828 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63830 texts.exit().remove(); // enter/update
63832 texts.enter().append('text').attr('class', function (d, i) {
63833 return classes + ' ' + labels[i].classes + ' ' + d.id;
63834 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
63835 textWidth(utilDisplayName(d), labels[i].height, this);
63839 function drawAreaLabels(selection, entities, filter, classes, labels) {
63840 entities = entities.filter(hasText);
63841 labels = labels.filter(hasText);
63842 drawPointLabels(selection, entities, filter, classes, labels);
63844 function hasText(d, i) {
63845 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
63849 function drawAreaIcons(selection, entities, filter, classes, labels) {
63850 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
63852 icons.exit().remove(); // enter/update
63854 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) {
63855 var preset = _mainPresetIndex.match(d, context.graph());
63856 var picon = preset && preset.icon;
63861 var isMaki = /^maki-/.test(picon);
63862 return '#' + picon + (isMaki ? '-15' : '');
63867 function drawCollisionBoxes(selection, rtree, which) {
63868 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
63871 if (context.getDebug('collision')) {
63872 gj = rtree.all().map(function (d) {
63875 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
63880 var boxes = selection.selectAll('.' + which).data(gj); // exit
63882 boxes.exit().remove(); // enter/update
63884 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
63887 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
63888 var wireframe = context.surface().classed('fill-wireframe');
63889 var zoom = geoScaleToZoom(projection.scale());
63890 var labelable = [];
63891 var renderNodeAs = {};
63892 var i, j, k, entity, geometry;
63894 for (i = 0; i < labelStack.length; i++) {
63895 labelable.push([]);
63903 _entitybboxes = {};
63905 for (i = 0; i < entities.length; i++) {
63906 entity = entities[i];
63907 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
63909 for (j = 0; j < toRemove.length; j++) {
63910 _rdrawn.remove(toRemove[j]);
63912 _rskipped.remove(toRemove[j]);
63915 } // Loop through all the entities to do some preprocessing
63918 for (i = 0; i < entities.length; i++) {
63919 entity = entities[i];
63920 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
63922 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
63923 var hasDirections = entity.directions(graph, projection).length;
63926 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
63927 renderNodeAs[entity.id] = 'point';
63928 markerPadding = 20; // extra y for marker height
63930 renderNodeAs[entity.id] = 'vertex';
63934 var coord = projection(entity.loc);
63935 var nodePadding = 10;
63937 minX: coord[0] - nodePadding,
63938 minY: coord[1] - nodePadding - markerPadding,
63939 maxX: coord[0] + nodePadding,
63940 maxY: coord[1] + nodePadding
63942 doInsert(bbox, entity.id + 'P');
63943 } // From here on, treat vertices like points
63946 if (geometry === 'vertex') {
63947 geometry = 'point';
63948 } // Determine which entities are label-able
63951 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
63952 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
63953 if (!icon && !utilDisplayName(entity)) continue;
63955 for (k = 0; k < labelStack.length; k++) {
63956 var matchGeom = labelStack[k][0];
63957 var matchKey = labelStack[k][1];
63958 var matchVal = labelStack[k][2];
63959 var hasVal = entity.tags[matchKey];
63961 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
63962 labelable[k].push(entity);
63977 }; // Try and find a valid label for labellable entities
63979 for (k = 0; k < labelable.length; k++) {
63980 var fontSize = labelStack[k][3];
63982 for (i = 0; i < labelable[k].length; i++) {
63983 entity = labelable[k][i];
63984 geometry = entity.geometry(graph);
63985 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
63986 var name = getName(entity);
63987 var width = name && textWidth(name, fontSize);
63990 if (geometry === 'point' || geometry === 'vertex') {
63991 // no point or vertex labels in wireframe mode
63992 // no vertex labels at low zooms (vertices have no icons)
63993 if (wireframe) continue;
63994 var renderAs = renderNodeAs[entity.id];
63995 if (renderAs === 'vertex' && zoom < 17) continue;
63996 p = getPointLabel(entity, width, fontSize, renderAs);
63997 } else if (geometry === 'line') {
63998 p = getLineLabel(entity, width, fontSize);
63999 } else if (geometry === 'area') {
64000 p = getAreaLabel(entity, width, fontSize);
64004 if (geometry === 'vertex') {
64005 geometry = 'point';
64006 } // treat vertex like point
64009 p.classes = geometry + ' tag-' + labelStack[k][1];
64010 positions[geometry].push(p);
64011 labelled[geometry].push(entity);
64016 function isInterestingVertex(entity) {
64017 var selectedIDs = context.selectedIDs();
64018 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
64019 return selectedIDs.indexOf(parent.id) !== -1;
64023 function getPointLabel(entity, width, height, geometry) {
64024 var y = geometry === 'point' ? -12 : 0;
64025 var pointOffsets = {
64026 ltr: [15, y, 'start'],
64027 rtl: [-15, y, 'end']
64029 var textDirection = _mainLocalizer.textDirection();
64030 var coord = projection(entity.loc);
64031 var textPadding = 2;
64032 var offset = pointOffsets[textDirection];
64036 x: coord[0] + offset[0],
64037 y: coord[1] + offset[1],
64038 textAnchor: offset[2]
64039 }; // insert a collision box for the text label..
64043 if (textDirection === 'rtl') {
64045 minX: p.x - width - textPadding,
64046 minY: p.y - height / 2 - textPadding,
64047 maxX: p.x + textPadding,
64048 maxY: p.y + height / 2 + textPadding
64052 minX: p.x - textPadding,
64053 minY: p.y - height / 2 - textPadding,
64054 maxX: p.x + width + textPadding,
64055 maxY: p.y + height / 2 + textPadding
64059 if (tryInsert([bbox], entity.id, true)) {
64064 function getLineLabel(entity, width, height) {
64065 var viewport = geoExtent(context.projection.clipExtent()).polygon();
64066 var points = graph.childNodes(entity).map(function (node) {
64067 return projection(node.loc);
64069 var length = geoPathLength(points);
64070 if (length < width + 20) return; // % along the line to attempt to place the label
64072 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
64075 for (var i = 0; i < lineOffsets.length; i++) {
64076 var offset = lineOffsets[i];
64077 var middle = offset / 100 * length;
64078 var start = middle - width / 2;
64079 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
64081 var sub = subpath(points, start, start + width);
64083 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
64087 var isReverse = reverse(sub);
64090 sub = sub.reverse();
64094 var boxsize = (height + 2) / 2;
64096 for (var j = 0; j < sub.length - 1; j++) {
64098 var b = sub[j + 1]; // split up the text into small collision boxes
64100 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
64102 for (var box = 0; box < num; box++) {
64103 var p = geoVecInterp(a, b, box / num);
64104 var x0 = p[0] - boxsize - padding;
64105 var y0 = p[1] - boxsize - padding;
64106 var x1 = p[0] + boxsize + padding;
64107 var y1 = p[1] + boxsize + padding;
64109 minX: Math.min(x0, x1),
64110 minY: Math.min(y0, y1),
64111 maxX: Math.max(x0, x1),
64112 maxY: Math.max(y0, y1)
64117 if (tryInsert(bboxes, entity.id, false)) {
64120 'font-size': height + 2,
64121 lineString: lineString(sub),
64122 startOffset: offset + '%'
64127 function reverse(p) {
64128 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
64129 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
64132 function lineString(points) {
64133 return 'M' + points.join('L');
64136 function subpath(points, from, to) {
64138 var start, end, i0, i1;
64140 for (var i = 0; i < points.length - 1; i++) {
64142 var b = points[i + 1];
64143 var current = geoVecLength(a, b);
64146 if (!start && sofar + current >= from) {
64147 portion = (from - sofar) / current;
64148 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
64152 if (!end && sofar + current >= to) {
64153 portion = (to - sofar) / current;
64154 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
64161 var result = points.slice(i0, i1);
64162 result.unshift(start);
64168 function getAreaLabel(entity, width, height) {
64169 var centroid = path.centroid(entity.asGeoJSON(graph));
64170 var extent = entity.extent(graph);
64171 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
64172 if (isNaN(centroid[0]) || areaWidth < 20) return;
64173 var preset = _mainPresetIndex.match(entity, context.graph());
64174 var picon = preset && preset.icon;
64180 // icon and label..
64182 addLabel(iconSize + padding);
64192 function addIcon() {
64193 var iconX = centroid[0] - iconSize / 2;
64194 var iconY = centroid[1] - iconSize / 2;
64198 maxX: iconX + iconSize,
64199 maxY: iconY + iconSize
64202 if (tryInsert([bbox], entity.id + 'I', true)) {
64203 p.transform = 'translate(' + iconX + ',' + iconY + ')';
64210 function addLabel(yOffset) {
64211 if (width && areaWidth >= width + 20) {
64212 var labelX = centroid[0];
64213 var labelY = centroid[1] + yOffset;
64215 minX: labelX - width / 2 - padding,
64216 minY: labelY - height / 2 - padding,
64217 maxX: labelX + width / 2 + padding,
64218 maxY: labelY + height / 2 + padding
64221 if (tryInsert([bbox], entity.id, true)) {
64224 p.textAnchor = 'middle';
64232 } // force insert a singular bounding box
64233 // singular box only, no array, id better be unique
64236 function doInsert(bbox, id) {
64238 var oldbox = _entitybboxes[id];
64241 _rdrawn.remove(oldbox);
64244 _entitybboxes[id] = bbox;
64246 _rdrawn.insert(bbox);
64249 function tryInsert(bboxes, id, saveSkipped) {
64250 var skipped = false;
64252 for (var i = 0; i < bboxes.length; i++) {
64253 var bbox = bboxes[i];
64254 bbox.id = id; // Check that label is visible
64256 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
64261 if (_rdrawn.collides(bbox)) {
64267 _entitybboxes[id] = bboxes;
64271 _rskipped.load(bboxes);
64274 _rdrawn.load(bboxes);
64280 var layer = selection.selectAll('.layer-osm.labels');
64281 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
64282 return 'labels-group ' + d;
64284 var halo = layer.selectAll('.labels-group.halo');
64285 var label = layer.selectAll('.labels-group.label');
64286 var debug = layer.selectAll('.labels-group.debug'); // points
64288 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
64289 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
64291 drawLinePaths(layer, labelled.line, filter, '', positions.line);
64292 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
64293 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
64295 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
64296 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
64297 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
64298 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
64300 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
64301 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
64302 layer.call(filterLabels);
64305 function filterLabels(selection) {
64306 var drawLayer = selection.selectAll('.layer-osm.labels');
64307 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
64308 layers.selectAll('.nolabel').classed('nolabel', false);
64309 var mouse = context.map().mouse();
64310 var graph = context.graph();
64311 var selectedIDs = context.selectedIDs();
64313 var pad, bbox; // hide labels near the mouse
64318 minX: mouse[0] - pad,
64319 minY: mouse[1] - pad,
64320 maxX: mouse[0] + pad,
64321 maxY: mouse[1] + pad
64324 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
64328 ids.push.apply(ids, nearMouse);
64329 } // hide labels on selected nodes (they look weird when dragging / haloed)
64332 for (var i = 0; i < selectedIDs.length; i++) {
64333 var entity = graph.hasEntity(selectedIDs[i]);
64335 if (entity && entity.type === 'node') {
64336 ids.push(selectedIDs[i]);
64340 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
64342 var debug = selection.selectAll('.labels-group.debug');
64345 if (context.getDebug('collision')) {
64348 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
64352 var box = debug.selectAll('.debug-mouse').data(gj); // exit
64354 box.exit().remove(); // enter/update
64356 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
64359 var throttleFilterLabels = throttle(filterLabels, 100);
64361 drawLabels.observe = function (selection) {
64362 var listener = function listener() {
64363 throttleFilterLabels(selection);
64366 selection.on('mousemove.hidelabels', listener);
64367 context.on('enter.hidelabels', listener);
64370 drawLabels.off = function (selection) {
64371 throttleFilterLabels.cancel();
64372 selection.on('mousemove.hidelabels', null);
64373 context.on('enter.hidelabels', null);
64379 var _layerEnabled$1 = false;
64383 function svgImproveOSM(projection, context, dispatch) {
64384 var throttledRedraw = throttle(function () {
64385 return dispatch.call('change');
64389 var touchLayer = select(null);
64390 var drawLayer = select(null);
64391 var layerVisible = false;
64393 function markerPath(selection, klass) {
64394 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');
64395 } // Loosely-coupled improveOSM service for fetching issues
64398 function getService() {
64399 if (services.improveOSM && !_qaService$1) {
64400 _qaService$1 = services.improveOSM;
64402 _qaService$1.on('loaded', throttledRedraw);
64403 } else if (!services.improveOSM && _qaService$1) {
64404 _qaService$1 = null;
64407 return _qaService$1;
64408 } // Show the markers
64411 function editOn() {
64412 if (!layerVisible) {
64413 layerVisible = true;
64414 drawLayer.style('display', 'block');
64416 } // Immediately remove the markers and their touch targets
64419 function editOff() {
64420 if (layerVisible) {
64421 layerVisible = false;
64422 drawLayer.style('display', 'none');
64423 drawLayer.selectAll('.qaItem.improveOSM').remove();
64424 touchLayer.selectAll('.qaItem.improveOSM').remove();
64426 } // Enable the layer. This shows the markers and transitions them to visible.
64429 function layerOn() {
64431 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
64432 return dispatch.call('change');
64434 } // Disable the layer. This transitions the layer invisible and then hides the markers.
64437 function layerOff() {
64438 throttledRedraw.cancel();
64439 drawLayer.interrupt();
64440 touchLayer.selectAll('.qaItem.improveOSM').remove();
64441 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
64443 dispatch.call('change');
64445 } // Update the issue markers
64448 function updateMarkers() {
64449 if (!layerVisible || !_layerEnabled$1) return;
64450 var service = getService();
64451 var selectedID = context.selectedErrorID();
64452 var data = service ? service.getItems(projection) : [];
64453 var getTransform = svgPointTransform(projection); // Draw markers..
64455 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
64459 markers.exit().remove(); // enter
64461 var markersEnter = markers.enter().append('g').attr('class', function (d) {
64462 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
64464 markersEnter.append('polygon').call(markerPath, 'shadow');
64465 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
64466 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
64467 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
64468 var picon = d.icon;
64473 var isMaki = /^maki-/.test(picon);
64474 return "#".concat(picon).concat(isMaki ? '-11' : '');
64478 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
64479 return d.id === selectedID;
64480 }).attr('transform', getTransform); // Draw targets..
64482 if (touchLayer.empty()) return;
64483 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64484 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
64488 targets.exit().remove(); // enter/update
64490 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
64491 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
64492 }).attr('transform', getTransform);
64494 function sortY(a, b) {
64495 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
64497 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
64500 function drawImproveOSM(selection) {
64501 var service = getService();
64502 var surface = context.surface();
64504 if (surface && !surface.empty()) {
64505 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
64508 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
64509 drawLayer.exit().remove();
64510 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
64512 if (_layerEnabled$1) {
64513 if (service && ~~context.map().zoom() >= minZoom) {
64515 service.loadIssues(projection);
64521 } // Toggles the layer on and off
64524 drawImproveOSM.enabled = function (val) {
64525 if (!arguments.length) return _layerEnabled$1;
64526 _layerEnabled$1 = val;
64528 if (_layerEnabled$1) {
64533 if (context.selectedErrorID()) {
64534 context.enter(modeBrowse(context));
64538 dispatch.call('change');
64542 drawImproveOSM.supported = function () {
64543 return !!getService();
64546 return drawImproveOSM;
64549 var _layerEnabled = false;
64553 function svgOsmose(projection, context, dispatch) {
64554 var throttledRedraw = throttle(function () {
64555 return dispatch.call('change');
64559 var touchLayer = select(null);
64560 var drawLayer = select(null);
64561 var layerVisible = false;
64563 function markerPath(selection, klass) {
64564 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');
64565 } // Loosely-coupled osmose service for fetching issues
64568 function getService() {
64569 if (services.osmose && !_qaService) {
64570 _qaService = services.osmose;
64572 _qaService.on('loaded', throttledRedraw);
64573 } else if (!services.osmose && _qaService) {
64578 } // Show the markers
64581 function editOn() {
64582 if (!layerVisible) {
64583 layerVisible = true;
64584 drawLayer.style('display', 'block');
64586 } // Immediately remove the markers and their touch targets
64589 function editOff() {
64590 if (layerVisible) {
64591 layerVisible = false;
64592 drawLayer.style('display', 'none');
64593 drawLayer.selectAll('.qaItem.osmose').remove();
64594 touchLayer.selectAll('.qaItem.osmose').remove();
64596 } // Enable the layer. This shows the markers and transitions them to visible.
64599 function layerOn() {
64601 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
64602 return dispatch.call('change');
64604 } // Disable the layer. This transitions the layer invisible and then hides the markers.
64607 function layerOff() {
64608 throttledRedraw.cancel();
64609 drawLayer.interrupt();
64610 touchLayer.selectAll('.qaItem.osmose').remove();
64611 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
64613 dispatch.call('change');
64615 } // Update the issue markers
64618 function updateMarkers() {
64619 if (!layerVisible || !_layerEnabled) return;
64620 var service = getService();
64621 var selectedID = context.selectedErrorID();
64622 var data = service ? service.getItems(projection) : [];
64623 var getTransform = svgPointTransform(projection); // Draw markers..
64625 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
64629 markers.exit().remove(); // enter
64631 var markersEnter = markers.enter().append('g').attr('class', function (d) {
64632 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
64634 markersEnter.append('polygon').call(markerPath, 'shadow');
64635 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
64636 markersEnter.append('polygon').attr('fill', function (d) {
64637 return service.getColor(d.item);
64638 }).call(markerPath, 'qaItem-fill');
64639 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
64640 var picon = d.icon;
64645 var isMaki = /^maki-/.test(picon);
64646 return "#".concat(picon).concat(isMaki ? '-11' : '');
64650 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
64651 return d.id === selectedID;
64652 }).attr('transform', getTransform); // Draw targets..
64654 if (touchLayer.empty()) return;
64655 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
64656 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
64660 targets.exit().remove(); // enter/update
64662 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
64663 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
64664 }).attr('transform', getTransform);
64666 function sortY(a, b) {
64667 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
64669 } // Draw the Osmose layer and schedule loading issues and updating markers.
64672 function drawOsmose(selection) {
64673 var service = getService();
64674 var surface = context.surface();
64676 if (surface && !surface.empty()) {
64677 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
64680 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
64681 drawLayer.exit().remove();
64682 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
64684 if (_layerEnabled) {
64685 if (service && ~~context.map().zoom() >= minZoom) {
64687 service.loadIssues(projection);
64693 } // Toggles the layer on and off
64696 drawOsmose.enabled = function (val) {
64697 if (!arguments.length) return _layerEnabled;
64698 _layerEnabled = val;
64700 if (_layerEnabled) {
64701 // Strings supplied by Osmose fetched before showing layer for first time
64702 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
64703 // Also, If layer is toggled quickly multiple requests are sent
64704 getService().loadStrings().then(layerOn)["catch"](function (err) {
64705 console.log(err); // eslint-disable-line no-console
64710 if (context.selectedErrorID()) {
64711 context.enter(modeBrowse(context));
64715 dispatch.call('change');
64719 drawOsmose.supported = function () {
64720 return !!getService();
64726 function svgStreetside(projection, context, dispatch) {
64727 var throttledRedraw = throttle(function () {
64728 dispatch.call('change');
64732 var minMarkerZoom = 16;
64733 var minViewfieldZoom = 18;
64734 var layer = select(null);
64735 var _viewerYaw = 0;
64736 var _selectedSequence = null;
64745 if (svgStreetside.initialized) return; // run once
64747 svgStreetside.enabled = false;
64748 svgStreetside.initialized = true;
64755 function getService() {
64756 if (services.streetside && !_streetside) {
64757 _streetside = services.streetside;
64759 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
64760 } else if (!services.streetside && _streetside) {
64761 _streetside = null;
64764 return _streetside;
64771 function showLayer() {
64772 var service = getService();
64773 if (!service) return;
64775 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
64776 dispatch.call('change');
64784 function hideLayer() {
64785 throttledRedraw.cancel();
64786 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
64793 function editOn() {
64794 layer.style('display', 'block');
64801 function editOff() {
64802 layer.selectAll('.viewfield-group').remove();
64803 layer.style('display', 'none');
64806 * click() Handles 'bubble' point click event.
64810 function click(d3_event, d) {
64811 var service = getService();
64812 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
64814 if (d.sequenceKey !== _selectedSequence) {
64815 _viewerYaw = 0; // reset
64818 _selectedSequence = d.sequenceKey;
64819 service.ensureViewerLoaded(context).then(function () {
64820 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
64822 context.map().centerEase(d.loc);
64829 function mouseover(d3_event, d) {
64830 var service = getService();
64831 if (service) service.setStyles(context, d);
64838 function mouseout() {
64839 var service = getService();
64840 if (service) service.setStyles(context, null);
64847 function transform(d) {
64848 var t = svgPointTransform(projection)(d);
64849 var rot = d.ca + _viewerYaw;
64852 t += ' rotate(' + Math.floor(rot) + ',0,0)';
64858 function viewerChanged() {
64859 var service = getService();
64860 if (!service) return;
64861 var viewer = service.viewer();
64862 if (!viewer) return; // update viewfield rotation
64864 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
64865 // e.g. during drags or easing.
64867 if (context.map().isTransformed()) return;
64868 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
64871 function filterBubbles(bubbles) {
64872 var fromDate = context.photos().fromDate();
64873 var toDate = context.photos().toDate();
64874 var usernames = context.photos().usernames();
64877 var fromTimestamp = new Date(fromDate).getTime();
64878 bubbles = bubbles.filter(function (bubble) {
64879 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
64884 var toTimestamp = new Date(toDate).getTime();
64885 bubbles = bubbles.filter(function (bubble) {
64886 return new Date(bubble.captured_at).getTime() <= toTimestamp;
64891 bubbles = bubbles.filter(function (bubble) {
64892 return usernames.indexOf(bubble.captured_by) !== -1;
64899 function filterSequences(sequences) {
64900 var fromDate = context.photos().fromDate();
64901 var toDate = context.photos().toDate();
64902 var usernames = context.photos().usernames();
64905 var fromTimestamp = new Date(fromDate).getTime();
64906 sequences = sequences.filter(function (sequences) {
64907 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
64912 var toTimestamp = new Date(toDate).getTime();
64913 sequences = sequences.filter(function (sequences) {
64914 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
64919 sequences = sequences.filter(function (sequences) {
64920 return usernames.indexOf(sequences.properties.captured_by) !== -1;
64931 function update() {
64932 var viewer = context.container().select('.photoviewer');
64933 var selected = viewer.empty() ? undefined : viewer.datum();
64934 var z = ~~context.map().zoom();
64935 var showMarkers = z >= minMarkerZoom;
64936 var showViewfields = z >= minViewfieldZoom;
64937 var service = getService();
64938 var sequences = [];
64941 if (context.photos().showsPanoramic()) {
64942 sequences = service ? service.sequences(projection) : [];
64943 bubbles = service && showMarkers ? service.bubbles(projection) : [];
64944 sequences = filterSequences(sequences);
64945 bubbles = filterBubbles(bubbles);
64948 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
64949 return d.properties.key;
64952 traces.exit().remove(); // enter/update
64954 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
64955 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
64956 // force reenter once bubbles are attached to a sequence
64957 return d.key + (d.sequenceKey ? 'v1' : 'v0');
64960 groups.exit().remove(); // enter
64962 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
64963 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
64965 var markers = groups.merge(groupsEnter).sort(function (a, b) {
64966 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
64967 }).attr('transform', transform).select('.viewfield-scale');
64968 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
64969 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
64970 viewfields.exit().remove(); // viewfields may or may not be drawn...
64971 // but if they are, draw below the circles
64973 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
64975 function viewfieldPath() {
64976 var d = this.parentNode.__data__;
64979 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
64981 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
64987 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
64988 * 'svgStreetside()' is called from index.js
64992 function drawImages(selection) {
64993 var enabled = svgStreetside.enabled;
64994 var service = getService();
64995 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
64996 layer.exit().remove();
64997 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
64998 layerEnter.append('g').attr('class', 'sequences');
64999 layerEnter.append('g').attr('class', 'markers');
65000 layer = layerEnter.merge(layer);
65003 if (service && ~~context.map().zoom() >= minZoom) {
65006 service.loadBubbles(projection);
65013 * drawImages.enabled().
65017 drawImages.enabled = function (_) {
65018 if (!arguments.length) return svgStreetside.enabled;
65019 svgStreetside.enabled = _;
65021 if (svgStreetside.enabled) {
65023 context.photos().on('change.streetside', update);
65026 context.photos().on('change.streetside', null);
65029 dispatch.call('change');
65033 * drawImages.supported().
65037 drawImages.supported = function () {
65038 return !!getService();
65045 function svgMapillaryImages(projection, context, dispatch) {
65046 var throttledRedraw = throttle(function () {
65047 dispatch.call('change');
65051 var minMarkerZoom = 16;
65052 var minViewfieldZoom = 18;
65053 var layer = select(null);
65058 if (svgMapillaryImages.initialized) return; // run once
65060 svgMapillaryImages.enabled = false;
65061 svgMapillaryImages.initialized = true;
65064 function getService() {
65065 if (services.mapillary && !_mapillary) {
65066 _mapillary = services.mapillary;
65068 _mapillary.event.on('loadedImages', throttledRedraw);
65069 } else if (!services.mapillary && _mapillary) {
65076 function showLayer() {
65077 var service = getService();
65078 if (!service) return;
65080 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
65081 dispatch.call('change');
65085 function hideLayer() {
65086 throttledRedraw.cancel();
65087 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
65090 function editOn() {
65091 layer.style('display', 'block');
65094 function editOff() {
65095 layer.selectAll('.viewfield-group').remove();
65096 layer.style('display', 'none');
65099 function click(d3_event, image) {
65100 var service = getService();
65101 if (!service) return;
65102 service.ensureViewerLoaded(context).then(function () {
65103 service.selectImage(context, image.id).showViewer(context);
65105 context.map().centerEase(image.loc);
65108 function mouseover(d3_event, image) {
65109 var service = getService();
65110 if (service) service.setStyles(context, image);
65113 function mouseout() {
65114 var service = getService();
65115 if (service) service.setStyles(context, null);
65118 function transform(d) {
65119 var t = svgPointTransform(projection)(d);
65122 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65128 function filterImages(images) {
65129 var showsPano = context.photos().showsPanoramic();
65130 var showsFlat = context.photos().showsFlat();
65131 var fromDate = context.photos().fromDate();
65132 var toDate = context.photos().toDate();
65134 if (!showsPano || !showsFlat) {
65135 images = images.filter(function (image) {
65136 if (image.is_pano) return showsPano;
65142 images = images.filter(function (image) {
65143 return new Date(image.captured_at).getTime() >= new Date(fromDate).getTime();
65148 images = images.filter(function (image) {
65149 return new Date(image.captured_at).getTime() <= new Date(toDate).getTime();
65156 function filterSequences(sequences) {
65157 var showsPano = context.photos().showsPanoramic();
65158 var showsFlat = context.photos().showsFlat();
65159 var fromDate = context.photos().fromDate();
65160 var toDate = context.photos().toDate();
65162 if (!showsPano || !showsFlat) {
65163 sequences = sequences.filter(function (sequence) {
65164 if (sequence.properties.hasOwnProperty('is_pano')) {
65165 if (sequence.properties.is_pano) return showsPano;
65174 sequences = sequences.filter(function (sequence) {
65175 return new Date(sequence.properties.captured_at).getTime() >= new Date(fromDate).getTime().toString();
65180 sequences = sequences.filter(function (sequence) {
65181 return new Date(sequence.properties.captured_at).getTime() <= new Date(toDate).getTime().toString();
65188 function update() {
65189 var z = ~~context.map().zoom();
65190 var showMarkers = z >= minMarkerZoom;
65191 var showViewfields = z >= minViewfieldZoom;
65192 var service = getService();
65193 var sequences = service ? service.sequences(projection) : [];
65194 var images = service && showMarkers ? service.images(projection) : [];
65195 images = filterImages(images);
65196 sequences = filterSequences(sequences);
65197 service.filterViewer(context);
65198 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
65199 return d.properties.id;
65202 traces.exit().remove(); // enter/update
65204 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
65205 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
65209 groups.exit().remove(); // enter
65211 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
65212 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65214 var markers = groups.merge(groupsEnter).sort(function (a, b) {
65215 return b.loc[1] - a.loc[1]; // sort Y
65216 }).attr('transform', transform).select('.viewfield-scale');
65217 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65218 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65219 viewfields.exit().remove();
65220 viewfields.enter() // viewfields may or may not be drawn...
65221 .insert('path', 'circle') // but if they are, draw below the circles
65222 .attr('class', 'viewfield').classed('pano', function () {
65223 return this.parentNode.__data__.is_pano;
65224 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
65226 function viewfieldPath() {
65227 if (this.parentNode.__data__.is_pano) {
65228 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
65230 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
65235 function drawImages(selection) {
65236 var enabled = svgMapillaryImages.enabled;
65237 var service = getService();
65238 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
65239 layer.exit().remove();
65240 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
65241 layerEnter.append('g').attr('class', 'sequences');
65242 layerEnter.append('g').attr('class', 'markers');
65243 layer = layerEnter.merge(layer);
65246 if (service && ~~context.map().zoom() >= minZoom) {
65249 service.loadImages(projection);
65256 drawImages.enabled = function (_) {
65257 if (!arguments.length) return svgMapillaryImages.enabled;
65258 svgMapillaryImages.enabled = _;
65260 if (svgMapillaryImages.enabled) {
65262 context.photos().on('change.mapillary_images', update);
65265 context.photos().on('change.mapillary_images', null);
65268 dispatch.call('change');
65272 drawImages.supported = function () {
65273 return !!getService();
65280 function svgMapillaryPosition(projection, context) {
65281 var throttledRedraw = throttle(function () {
65286 var minViewfieldZoom = 18;
65287 var layer = select(null);
65291 var viewerCompassAngle;
65294 if (svgMapillaryPosition.initialized) return; // run once
65296 svgMapillaryPosition.initialized = true;
65299 function getService() {
65300 if (services.mapillary && !_mapillary) {
65301 _mapillary = services.mapillary;
65303 _mapillary.event.on('imageChanged', throttledRedraw);
65305 _mapillary.event.on('bearingChanged', function (e) {
65306 viewerCompassAngle = e.bearing;
65307 if (context.map().isTransformed()) return;
65308 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
65310 }).attr('transform', transform);
65312 } else if (!services.mapillary && _mapillary) {
65319 function editOn() {
65320 layer.style('display', 'block');
65323 function editOff() {
65324 layer.selectAll('.viewfield-group').remove();
65325 layer.style('display', 'none');
65328 function transform(d) {
65329 var t = svgPointTransform(projection)(d);
65331 if (d.is_pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
65332 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
65334 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65340 function update() {
65341 var z = ~~context.map().zoom();
65342 var showViewfields = z >= minViewfieldZoom;
65343 var service = getService();
65344 var image = service && service.getActiveImage();
65345 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(image ? [image] : [], function (d) {
65349 groups.exit().remove(); // enter
65351 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
65352 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65354 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
65355 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65356 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65357 viewfields.exit().remove();
65358 viewfields.enter().insert('path', 'circle').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');
65361 function drawImages(selection) {
65362 var service = getService();
65363 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
65364 layer.exit().remove();
65365 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
65366 layerEnter.append('g').attr('class', 'markers');
65367 layer = layerEnter.merge(layer);
65369 if (service && ~~context.map().zoom() >= minZoom) {
65377 drawImages.enabled = function () {
65382 drawImages.supported = function () {
65383 return !!getService();
65390 function svgMapillarySigns(projection, context, dispatch) {
65391 var throttledRedraw = throttle(function () {
65392 dispatch.call('change');
65396 var layer = select(null);
65401 if (svgMapillarySigns.initialized) return; // run once
65403 svgMapillarySigns.enabled = false;
65404 svgMapillarySigns.initialized = true;
65407 function getService() {
65408 if (services.mapillary && !_mapillary) {
65409 _mapillary = services.mapillary;
65411 _mapillary.event.on('loadedSigns', throttledRedraw);
65412 } else if (!services.mapillary && _mapillary) {
65419 function showLayer() {
65420 var service = getService();
65421 if (!service) return;
65422 service.loadSignResources(context);
65426 function hideLayer() {
65427 throttledRedraw.cancel();
65431 function editOn() {
65432 layer.style('display', 'block');
65435 function editOff() {
65436 layer.selectAll('.icon-sign').remove();
65437 layer.style('display', 'none');
65440 function click(d3_event, d) {
65441 var service = getService();
65442 if (!service) return;
65443 context.map().centerEase(d.loc);
65444 var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
65445 service.getDetections(d.id).then(function (detections) {
65446 if (detections.length) {
65447 var imageId = detections[0].image.id;
65449 if (imageId === selectedImageId) {
65450 service.highlightDetection(detections[0]).selectImage(context, imageId);
65452 service.ensureViewerLoaded(context).then(function () {
65453 service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
65460 function filterData(detectedFeatures) {
65461 var fromDate = context.photos().fromDate();
65462 var toDate = context.photos().toDate();
65465 var fromTimestamp = new Date(fromDate).getTime();
65466 detectedFeatures = detectedFeatures.filter(function (feature) {
65467 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
65472 var toTimestamp = new Date(toDate).getTime();
65473 detectedFeatures = detectedFeatures.filter(function (feature) {
65474 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
65478 return detectedFeatures;
65481 function update() {
65482 var service = getService();
65483 var data = service ? service.signs(projection) : [];
65484 data = filterData(data);
65485 var transform = svgPointTransform(projection);
65486 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
65490 signs.exit().remove(); // enter
65492 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
65493 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
65494 return '#' + d.value;
65496 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
65498 signs.merge(enter).attr('transform', transform);
65501 function drawSigns(selection) {
65502 var enabled = svgMapillarySigns.enabled;
65503 var service = getService();
65504 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
65505 layer.exit().remove();
65506 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
65509 if (service && ~~context.map().zoom() >= minZoom) {
65512 service.loadSigns(projection);
65513 service.showSignDetections(true);
65517 } else if (service) {
65518 service.showSignDetections(false);
65522 drawSigns.enabled = function (_) {
65523 if (!arguments.length) return svgMapillarySigns.enabled;
65524 svgMapillarySigns.enabled = _;
65526 if (svgMapillarySigns.enabled) {
65528 context.photos().on('change.mapillary_signs', update);
65531 context.photos().on('change.mapillary_signs', null);
65534 dispatch.call('change');
65538 drawSigns.supported = function () {
65539 return !!getService();
65546 function svgMapillaryMapFeatures(projection, context, dispatch) {
65547 var throttledRedraw = throttle(function () {
65548 dispatch.call('change');
65552 var layer = select(null);
65557 if (svgMapillaryMapFeatures.initialized) return; // run once
65559 svgMapillaryMapFeatures.enabled = false;
65560 svgMapillaryMapFeatures.initialized = true;
65563 function getService() {
65564 if (services.mapillary && !_mapillary) {
65565 _mapillary = services.mapillary;
65567 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
65568 } else if (!services.mapillary && _mapillary) {
65575 function showLayer() {
65576 var service = getService();
65577 if (!service) return;
65578 service.loadObjectResources(context);
65582 function hideLayer() {
65583 throttledRedraw.cancel();
65587 function editOn() {
65588 layer.style('display', 'block');
65591 function editOff() {
65592 layer.selectAll('.icon-map-feature').remove();
65593 layer.style('display', 'none');
65596 function click(d3_event, d) {
65597 var service = getService();
65598 if (!service) return;
65599 context.map().centerEase(d.loc);
65600 var selectedImageId = service.getActiveImage() && service.getActiveImage().id;
65601 service.getDetections(d.id).then(function (detections) {
65602 if (detections.length) {
65603 var imageId = detections[0].image.id;
65605 if (imageId === selectedImageId) {
65606 service.highlightDetection(detections[0]).selectImage(context, imageId);
65608 service.ensureViewerLoaded(context).then(function () {
65609 service.highlightDetection(detections[0]).selectImage(context, imageId).showViewer(context);
65616 function filterData(detectedFeatures) {
65617 var fromDate = context.photos().fromDate();
65618 var toDate = context.photos().toDate();
65621 detectedFeatures = detectedFeatures.filter(function (feature) {
65622 return new Date(feature.last_seen_at).getTime() >= new Date(fromDate).getTime();
65627 detectedFeatures = detectedFeatures.filter(function (feature) {
65628 return new Date(feature.first_seen_at).getTime() <= new Date(toDate).getTime();
65632 return detectedFeatures;
65635 function update() {
65636 var service = getService();
65637 var data = service ? service.mapFeatures(projection) : [];
65638 data = filterData(data);
65639 var transform = svgPointTransform(projection);
65640 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
65644 mapFeatures.exit().remove(); // enter
65646 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
65647 enter.append('title').text(function (d) {
65648 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
65649 return _t('mapillary_map_features.' + id);
65651 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
65652 if (d.value === 'object--billboard') {
65653 // no billboard icon right now, so use the advertisement icon
65654 return '#object--sign--advertisement';
65657 return '#' + d.value;
65659 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
65661 mapFeatures.merge(enter).attr('transform', transform);
65664 function drawMapFeatures(selection) {
65665 var enabled = svgMapillaryMapFeatures.enabled;
65666 var service = getService();
65667 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
65668 layer.exit().remove();
65669 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
65672 if (service && ~~context.map().zoom() >= minZoom) {
65675 service.loadMapFeatures(projection);
65676 service.showFeatureDetections(true);
65680 } else if (service) {
65681 service.showFeatureDetections(false);
65685 drawMapFeatures.enabled = function (_) {
65686 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
65687 svgMapillaryMapFeatures.enabled = _;
65689 if (svgMapillaryMapFeatures.enabled) {
65691 context.photos().on('change.mapillary_map_features', update);
65694 context.photos().on('change.mapillary_map_features', null);
65697 dispatch.call('change');
65701 drawMapFeatures.supported = function () {
65702 return !!getService();
65706 return drawMapFeatures;
65709 function svgOpenstreetcamImages(projection, context, dispatch) {
65710 var throttledRedraw = throttle(function () {
65711 dispatch.call('change');
65715 var minMarkerZoom = 16;
65716 var minViewfieldZoom = 18;
65717 var layer = select(null);
65719 var _openstreetcam;
65722 if (svgOpenstreetcamImages.initialized) return; // run once
65724 svgOpenstreetcamImages.enabled = false;
65725 svgOpenstreetcamImages.initialized = true;
65728 function getService() {
65729 if (services.openstreetcam && !_openstreetcam) {
65730 _openstreetcam = services.openstreetcam;
65732 _openstreetcam.event.on('loadedImages', throttledRedraw);
65733 } else if (!services.openstreetcam && _openstreetcam) {
65734 _openstreetcam = null;
65737 return _openstreetcam;
65740 function showLayer() {
65741 var service = getService();
65742 if (!service) return;
65744 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
65745 dispatch.call('change');
65749 function hideLayer() {
65750 throttledRedraw.cancel();
65751 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
65754 function editOn() {
65755 layer.style('display', 'block');
65758 function editOff() {
65759 layer.selectAll('.viewfield-group').remove();
65760 layer.style('display', 'none');
65763 function click(d3_event, d) {
65764 var service = getService();
65765 if (!service) return;
65766 service.ensureViewerLoaded(context).then(function () {
65767 service.selectImage(context, d.key).showViewer(context);
65769 context.map().centerEase(d.loc);
65772 function mouseover(d3_event, d) {
65773 var service = getService();
65774 if (service) service.setStyles(context, d);
65777 function mouseout() {
65778 var service = getService();
65779 if (service) service.setStyles(context, null);
65782 function transform(d) {
65783 var t = svgPointTransform(projection)(d);
65786 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
65792 function filterImages(images) {
65793 var fromDate = context.photos().fromDate();
65794 var toDate = context.photos().toDate();
65795 var usernames = context.photos().usernames();
65798 var fromTimestamp = new Date(fromDate).getTime();
65799 images = images.filter(function (item) {
65800 return new Date(item.captured_at).getTime() >= fromTimestamp;
65805 var toTimestamp = new Date(toDate).getTime();
65806 images = images.filter(function (item) {
65807 return new Date(item.captured_at).getTime() <= toTimestamp;
65812 images = images.filter(function (item) {
65813 return usernames.indexOf(item.captured_by) !== -1;
65820 function filterSequences(sequences) {
65821 var fromDate = context.photos().fromDate();
65822 var toDate = context.photos().toDate();
65823 var usernames = context.photos().usernames();
65826 var fromTimestamp = new Date(fromDate).getTime();
65827 sequences = sequences.filter(function (image) {
65828 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
65833 var toTimestamp = new Date(toDate).getTime();
65834 sequences = sequences.filter(function (image) {
65835 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
65840 sequences = sequences.filter(function (image) {
65841 return usernames.indexOf(image.properties.captured_by) !== -1;
65848 function update() {
65849 var viewer = context.container().select('.photoviewer');
65850 var selected = viewer.empty() ? undefined : viewer.datum();
65851 var z = ~~context.map().zoom();
65852 var showMarkers = z >= minMarkerZoom;
65853 var showViewfields = z >= minViewfieldZoom;
65854 var service = getService();
65855 var sequences = [];
65858 if (context.photos().showsFlat()) {
65859 sequences = service ? service.sequences(projection) : [];
65860 images = service && showMarkers ? service.images(projection) : [];
65861 sequences = filterSequences(sequences);
65862 images = filterImages(images);
65865 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
65866 return d.properties.key;
65869 traces.exit().remove(); // enter/update
65871 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
65872 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
65876 groups.exit().remove(); // enter
65878 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
65879 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
65881 var markers = groups.merge(groupsEnter).sort(function (a, b) {
65882 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
65883 }).attr('transform', transform).select('.viewfield-scale');
65884 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
65885 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
65886 viewfields.exit().remove();
65887 viewfields.enter() // viewfields may or may not be drawn...
65888 .insert('path', 'circle') // but if they are, draw below the circles
65889 .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');
65892 function drawImages(selection) {
65893 var enabled = svgOpenstreetcamImages.enabled,
65894 service = getService();
65895 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
65896 layer.exit().remove();
65897 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
65898 layerEnter.append('g').attr('class', 'sequences');
65899 layerEnter.append('g').attr('class', 'markers');
65900 layer = layerEnter.merge(layer);
65903 if (service && ~~context.map().zoom() >= minZoom) {
65906 service.loadImages(projection);
65913 drawImages.enabled = function (_) {
65914 if (!arguments.length) return svgOpenstreetcamImages.enabled;
65915 svgOpenstreetcamImages.enabled = _;
65917 if (svgOpenstreetcamImages.enabled) {
65919 context.photos().on('change.openstreetcam_images', update);
65922 context.photos().on('change.openstreetcam_images', null);
65925 dispatch.call('change');
65929 drawImages.supported = function () {
65930 return !!getService();
65937 function svgOsm(projection, context, dispatch) {
65938 var enabled = true;
65940 function drawOsm(selection) {
65941 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
65942 return 'layer-osm ' + d;
65944 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
65945 return 'points-group ' + d;
65949 function showLayer() {
65950 var layer = context.surface().selectAll('.data-layer.osm');
65952 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
65953 dispatch.call('change');
65957 function hideLayer() {
65958 var layer = context.surface().selectAll('.data-layer.osm');
65960 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
65961 layer.classed('disabled', true);
65962 dispatch.call('change');
65966 drawOsm.enabled = function (val) {
65967 if (!arguments.length) return enabled;
65976 dispatch.call('change');
65983 var _notesEnabled = false;
65987 function svgNotes(projection, context, dispatch) {
65989 dispatch = dispatch$8('change');
65992 var throttledRedraw = throttle(function () {
65993 dispatch.call('change');
65997 var touchLayer = select(null);
65998 var drawLayer = select(null);
65999 var _notesVisible = false;
66001 function markerPath(selection, klass) {
66002 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');
66003 } // Loosely-coupled osm service for fetching notes.
66006 function getService() {
66007 if (services.osm && !_osmService) {
66008 _osmService = services.osm;
66010 _osmService.on('loadedNotes', throttledRedraw);
66011 } else if (!services.osm && _osmService) {
66012 _osmService = null;
66015 return _osmService;
66016 } // Show the notes
66019 function editOn() {
66020 if (!_notesVisible) {
66021 _notesVisible = true;
66022 drawLayer.style('display', 'block');
66024 } // Immediately remove the notes and their touch targets
66027 function editOff() {
66028 if (_notesVisible) {
66029 _notesVisible = false;
66030 drawLayer.style('display', 'none');
66031 drawLayer.selectAll('.note').remove();
66032 touchLayer.selectAll('.note').remove();
66034 } // Enable the layer. This shows the notes and transitions them to visible.
66037 function layerOn() {
66039 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
66040 dispatch.call('change');
66042 } // Disable the layer. This transitions the layer invisible and then hides the notes.
66045 function layerOff() {
66046 throttledRedraw.cancel();
66047 drawLayer.interrupt();
66048 touchLayer.selectAll('.note').remove();
66049 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
66051 dispatch.call('change');
66053 } // Update the note markers
66056 function updateMarkers() {
66057 if (!_notesVisible || !_notesEnabled) return;
66058 var service = getService();
66059 var selectedID = context.selectedNoteID();
66060 var data = service ? service.notes(projection) : [];
66061 var getTransform = svgPointTransform(projection); // Draw markers..
66063 var notes = drawLayer.selectAll('.note').data(data, function (d) {
66064 return d.status + d.id;
66067 notes.exit().remove(); // enter
66069 var notesEnter = notes.enter().append('g').attr('class', function (d) {
66070 return 'note note-' + d.id + ' ' + d.status;
66071 }).classed('new', function (d) {
66074 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
66075 notesEnter.append('path').call(markerPath, 'shadow');
66076 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');
66077 notesEnter.selectAll('.icon-annotation').data(function (d) {
66079 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
66080 if (d.id < 0) return '#iD-icon-plus';
66081 if (d.status === 'open') return '#iD-icon-close';
66082 return '#iD-icon-apply';
66085 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
66086 var mode = context.mode();
66087 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
66089 return !isMoving && d.id === selectedID;
66090 }).attr('transform', getTransform); // Draw targets..
66092 if (touchLayer.empty()) return;
66093 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66094 var targets = touchLayer.selectAll('.note').data(data, function (d) {
66098 targets.exit().remove(); // enter/update
66100 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
66101 var newClass = d.id < 0 ? 'new' : '';
66102 return 'note target note-' + d.id + ' ' + fillClass + newClass;
66103 }).attr('transform', getTransform);
66105 function sortY(a, b) {
66106 if (a.id === selectedID) return 1;
66107 if (b.id === selectedID) return -1;
66108 return b.loc[1] - a.loc[1];
66110 } // Draw the notes layer and schedule loading notes and updating markers.
66113 function drawNotes(selection) {
66114 var service = getService();
66115 var surface = context.surface();
66117 if (surface && !surface.empty()) {
66118 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
66121 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
66122 drawLayer.exit().remove();
66123 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
66125 if (_notesEnabled) {
66126 if (service && ~~context.map().zoom() >= minZoom) {
66128 service.loadNotes(projection);
66134 } // Toggles the layer on and off
66137 drawNotes.enabled = function (val) {
66138 if (!arguments.length) return _notesEnabled;
66139 _notesEnabled = val;
66141 if (_notesEnabled) {
66146 if (context.selectedNoteID()) {
66147 context.enter(modeBrowse(context));
66151 dispatch.call('change');
66158 function svgTouch() {
66159 function drawTouch(selection) {
66160 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
66161 return 'layer-touch ' + d;
66168 function refresh(selection, node) {
66169 var cr = node.getBoundingClientRect();
66170 var prop = [cr.width, cr.height];
66171 selection.property('__dimensions__', prop);
66175 function utilGetDimensions(selection, force) {
66176 if (!selection || selection.empty()) {
66180 var node = selection.node(),
66181 cached = selection.property('__dimensions__');
66182 return !cached || force ? refresh(selection, node) : cached;
66184 function utilSetDimensions(selection, dimensions) {
66185 if (!selection || selection.empty()) {
66189 var node = selection.node();
66191 if (dimensions === null) {
66192 refresh(selection, node);
66196 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
66199 function svgLayers(projection, context) {
66200 var dispatch = dispatch$8('change');
66201 var svg = select(null);
66204 layer: svgOsm(projection, context, dispatch)
66207 layer: svgNotes(projection, context, dispatch)
66210 layer: svgData(projection, context, dispatch)
66213 layer: svgKeepRight(projection, context, dispatch)
66216 layer: svgImproveOSM(projection, context, dispatch)
66219 layer: svgOsmose(projection, context, dispatch)
66222 layer: svgStreetside(projection, context, dispatch)
66225 layer: svgMapillaryImages(projection, context, dispatch)
66227 id: 'mapillary-position',
66228 layer: svgMapillaryPosition(projection, context)
66230 id: 'mapillary-map-features',
66231 layer: svgMapillaryMapFeatures(projection, context, dispatch)
66233 id: 'mapillary-signs',
66234 layer: svgMapillarySigns(projection, context, dispatch)
66236 id: 'openstreetcam',
66237 layer: svgOpenstreetcamImages(projection, context, dispatch)
66240 layer: svgDebug(projection, context)
66243 layer: svgGeolocate(projection)
66249 function drawLayers(selection) {
66250 svg = selection.selectAll('.surface').data([0]);
66251 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
66252 var defs = svg.selectAll('.surface-defs').data([0]);
66253 defs.enter().append('defs').attr('class', 'surface-defs');
66254 var groups = svg.selectAll('.data-layer').data(_layers);
66255 groups.exit().remove();
66256 groups.enter().append('g').attr('class', function (d) {
66257 return 'data-layer ' + d.id;
66258 }).merge(groups).each(function (d) {
66259 select(this).call(d.layer);
66263 drawLayers.all = function () {
66267 drawLayers.layer = function (id) {
66268 var obj = _layers.find(function (o) {
66269 return o.id === id;
66272 return obj && obj.layer;
66275 drawLayers.only = function (what) {
66276 var arr = [].concat(what);
66278 var all = _layers.map(function (layer) {
66282 return drawLayers.remove(utilArrayDifference(all, arr));
66285 drawLayers.remove = function (what) {
66286 var arr = [].concat(what);
66287 arr.forEach(function (id) {
66288 _layers = _layers.filter(function (o) {
66289 return o.id !== id;
66292 dispatch.call('change');
66296 drawLayers.add = function (what) {
66297 var arr = [].concat(what);
66298 arr.forEach(function (obj) {
66299 if ('id' in obj && 'layer' in obj) {
66303 dispatch.call('change');
66307 drawLayers.dimensions = function (val) {
66308 if (!arguments.length) return utilGetDimensions(svg);
66309 utilSetDimensions(svg, val);
66313 return utilRebind(drawLayers, dispatch, 'on');
66316 function svgLines(projection, context) {
66317 var detected = utilDetect();
66318 var highway_stack = {
66333 function drawTargets(selection, graph, entities, filter) {
66334 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66335 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
66336 var getPath = svgPath(projection).geojson;
66337 var activeID = context.activeID();
66338 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
66344 entities.forEach(function (way) {
66345 var features = svgSegmentWay(way, graph, activeID);
66346 data.targets.push.apply(data.targets, features.passive);
66347 data.nopes.push.apply(data.nopes, features.active);
66348 }); // Targets allow hover and vertex snapping
66350 var targetData = data.targets.filter(getPath);
66351 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
66352 return filter(d.properties.entity);
66353 }).data(targetData, function key(d) {
66357 targets.exit().remove();
66359 var segmentWasEdited = function segmentWasEdited(d) {
66360 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
66362 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
66366 return d.properties.nodes.some(function (n) {
66367 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
66372 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
66373 return 'way line target target-allowed ' + targetClass + d.id;
66374 }).classed('segment-edited', segmentWasEdited); // NOPE
66376 var nopeData = data.nopes.filter(getPath);
66377 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
66378 return filter(d.properties.entity);
66379 }).data(nopeData, function key(d) {
66383 nopes.exit().remove(); // enter/update
66385 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
66386 return 'way line target target-nope ' + nopeClass + d.id;
66387 }).classed('segment-edited', segmentWasEdited);
66390 function drawLines(selection, graph, entities, filter) {
66391 var base = context.history().base();
66393 function waystack(a, b) {
66394 var selected = context.selectedIDs();
66395 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
66396 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
66398 if (a.tags.highway) {
66399 scoreA -= highway_stack[a.tags.highway];
66402 if (b.tags.highway) {
66403 scoreB -= highway_stack[b.tags.highway];
66406 return scoreA - scoreB;
66409 function drawLineGroup(selection, klass, isSelected) {
66410 // Note: Don't add `.selected` class in draw modes
66411 var mode = context.mode();
66412 var isDrawing = mode && /^draw/.test(mode.id);
66413 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
66414 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
66415 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
66416 // works because osmEntity.key is defined to include the entity v attribute.
66418 lines.enter().append('path').attr('class', function (d) {
66419 var prefix = 'way line'; // if this line isn't styled by its own tags
66421 if (!d.hasInterestingTags()) {
66422 var parentRelations = graph.parentRelations(d);
66423 var parentMultipolygons = parentRelations.filter(function (relation) {
66424 return relation.isMultipolygon();
66425 }); // and if it's a member of at least one multipolygon relation
66427 if (parentMultipolygons.length > 0 && // and only multipolygon relations
66428 parentRelations.length === parentMultipolygons.length) {
66429 // then fudge the classes to style this as an area edge
66430 prefix = 'relation area';
66434 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
66435 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
66436 }).classed('added', function (d) {
66437 return !base.entities[d.id];
66438 }).classed('geometry-edited', function (d) {
66439 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
66440 }).classed('retagged', function (d) {
66441 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66442 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
66446 function getPathData(isSelected) {
66447 return function () {
66448 var layer = this.parentNode.__data__;
66449 var data = pathdata[layer] || [];
66450 return data.filter(function (d) {
66452 return context.selectedIDs().indexOf(d.id) !== -1;
66454 return context.selectedIDs().indexOf(d.id) === -1;
66460 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
66461 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
66462 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
66463 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
66464 return groupdata[this.parentNode.__data__] || [];
66465 }, function key(d) {
66466 return [d.id, d.index];
66468 markers.exit().remove();
66469 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
66474 markers.each(function () {
66475 this.parentNode.insertBefore(this, this);
66480 var getPath = svgPath(projection, graph);
66482 var onewaydata = {};
66483 var sideddata = {};
66484 var oldMultiPolygonOuters = {};
66486 for (var i = 0; i < entities.length; i++) {
66487 var entity = entities[i];
66488 var outer = osmOldMultipolygonOuterMember(entity, graph);
66491 ways.push(entity.mergeTags(outer.tags));
66492 oldMultiPolygonOuters[outer.id] = true;
66493 } else if (entity.geometry(graph) === 'line') {
66498 ways = ways.filter(getPath);
66499 var pathdata = utilArrayGroupBy(ways, function (way) {
66500 return way.layer();
66502 Object.keys(pathdata).forEach(function (k) {
66503 var v = pathdata[k];
66504 var onewayArr = v.filter(function (d) {
66505 return d.isOneWay();
66507 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
66508 return entity.tags.oneway === '-1';
66509 }, function bothDirections(entity) {
66510 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
66512 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
66513 var sidedArr = v.filter(function (d) {
66514 return d.isSided();
66516 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
66518 }, function bothDirections() {
66521 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
66523 var covered = selection.selectAll('.layer-osm.covered'); // under areas
66525 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
66527 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
66529 [covered, uncovered].forEach(function (selection) {
66530 var range = selection === covered ? range$1(-10, 0) : range$1(0, 11);
66531 var layergroup = selection.selectAll('g.layergroup').data(range);
66532 layergroup = layergroup.enter().append('g').attr('class', function (d) {
66533 return 'layergroup layer' + String(d);
66534 }).merge(layergroup);
66535 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
66536 return 'linegroup line-' + d;
66538 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
66539 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
66540 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
66541 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
66542 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
66543 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
66544 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
66545 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
66546 var category = graph.entity(d.id).sidednessIdentifier();
66547 return 'url(#ideditor-sided-marker-' + category + ')';
66549 }); // Draw touch targets..
66551 touchLayer.call(drawTargets, graph, ways, filter);
66557 function svgMidpoints(projection, context) {
66558 var targetRadius = 8;
66560 function drawTargets(selection, graph, entities, filter) {
66561 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66562 var getTransform = svgPointTransform(projection).geojson;
66563 var data = entities.map(function (midpoint) {
66573 coordinates: midpoint.loc
66577 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
66578 return filter(d.properties.entity);
66579 }).data(data, function key(d) {
66583 targets.exit().remove(); // enter/update
66585 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
66586 return 'node midpoint target ' + fillClass + d.id;
66587 }).attr('transform', getTransform);
66590 function drawMidpoints(selection, graph, entities, filter, extent) {
66591 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
66592 var touchLayer = selection.selectAll('.layer-touch.points');
66593 var mode = context.mode();
66595 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
66596 drawLayer.selectAll('.midpoint').remove();
66597 touchLayer.selectAll('.midpoint.target').remove();
66601 var poly = extent.polygon();
66602 var midpoints = {};
66604 for (var i = 0; i < entities.length; i++) {
66605 var entity = entities[i];
66606 if (entity.type !== 'way') continue;
66607 if (!filter(entity)) continue;
66608 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
66609 var nodes = graph.childNodes(entity);
66611 for (var j = 0; j < nodes.length - 1; j++) {
66613 var b = nodes[j + 1];
66614 var id = [a.id, b.id].sort().join('-');
66616 if (midpoints[id]) {
66617 midpoints[id].parents.push(entity);
66618 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
66619 var point = geoVecInterp(a.loc, b.loc, 0.5);
66622 if (extent.intersects(point)) {
66625 for (var k = 0; k < 4; k++) {
66626 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
66628 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
66640 edge: [a.id, b.id],
66648 function midpointFilter(d) {
66649 if (midpoints[d.id]) return true;
66651 for (var i = 0; i < d.parents.length; i++) {
66652 if (filter(d.parents[i])) {
66660 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
66663 groups.exit().remove();
66664 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
66665 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
66666 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
66667 groups = groups.merge(enter).attr('transform', function (d) {
66668 var translate = svgPointTransform(projection);
66669 var a = graph.entity(d.edge[0]);
66670 var b = graph.entity(d.edge[1]);
66671 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
66672 return translate(d) + ' rotate(' + angle + ')';
66673 }).call(svgTagClasses().tags(function (d) {
66674 return d.parents[0].tags;
66675 })); // Propagate data bindings.
66677 groups.select('polygon.shadow');
66678 groups.select('polygon.fill'); // Draw touch targets..
66680 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
66683 return drawMidpoints;
66686 function svgPoints(projection, context) {
66687 function markerPath(selection, klass) {
66688 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');
66691 function sortY(a, b) {
66692 return b.loc[1] - a.loc[1];
66693 } // Avoid exit/enter if we're just moving stuff around.
66694 // The node will get a new version but we only need to run the update selection.
66697 function fastEntityKey(d) {
66698 var mode = context.mode();
66699 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
66700 return isMoving ? d.id : osmEntity.key(d);
66703 function drawTargets(selection, graph, entities, filter) {
66704 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66705 var getTransform = svgPointTransform(projection).geojson;
66706 var activeID = context.activeID();
66708 entities.forEach(function (node) {
66709 if (activeID === node.id) return; // draw no target on the activeID
66718 geometry: node.asGeoJSON()
66721 var targets = selection.selectAll('.point.target').filter(function (d) {
66722 return filter(d.properties.entity);
66723 }).data(data, function key(d) {
66727 targets.exit().remove(); // enter/update
66729 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
66730 return 'node point target ' + fillClass + d.id;
66731 }).attr('transform', getTransform);
66734 function drawPoints(selection, graph, entities, filter) {
66735 var wireframe = context.surface().classed('fill-wireframe');
66736 var zoom = geoScaleToZoom(projection.scale());
66737 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
66739 function renderAsPoint(entity) {
66740 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
66741 } // All points will render as vertices in wireframe mode too..
66744 var points = wireframe ? [] : entities.filter(renderAsPoint);
66745 points.sort(sortY);
66746 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
66747 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
66749 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
66750 groups.exit().remove();
66751 var enter = groups.enter().append('g').attr('class', function (d) {
66752 return 'node point ' + d.id;
66754 enter.append('path').call(markerPath, 'shadow');
66755 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
66756 enter.append('path').call(markerPath, 'stroke');
66757 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
66758 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
66759 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
66760 }).classed('moved', function (d) {
66761 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
66762 }).classed('retagged', function (d) {
66763 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66764 }).call(svgTagClasses());
66765 groups.select('.shadow'); // propagate bound data
66767 groups.select('.stroke'); // propagate bound data
66769 groups.select('.icon') // propagate bound data
66770 .attr('xlink:href', function (entity) {
66771 var preset = _mainPresetIndex.match(entity, graph);
66772 var picon = preset && preset.icon;
66777 var isMaki = /^maki-/.test(picon);
66778 return '#' + picon + (isMaki ? '-11' : '');
66780 }); // Draw touch targets..
66782 touchLayer.call(drawTargets, graph, points, filter);
66788 function svgTurns(projection, context) {
66789 function icon(turn) {
66790 var u = turn.u ? '-u' : '';
66791 if (turn.no) return '#iD-turn-no' + u;
66792 if (turn.only) return '#iD-turn-only' + u;
66793 return '#iD-turn-yes' + u;
66796 function drawTurns(selection, graph, turns) {
66797 function turnTransform(d) {
66799 var toWay = graph.entity(d.to.way);
66800 var toPoints = graph.childNodes(toWay).map(function (n) {
66802 }).map(projection);
66803 var toLength = geoPathLength(toPoints);
66804 var mid = toLength / 2; // midpoint of destination way
66806 var toNode = graph.entity(d.to.node);
66807 var toVertex = graph.entity(d.to.vertex);
66808 var a = geoAngle(toVertex, toNode, projection);
66809 var o = projection(toVertex.loc);
66810 var r = d.u ? 0 // u-turn: no radius
66811 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
66812 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
66814 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
66817 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
66818 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
66820 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
66824 groups.exit().remove(); // enter
66826 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
66827 return 'turn ' + d.key;
66829 var turnsEnter = groupsEnter.filter(function (d) {
66832 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66833 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66834 var uEnter = groupsEnter.filter(function (d) {
66837 uEnter.append('circle').attr('r', '16');
66838 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
66840 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
66841 return d.direct === false ? '0.7' : null;
66842 }).attr('transform', turnTransform);
66843 groups.select('use').attr('xlink:href', icon);
66844 groups.select('rect'); // propagate bound data
66846 groups.select('circle'); // propagate bound data
66847 // Draw touch targets..
66849 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
66850 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
66854 groups.exit().remove(); // enter
66856 groupsEnter = groups.enter().append('g').attr('class', function (d) {
66857 return 'turn ' + d.key;
66859 turnsEnter = groupsEnter.filter(function (d) {
66862 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
66863 uEnter = groupsEnter.filter(function (d) {
66866 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
66868 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
66869 groups.select('rect'); // propagate bound data
66871 groups.select('circle'); // propagate bound data
66879 function svgVertices(projection, context) {
66881 // z16-, z17, z18+, w/icon
66882 shadow: [6, 7.5, 7.5, 12],
66883 stroke: [2.5, 3.5, 3.5, 8],
66884 fill: [1, 1.5, 1.5, 1.5]
66887 var _currHoverTarget;
66889 var _currPersistent = {};
66890 var _currHover = {};
66891 var _prevHover = {};
66892 var _currSelected = {};
66893 var _prevSelected = {};
66896 function sortY(a, b) {
66897 return b.loc[1] - a.loc[1];
66898 } // Avoid exit/enter if we're just moving stuff around.
66899 // The node will get a new version but we only need to run the update selection.
66902 function fastEntityKey(d) {
66903 var mode = context.mode();
66904 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
66905 return isMoving ? d.id : osmEntity.key(d);
66908 function draw(selection, graph, vertices, sets, filter) {
66915 var directions = {};
66916 var wireframe = context.surface().classed('fill-wireframe');
66917 var zoom = geoScaleToZoom(projection.scale());
66918 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
66919 var activeID = context.activeID();
66920 var base = context.history().base();
66922 function getIcon(d) {
66923 // always check latest entity, as fastEntityKey avoids enter/exit now
66924 var entity = graph.entity(d.id);
66925 if (entity.id in icons) return icons[entity.id];
66926 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
66927 return icons[entity.id];
66928 } // memoize directions results, return false for empty arrays (for use in filter)
66931 function getDirections(entity) {
66932 if (entity.id in directions) return directions[entity.id];
66933 var angles = entity.directions(graph, projection);
66934 directions[entity.id] = angles.length ? angles : false;
66938 function updateAttributes(selection) {
66939 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
66940 var rads = radiuses[klass];
66941 selection.selectAll('.' + klass).each(function (entity) {
66942 var i = z && getIcon(entity);
66943 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
66945 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
66949 if (klass === 'shadow') {
66950 // remember this value, so we don't need to
66951 _radii[entity.id] = r; // recompute it when we draw the touch targets
66954 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
66959 vertices.sort(sortY);
66960 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
66962 groups.exit().remove(); // enter
66964 var enter = groups.enter().append('g').attr('class', function (d) {
66965 return 'node vertex ' + d.id;
66967 enter.append('circle').attr('class', 'shadow');
66968 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
66970 enter.filter(function (d) {
66971 return d.hasInterestingTags();
66972 }).append('circle').attr('class', 'fill'); // update
66974 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
66975 return d.id in sets.selected;
66976 }).classed('shared', function (d) {
66977 return graph.isShared(d);
66978 }).classed('endpoint', function (d) {
66979 return d.isEndpoint(graph);
66980 }).classed('added', function (d) {
66981 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
66982 }).classed('moved', function (d) {
66983 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
66984 }).classed('retagged', function (d) {
66985 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
66986 }).call(updateAttributes); // Vertices with icons get a `use`.
66988 var iconUse = groups.selectAll('.icon').data(function data(d) {
66989 return zoom >= 17 && getIcon(d) ? [d] : [];
66990 }, fastEntityKey); // exit
66992 iconUse.exit().remove(); // enter
66994 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) {
66995 var picon = getIcon(d);
66996 var isMaki = /^maki-/.test(picon);
66997 return '#' + picon + (isMaki ? '-11' : '');
66998 }); // Vertices with directions get viewfields
67000 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
67001 return zoom >= 18 && getDirections(d) ? [d] : [];
67002 }, fastEntityKey); // exit
67004 dgroups.exit().remove(); // enter/update
67006 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
67007 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
67008 return osmEntity.key(d);
67011 viewfields.exit().remove(); // enter/update
67013 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) {
67014 return 'rotate(' + d + ')';
67018 function drawTargets(selection, graph, entities, filter) {
67019 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
67020 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
67021 var getTransform = svgPointTransform(projection).geojson;
67022 var activeID = context.activeID();
67027 entities.forEach(function (node) {
67028 if (activeID === node.id) return; // draw no target on the activeID
67030 var vertexType = svgPassiveVertex(node, graph, activeID);
67032 if (vertexType !== 0) {
67033 // passive or adjacent - allow to connect
67034 data.targets.push({
67041 geometry: node.asGeoJSON()
67046 id: node.id + '-nope',
67052 geometry: node.asGeoJSON()
67055 }); // Targets allow hover and vertex snapping
67057 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
67058 return filter(d.properties.entity);
67059 }).data(data.targets, function key(d) {
67063 targets.exit().remove(); // enter/update
67065 targets.enter().append('circle').attr('r', function (d) {
67066 return _radii[d.id] || radiuses.shadow[3];
67067 }).merge(targets).attr('class', function (d) {
67068 return 'node vertex target target-allowed ' + targetClass + d.id;
67069 }).attr('transform', getTransform); // NOPE
67071 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
67072 return filter(d.properties.entity);
67073 }).data(data.nopes, function key(d) {
67077 nopes.exit().remove(); // enter/update
67079 nopes.enter().append('circle').attr('r', function (d) {
67080 return _radii[d.properties.entity.id] || radiuses.shadow[3];
67081 }).merge(nopes).attr('class', function (d) {
67082 return 'node vertex target target-nope ' + nopeClass + d.id;
67083 }).attr('transform', getTransform);
67084 } // Points can also render as vertices:
67085 // 1. in wireframe mode or
67086 // 2. at higher zooms if they have a direction
67089 function renderAsVertex(entity, graph, wireframe, zoom) {
67090 var geometry = entity.geometry(graph);
67091 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
67094 function isEditedNode(node, base, head) {
67095 var baseNode = base.entities[node.id];
67096 var headNode = head.entities[node.id];
67097 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
67100 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
67104 function addChildVertices(entity) {
67105 // avoid redundant work and infinite recursion of circular relations
67106 if (seenIds[entity.id]) return;
67107 seenIds[entity.id] = true;
67108 var geometry = entity.geometry(graph);
67110 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
67113 if (entity.type === 'way') {
67114 for (i = 0; i < entity.nodes.length; i++) {
67115 var child = graph.hasEntity(entity.nodes[i]);
67118 addChildVertices(child);
67121 } else if (entity.type === 'relation') {
67122 for (i = 0; i < entity.members.length; i++) {
67123 var member = graph.hasEntity(entity.members[i].id);
67126 addChildVertices(member);
67129 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
67130 results[entity.id] = entity;
67135 ids.forEach(function (id) {
67136 var entity = graph.hasEntity(id);
67137 if (!entity) return;
67139 if (entity.type === 'node') {
67140 if (renderAsVertex(entity, graph, wireframe, zoom)) {
67141 results[entity.id] = entity;
67142 graph.parentWays(entity).forEach(function (entity) {
67143 addChildVertices(entity);
67148 addChildVertices(entity);
67154 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
67155 var wireframe = context.surface().classed('fill-wireframe');
67156 var visualDiff = context.surface().classed('highlight-edited');
67157 var zoom = geoScaleToZoom(projection.scale());
67158 var mode = context.mode();
67159 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
67160 var base = context.history().base();
67161 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
67162 var touchLayer = selection.selectAll('.layer-touch.points');
67165 _currPersistent = {};
67167 } // Collect important vertices from the `entities` list..
67168 // (during a partial redraw, it will not contain everything)
67171 for (var i = 0; i < entities.length; i++) {
67172 var entity = entities[i];
67173 var geometry = entity.geometry(graph);
67174 var keep = false; // a point that looks like a vertex..
67176 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
67177 _currPersistent[entity.id] = entity;
67178 keep = true; // a vertex of some importance..
67179 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
67180 _currPersistent[entity.id] = entity;
67182 } // whatever this is, it's not a persistent vertex..
67185 if (!keep && !fullRedraw) {
67186 delete _currPersistent[entity.id];
67188 } // 3 sets of vertices to consider:
67192 persistent: _currPersistent,
67193 // persistent = important vertices (render always)
67194 selected: _currSelected,
67195 // selected + siblings of selected (render always)
67196 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
67199 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
67200 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
67201 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
67203 var filterRendered = function filterRendered(d) {
67204 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
67207 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
67208 // When drawing, render all targets (not just those affected by a partial redraw)
67210 var filterTouch = function filterTouch(d) {
67211 return isMoving ? true : filterRendered(d);
67214 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
67216 function currentVisible(which) {
67217 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
67218 .filter(function (entity) {
67219 return entity && entity.intersects(extent, graph);
67222 } // partial redraw - only update the selected items..
67225 drawVertices.drawSelected = function (selection, graph, extent) {
67226 var wireframe = context.surface().classed('fill-wireframe');
67227 var zoom = geoScaleToZoom(projection.scale());
67228 _prevSelected = _currSelected || {};
67230 if (context.map().isInWideSelection()) {
67231 _currSelected = {};
67232 context.selectedIDs().forEach(function (id) {
67233 var entity = graph.hasEntity(id);
67234 if (!entity) return;
67236 if (entity.type === 'node') {
67237 if (renderAsVertex(entity, graph, wireframe, zoom)) {
67238 _currSelected[entity.id] = entity;
67243 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
67244 } // note that drawVertices will add `_currSelected` automatically if needed..
67247 var filter = function filter(d) {
67248 return d.id in _prevSelected;
67251 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
67252 }; // partial redraw - only update the hovered items..
67255 drawVertices.drawHover = function (selection, graph, target, extent) {
67256 if (target === _currHoverTarget) return; // continue only if something changed
67258 var wireframe = context.surface().classed('fill-wireframe');
67259 var zoom = geoScaleToZoom(projection.scale());
67260 _prevHover = _currHover || {};
67261 _currHoverTarget = target;
67262 var entity = target && target.properties && target.properties.entity;
67265 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
67268 } // note that drawVertices will add `_currHover` automatically if needed..
67271 var filter = function filter(d) {
67272 return d.id in _prevHover;
67275 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
67278 return drawVertices;
67281 function utilBindOnce(target, type, listener, capture) {
67282 var typeOnce = type + '.once';
67285 target.on(typeOnce, null);
67286 listener.apply(this, arguments);
67289 target.on(typeOnce, one, capture);
67293 function defaultFilter(d3_event) {
67294 return !d3_event.ctrlKey && !d3_event.button;
67297 function defaultExtent() {
67300 if (e instanceof SVGElement) {
67301 e = e.ownerSVGElement || e;
67303 if (e.hasAttribute('viewBox')) {
67304 e = e.viewBox.baseVal;
67305 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
67308 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
67311 return [[0, 0], [e.clientWidth, e.clientHeight]];
67314 function defaultWheelDelta(d3_event) {
67315 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
67318 function defaultConstrain(transform, extent, translateExtent) {
67319 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
67320 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
67321 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
67322 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
67323 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));
67326 function utilZoomPan() {
67327 var filter = defaultFilter,
67328 extent = defaultExtent,
67329 constrain = defaultConstrain,
67330 wheelDelta = defaultWheelDelta,
67331 scaleExtent = [0, Infinity],
67332 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
67333 interpolate = interpolateZoom,
67334 dispatch = dispatch$8('start', 'zoom', 'end'),
67336 _transform = identity$2,
67339 function zoom(selection) {
67340 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
67341 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
67344 zoom.transform = function (collection, transform, point) {
67345 var selection = collection.selection ? collection.selection() : collection;
67347 if (collection !== selection) {
67348 schedule(collection, transform, point);
67350 selection.interrupt().each(function () {
67351 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
67356 zoom.scaleBy = function (selection, k, p) {
67357 zoom.scaleTo(selection, function () {
67358 var k0 = _transform.k,
67359 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
67364 zoom.scaleTo = function (selection, k, p) {
67365 zoom.transform(selection, function () {
67366 var e = extent.apply(this, arguments),
67368 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
67369 p1 = t0.invert(p0),
67370 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
67371 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
67375 zoom.translateBy = function (selection, x, y) {
67376 zoom.transform(selection, function () {
67377 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);
67381 zoom.translateTo = function (selection, x, y, p) {
67382 zoom.transform(selection, function () {
67383 var e = extent.apply(this, arguments),
67385 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
67386 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);
67390 function scale(transform, k) {
67391 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
67392 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
67395 function translate(transform, p0, p1) {
67396 var x = p0[0] - p1[0] * transform.k,
67397 y = p0[1] - p1[1] * transform.k;
67398 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
67401 function centroid(extent) {
67402 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
67405 function schedule(transition, transform, point) {
67406 transition.on('start.zoom', function () {
67407 gesture(this, arguments).start(null);
67408 }).on('interrupt.zoom end.zoom', function () {
67409 gesture(this, arguments).end(null);
67410 }).tween('zoom', function () {
67413 g = gesture(that, args),
67414 e = extent.apply(that, args),
67415 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
67416 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
67418 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
67419 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
67420 return function (t) {
67422 // Avoid rounding error on end.
67427 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
67430 g.zoom(null, null, t);
67435 function gesture(that, args, clean) {
67436 return !clean && _activeGesture || new Gesture(that, args);
67439 function Gesture(that, args) {
67443 this.extent = extent.apply(that, args);
67446 Gesture.prototype = {
67447 start: function start(d3_event) {
67448 if (++this.active === 1) {
67449 _activeGesture = this;
67450 dispatch.call('start', this, d3_event);
67455 zoom: function zoom(d3_event, key, transform) {
67456 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
67457 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
67458 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
67459 _transform = transform;
67460 dispatch.call('zoom', this, d3_event, key, transform);
67463 end: function end(d3_event) {
67464 if (--this.active === 0) {
67465 _activeGesture = null;
67466 dispatch.call('end', this, d3_event);
67473 function wheeled(d3_event) {
67474 if (!filter.apply(this, arguments)) return;
67475 var g = gesture(this, arguments),
67477 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
67478 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
67479 // If there were recent wheel events, reset the wheel idle timeout.
67482 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
67483 g.mouse[1] = t.invert(g.mouse[0] = p);
67486 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
67488 g.mouse = [p, t.invert(p)];
67493 d3_event.preventDefault();
67494 d3_event.stopImmediatePropagation();
67495 g.wheel = setTimeout(wheelidled, _wheelDelay);
67496 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
67498 function wheelidled() {
67504 var _downPointerIDs = new Set();
67506 var _pointerLocGetter;
67508 function pointerdown(d3_event) {
67509 _downPointerIDs.add(d3_event.pointerId);
67511 if (!filter.apply(this, arguments)) return;
67512 var g = gesture(this, arguments, _downPointerIDs.size === 1);
67514 d3_event.stopImmediatePropagation();
67515 _pointerLocGetter = utilFastMouse(this);
67517 var loc = _pointerLocGetter(d3_event);
67519 var p = [loc, _transform.invert(loc), d3_event.pointerId];
67524 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
67534 function pointermove(d3_event) {
67535 if (!_downPointerIDs.has(d3_event.pointerId)) return;
67536 if (!_activeGesture || !_pointerLocGetter) return;
67537 var g = gesture(this, arguments);
67538 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
67539 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
67541 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
67542 // The pointer went up without ending the gesture somehow, e.g.
67543 // a down mouse was moved off the map and released. End it here.
67544 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
67545 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
67550 d3_event.preventDefault();
67551 d3_event.stopImmediatePropagation();
67553 var loc = _pointerLocGetter(d3_event);
67556 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
67560 var p0 = g.pointer0[0],
67561 l0 = g.pointer0[1],
67562 p1 = g.pointer1[0],
67563 l1 = g.pointer1[1],
67564 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
67565 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
67566 t = scale(t, Math.sqrt(dp / dl));
67567 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
67568 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
67569 } else if (g.pointer0) {
67576 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
67579 function pointerup(d3_event) {
67580 if (!_downPointerIDs.has(d3_event.pointerId)) return;
67582 _downPointerIDs["delete"](d3_event.pointerId);
67584 if (!_activeGesture) return;
67585 var g = gesture(this, arguments);
67586 d3_event.stopImmediatePropagation();
67587 if (g.pointer0 && g.pointer0[2] === d3_event.pointerId) delete g.pointer0;else if (g.pointer1 && g.pointer1[2] === d3_event.pointerId) delete g.pointer1;
67589 if (g.pointer1 && !g.pointer0) {
67590 g.pointer0 = g.pointer1;
67595 g.pointer0[1] = _transform.invert(g.pointer0[0]);
67601 zoom.wheelDelta = function (_) {
67602 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
67605 zoom.filter = function (_) {
67606 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
67609 zoom.extent = function (_) {
67610 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
67613 zoom.scaleExtent = function (_) {
67614 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
67617 zoom.translateExtent = function (_) {
67618 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]]];
67621 zoom.constrain = function (_) {
67622 return arguments.length ? (constrain = _, zoom) : constrain;
67625 zoom.interpolate = function (_) {
67626 return arguments.length ? (interpolate = _, zoom) : interpolate;
67629 zoom._transform = function (_) {
67630 return arguments.length ? (_transform = _, zoom) : _transform;
67633 return utilRebind(zoom, dispatch, 'on');
67636 // if pointer events are supported. Falls back to default `dblclick` event.
67638 function utilDoubleUp() {
67639 var dispatch = dispatch$8('doubleUp');
67640 var _maxTimespan = 500; // milliseconds
67642 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
67644 var _pointer; // object representing the pointer that could trigger double up
67647 function pointerIsValidFor(loc) {
67648 // second pointerup must occur within a small timeframe after the first pointerdown
67649 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
67650 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
67653 function pointerdown(d3_event) {
67654 // ignore right-click
67655 if (d3_event.ctrlKey || d3_event.button === 2) return;
67656 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
67657 // events on touch devices
67659 if (_pointer && !pointerIsValidFor(loc)) {
67660 // if this pointer is no longer valid, clear it so another can be started
67661 _pointer = undefined;
67667 startTime: new Date().getTime(),
67669 pointerId: d3_event.pointerId
67673 _pointer.pointerId = d3_event.pointerId;
67677 function pointerup(d3_event) {
67678 // ignore right-click
67679 if (d3_event.ctrlKey || d3_event.button === 2) return;
67680 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
67681 _pointer.upCount += 1;
67683 if (_pointer.upCount === 2) {
67685 var loc = [d3_event.clientX, d3_event.clientY];
67687 if (pointerIsValidFor(loc)) {
67688 var locInThis = utilFastMouse(this)(d3_event);
67689 dispatch.call('doubleUp', this, d3_event, locInThis);
67690 } // clear the pointer info in any case
67693 _pointer = undefined;
67697 function doubleUp(selection) {
67698 if ('PointerEvent' in window) {
67699 // dblclick isn't well supported on touch devices so manually use
67700 // pointer events if they're available
67701 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
67703 // fallback to dblclick
67704 selection.on('dblclick.doubleUp', function (d3_event) {
67705 dispatch.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
67710 doubleUp.off = function (selection) {
67711 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
67714 return utilRebind(doubleUp, dispatch, 'on');
67717 var TILESIZE = 256;
67720 var kMin = geoZoomToScale(minZoom, TILESIZE);
67721 var kMax = geoZoomToScale(maxZoom, TILESIZE);
67723 function clamp$1(num, min, max) {
67724 return Math.max(min, Math.min(num, max));
67727 function rendererMap(context) {
67728 var dispatch = dispatch$8('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
67729 var projection = context.projection;
67730 var curtainProjection = context.curtainProjection;
67739 var _selection = select(null);
67741 var supersurface = select(null);
67742 var wrapper = select(null);
67743 var surface = select(null);
67744 var _dimensions = [1, 1];
67745 var _dblClickZoomEnabled = true;
67746 var _redrawEnabled = true;
67748 var _gestureTransformStart;
67750 var _transformStart = projection.transform();
67752 var _transformLast;
67754 var _isTransformed = false;
67757 var _getMouseCoords;
67759 var _lastPointerEvent;
67761 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
67764 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
67766 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
67769 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
67771 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate$1).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
67772 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
67773 }).on('end.map', function () {
67774 _pointerDown = false;
67777 var _doubleUpHandler = utilDoubleUp();
67779 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
67780 // var pendingRedrawCall;
67781 // function scheduleRedraw() {
67782 // // Only schedule the redraw if one has not already been set.
67783 // if (isRedrawScheduled) return;
67784 // isRedrawScheduled = true;
67785 // var that = this;
67786 // var args = arguments;
67787 // pendingRedrawCall = window.requestIdleCallback(function () {
67788 // // Reset the boolean so future redraws can be set.
67789 // isRedrawScheduled = false;
67790 // redraw.apply(that, args);
67791 // }, { timeout: 1400 });
67795 function cancelPendingRedraw() {
67796 scheduleRedraw.cancel(); // isRedrawScheduled = false;
67797 // window.cancelIdleCallback(pendingRedrawCall);
67800 function map(selection) {
67801 _selection = selection;
67802 context.on('change.map', immediateRedraw);
67803 var osm = context.connection();
67806 osm.on('change.map', immediateRedraw);
67809 function didUndoOrRedo(targetTransform) {
67810 var mode = context.mode().id;
67811 if (mode !== 'browse' && mode !== 'select') return;
67813 if (targetTransform) {
67814 map.transformEase(targetTransform);
67818 context.history().on('merge.map', function () {
67820 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
67821 didUndoOrRedo(fromStack.transform);
67822 }).on('redone.map', function (stack) {
67823 didUndoOrRedo(stack.transform);
67825 context.background().on('change.map', immediateRedraw);
67826 context.features().on('redraw.map', immediateRedraw);
67827 drawLayers.on('change.map', function () {
67828 context.background().updateImagery();
67831 selection.on('wheel.map mousewheel.map', function (d3_event) {
67832 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
67833 d3_event.preventDefault();
67834 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
67836 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
67837 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
67839 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
67840 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
67841 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
67842 _lastPointerEvent = d3_event;
67844 if (d3_event.button === 2) {
67845 d3_event.stopPropagation();
67847 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
67848 _lastPointerEvent = d3_event;
67850 if (resetTransform()) {
67853 }).on(_pointerPrefix + 'move.map', function (d3_event) {
67854 _lastPointerEvent = d3_event;
67855 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
67856 if (map.editableDataEnabled() && !_isTransformed) {
67857 var hover = d3_event.target.__data__;
67858 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
67859 dispatch.call('drawn', this, {
67863 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
67864 if (map.editableDataEnabled() && !_isTransformed) {
67865 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
67866 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
67867 dispatch.call('drawn', this, {
67872 var detected = utilDetect(); // only WebKit supports gesture events
67874 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
67875 // but we only need to do this on desktop Safari anyway. – #7694
67876 !detected.isMobileWebKit) {
67877 // Desktop Safari sends gesture events for multitouch trackpad pinches.
67878 // We can listen for these and translate them into map zooms.
67879 surface.on('gesturestart.surface', function (d3_event) {
67880 d3_event.preventDefault();
67881 _gestureTransformStart = projection.transform();
67882 }).on('gesturechange.surface', gestureChange);
67883 } // must call after surface init
67888 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
67889 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
67891 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
67892 !select(d3_event.target).classed('fill')) return;
67893 var zoomOut = d3_event.shiftKey;
67894 var t = projection.transform();
67895 var p1 = t.invert(p0);
67896 t = t.scale(zoomOut ? 0.5 : 2);
67897 t.x = p0[0] - p1[0] * t.k;
67898 t.y = p0[1] - p1[1] * t.k;
67899 map.transformEase(t);
67902 context.on('enter.map', function () {
67903 if (!map.editableDataEnabled(true
67904 /* skip zoom check */
67906 if (_isTransformed) return; // redraw immediately any objects affected by a change in selectedIDs.
67908 var graph = context.graph();
67909 var selectedAndParents = {};
67910 context.selectedIDs().forEach(function (id) {
67911 var entity = graph.hasEntity(id);
67914 selectedAndParents[entity.id] = entity;
67916 if (entity.type === 'node') {
67917 graph.parentWays(entity).forEach(function (parent) {
67918 selectedAndParents[parent.id] = parent;
67923 var data = Object.values(selectedAndParents);
67925 var filter = function filter(d) {
67926 return d.id in selectedAndParents;
67929 data = context.features().filter(data, graph);
67930 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
67931 dispatch.call('drawn', this, {
67933 }); // redraw everything else later
67937 map.dimensions(utilGetDimensions(selection));
67940 function zoomEventFilter(d3_event) {
67941 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
67942 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
67943 // This can happen if a previous `mousedown` occurred without a `mouseup`.
67944 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
67945 // so that d3-zoom won't stop propagation of new `mousedown` events.
67946 if (d3_event.type === 'mousedown') {
67947 var hasOrphan = false;
67948 var listeners = window.__on;
67950 for (var i = 0; i < listeners.length; i++) {
67951 var listener = listeners[i];
67953 if (listener.name === 'zoom' && listener.type === 'mouseup') {
67960 var event = window.CustomEvent;
67963 event = new event('mouseup');
67965 event = window.document.createEvent('Event');
67966 event.initEvent('mouseup', false, false);
67967 } // Event needs to be dispatched with an event.view property.
67970 event.view = window;
67971 window.dispatchEvent(event);
67975 return d3_event.button !== 2; // ignore right clicks
67978 function pxCenter() {
67979 return [_dimensions[0] / 2, _dimensions[1] / 2];
67982 function drawEditable(difference, extent) {
67983 var mode = context.mode();
67984 var graph = context.graph();
67985 var features = context.features();
67986 var all = context.history().intersects(map.extent());
67987 var fullRedraw = false;
67991 var applyFeatureLayerFilters = true;
67993 if (map.isInWideSelection()) {
67995 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
67996 var entity = context.hasEntity(id);
67997 if (entity) data.push(entity);
68000 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
68002 applyFeatureLayerFilters = false;
68003 } else if (difference) {
68004 var complete = difference.complete(map.extent());
68005 data = Object.values(complete).filter(Boolean);
68006 set = new Set(Object.keys(complete));
68008 filter = function filter(d) {
68009 return set.has(d.id);
68012 features.clear(data);
68014 // force a full redraw if gatherStats detects that a feature
68015 // should be auto-hidden (e.g. points or buildings)..
68016 if (features.gatherStats(all, graph, _dimensions)) {
68017 extent = undefined;
68021 data = context.history().intersects(map.extent().intersection(extent));
68022 set = new Set(data.map(function (entity) {
68026 filter = function filter(d) {
68027 return set.has(d.id);
68032 filter = utilFunctor(true);
68036 if (applyFeatureLayerFilters) {
68037 data = features.filter(data, graph);
68039 context.features().resetStats();
68042 if (mode && mode.id === 'select') {
68043 // update selected vertices - the user might have just double-clicked a way,
68044 // creating a new vertex, triggering a partial redraw without a mode change
68045 surface.call(drawVertices.drawSelected, graph, map.extent());
68048 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);
68049 dispatch.call('drawn', this, {
68054 map.init = function () {
68055 drawLayers = svgLayers(projection, context);
68056 drawPoints = svgPoints(projection, context);
68057 drawVertices = svgVertices(projection, context);
68058 drawLines = svgLines(projection, context);
68059 drawAreas = svgAreas(projection, context);
68060 drawMidpoints = svgMidpoints(projection, context);
68061 drawLabels = svgLabels(projection, context);
68064 function editOff() {
68065 context.features().resetStats();
68066 surface.selectAll('.layer-osm *').remove();
68067 surface.selectAll('.layer-touch:not(.markers) *').remove();
68071 'select-note': true,
68072 'select-data': true,
68073 'select-error': true
68075 var mode = context.mode();
68077 if (mode && !allowed[mode.id]) {
68078 context.enter(modeBrowse(context));
68081 dispatch.call('drawn', this, {
68086 function gestureChange(d3_event) {
68087 // Remap Safari gesture events to wheel events - #5492
68088 // We want these disabled most places, but enabled for zoom/unzoom on map surface
68089 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
68091 e.preventDefault();
68094 // dummy values to ignore in zoomPan
68096 // dummy values to ignore in zoomPan
68097 clientX: e.clientX,
68098 clientY: e.clientY,
68099 screenX: e.screenX,
68100 screenY: e.screenY,
68104 var e2 = new WheelEvent('wheel', props);
68105 e2._scale = e.scale; // preserve the original scale
68107 e2._rotation = e.rotation; // preserve the original rotation
68109 _selection.node().dispatchEvent(e2);
68112 function zoomPan(event, key, transform) {
68113 var source = event && event.sourceEvent || event;
68114 var eventTransform = transform || event && event.transform;
68115 var x = eventTransform.x;
68116 var y = eventTransform.y;
68117 var k = eventTransform.k; // Special handling of 'wheel' events:
68118 // They might be triggered by the user scrolling the mouse wheel,
68119 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
68121 if (source && source.type === 'wheel') {
68122 // assume that the gesture is already handled by pointer events
68123 if (_pointerDown) return;
68124 var detected = utilDetect();
68125 var dX = source.deltaX;
68126 var dY = source.deltaY;
68130 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
68131 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
68132 // We are essentially redoing the calculations that occur here:
68133 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
68134 // See this for more info:
68135 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
68137 if (source.deltaMode === 1
68140 // Convert from lines to pixels, more if the user is scrolling fast.
68141 // (I made up the exp function to roughly match Firefox to what Chrome does)
68142 // These numbers should be floats, because integers are treated as pan gesture below.
68143 var lines = Math.abs(source.deltaY);
68144 var sign = source.deltaY > 0 ? 1 : -1;
68145 dY = sign * clamp$1(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
68146 350.000244140625 // max
68147 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
68148 // There doesn't seem to be any scroll acceleration.
68149 // This multiplier increases the speed a little bit - #5512
68151 if (detected.os !== 'mac') {
68153 } // recalculate x2,y2,k2
68156 t0 = _isTransformed ? _transformLast : _transformStart;
68157 p0 = _getMouseCoords(source);
68158 p1 = t0.invert(p0);
68159 k2 = t0.k * Math.pow(2, -dY / 500);
68160 k2 = clamp$1(k2, kMin, kMax);
68161 x2 = p0[0] - p1[0] * k2;
68162 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
68163 // These are fake `wheel` events we made from Safari `gesturechange` events..
68164 } else if (source._scale) {
68165 // recalculate x2,y2,k2
68166 t0 = _gestureTransformStart;
68167 p0 = _getMouseCoords(source);
68168 p1 = t0.invert(p0);
68169 k2 = t0.k * source._scale;
68170 k2 = clamp$1(k2, kMin, kMax);
68171 x2 = p0[0] - p1[0] * k2;
68172 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
68173 // Pinch zooming via the `wheel` event will always have:
68174 // - `ctrlKey = true`
68175 // - `deltaY` is not round integer pixels (ignore `deltaX`)
68176 } else if (source.ctrlKey && !isInteger(dY)) {
68177 dY *= 6; // slightly scale up whatever the browser gave us
68178 // recalculate x2,y2,k2
68180 t0 = _isTransformed ? _transformLast : _transformStart;
68181 p0 = _getMouseCoords(source);
68182 p1 = t0.invert(p0);
68183 k2 = t0.k * Math.pow(2, -dY / 500);
68184 k2 = clamp$1(k2, kMin, kMax);
68185 x2 = p0[0] - p1[0] * k2;
68186 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
68187 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
68188 // recalculate x2,y2,k2
68189 t0 = _isTransformed ? _transformLast : _transformStart;
68190 p0 = _getMouseCoords(source);
68191 p1 = t0.invert(p0);
68192 k2 = t0.k * Math.pow(2, -dY / 500);
68193 k2 = clamp$1(k2, kMin, kMax);
68194 x2 = p0[0] - p1[0] * k2;
68195 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers except Firefox #8595) - #5492, #5512
68196 // Panning via the `wheel` event will always have:
68197 // - `ctrlKey = false`
68198 // - `deltaX`,`deltaY` are round integer pixels
68199 } else if (detected.os === 'mac' && detected.browser !== 'Firefox' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
68200 p1 = projection.translate();
68203 k2 = projection.scale();
68204 k2 = clamp$1(k2, kMin, kMax);
68205 } // something changed - replace the event transform
68208 if (x2 !== x || y2 !== y || k2 !== k) {
68212 eventTransform = identity$2.translate(x2, y2).scale(k2);
68214 if (_zoomerPanner._transform) {
68215 // utilZoomPan interface
68216 _zoomerPanner._transform(eventTransform);
68218 // d3_zoom interface
68219 _selection.node().__zoom = eventTransform;
68224 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
68225 return; // no change
68228 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
68229 surface.interrupt();
68230 dispatch.call('hitMinZoom', this, map);
68231 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
68233 dispatch.call('move', this, map);
68237 projection.transform(eventTransform);
68238 var withinEditableZoom = map.withinEditableZoom();
68240 if (_lastWithinEditableZoom !== withinEditableZoom) {
68241 if (_lastWithinEditableZoom !== undefined) {
68242 // notify that the map zoomed in or out over the editable zoom threshold
68243 dispatch.call('crossEditableZoom', this, withinEditableZoom);
68246 _lastWithinEditableZoom = withinEditableZoom;
68249 var scale = k / _transformStart.k;
68250 var tX = (x / scale - _transformStart.x) * scale;
68251 var tY = (y / scale - _transformStart.y) * scale;
68253 if (context.inIntro()) {
68254 curtainProjection.transform({
68262 _lastPointerEvent = event;
68265 _isTransformed = true;
68266 _transformLast = eventTransform;
68267 utilSetTransform(supersurface, tX, tY, scale);
68269 dispatch.call('move', this, map);
68271 function isInteger(val) {
68272 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
68276 function resetTransform() {
68277 if (!_isTransformed) return false;
68278 utilSetTransform(supersurface, 0, 0);
68279 _isTransformed = false;
68281 if (context.inIntro()) {
68282 curtainProjection.transform(projection.transform());
68288 function redraw(difference, extent) {
68289 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
68290 // It would result in artifacts where differenced entities are redrawn with
68291 // one transform and unchanged entities with another.
68293 if (resetTransform()) {
68294 difference = extent = undefined;
68297 var zoom = map.zoom();
68298 var z = String(~~zoom);
68300 if (surface.attr('data-zoom') !== z) {
68301 surface.attr('data-zoom', z);
68302 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
68305 var lat = map.center()[1];
68306 var lowzoom = linear().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
68307 surface.classed('low-zoom', zoom <= lowzoom(lat));
68310 supersurface.call(context.background());
68311 wrapper.call(drawLayers);
68315 if (map.editableDataEnabled() || map.isInWideSelection()) {
68316 context.loadTiles(projection);
68317 drawEditable(difference, extent);
68322 _transformStart = projection.transform();
68326 var immediateRedraw = function immediateRedraw(difference, extent) {
68327 if (!difference && !extent) cancelPendingRedraw();
68328 redraw(difference, extent);
68331 map.lastPointerEvent = function () {
68332 return _lastPointerEvent;
68335 map.mouse = function (d3_event) {
68336 var event = d3_event || _lastPointerEvent;
68341 while (s = event.sourceEvent) {
68345 return _getMouseCoords(event);
68349 }; // returns Lng/Lat
68352 map.mouseCoordinates = function () {
68353 var coord = map.mouse() || pxCenter();
68354 return projection.invert(coord);
68357 map.dblclickZoomEnable = function (val) {
68358 if (!arguments.length) return _dblClickZoomEnabled;
68359 _dblClickZoomEnabled = val;
68363 map.redrawEnable = function (val) {
68364 if (!arguments.length) return _redrawEnabled;
68365 _redrawEnabled = val;
68369 map.isTransformed = function () {
68370 return _isTransformed;
68373 function setTransform(t2, duration, force) {
68374 var t = projection.transform();
68375 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
68378 _selection.transition().duration(duration).on('start', function () {
68380 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
68382 projection.transform(t2);
68383 _transformStart = t2;
68385 _selection.call(_zoomerPanner.transform, _transformStart);
68391 function setCenterZoom(loc2, z2, duration, force) {
68392 var c = map.center();
68393 var z = map.zoom();
68394 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
68395 var proj = geoRawMercator().transform(projection.transform()); // copy projection
68397 var k2 = clamp$1(geoZoomToScale(z2, TILESIZE), kMin, kMax);
68399 var t = proj.translate();
68400 var point = proj(loc2);
68401 var center = pxCenter();
68402 t[0] += center[0] - point[0];
68403 t[1] += center[1] - point[1];
68404 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
68407 map.pan = function (delta, duration) {
68408 var t = projection.translate();
68409 var k = projection.scale();
68414 _selection.transition().duration(duration).on('start', function () {
68416 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
68418 projection.translate(t);
68419 _transformStart = projection.transform();
68421 _selection.call(_zoomerPanner.transform, _transformStart);
68423 dispatch.call('move', this, map);
68430 map.dimensions = function (val) {
68431 if (!arguments.length) return _dimensions;
68433 drawLayers.dimensions(_dimensions);
68434 context.background().dimensions(_dimensions);
68435 projection.clipExtent([[0, 0], _dimensions]);
68436 _getMouseCoords = utilFastMouse(supersurface.node());
68441 function zoomIn(delta) {
68442 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
68445 function zoomOut(delta) {
68446 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
68449 map.zoomIn = function () {
68453 map.zoomInFurther = function () {
68457 map.canZoomIn = function () {
68458 return map.zoom() < maxZoom;
68461 map.zoomOut = function () {
68465 map.zoomOutFurther = function () {
68469 map.canZoomOut = function () {
68470 return map.zoom() > minZoom;
68473 map.center = function (loc2) {
68474 if (!arguments.length) {
68475 return projection.invert(pxCenter());
68478 if (setCenterZoom(loc2, map.zoom())) {
68479 dispatch.call('move', this, map);
68486 map.unobscuredCenterZoomEase = function (loc, zoom) {
68487 var offset = map.unobscuredOffsetPx();
68488 var proj = geoRawMercator().transform(projection.transform()); // copy projection
68489 // use the target zoom to calculate the offset center
68491 proj.scale(geoZoomToScale(zoom, TILESIZE));
68492 var locPx = proj(loc);
68493 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
68494 var offsetLoc = proj.invert(offsetLocPx);
68495 map.centerZoomEase(offsetLoc, zoom);
68498 map.unobscuredOffsetPx = function () {
68499 var openPane = context.container().select('.map-panes .map-pane.shown');
68501 if (!openPane.empty()) {
68502 return [openPane.node().offsetWidth / 2, 0];
68508 map.zoom = function (z2) {
68509 if (!arguments.length) {
68510 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
68513 if (z2 < _minzoom) {
68514 surface.interrupt();
68515 dispatch.call('hitMinZoom', this, map);
68516 z2 = context.minEditableZoom();
68519 if (setCenterZoom(map.center(), z2)) {
68520 dispatch.call('move', this, map);
68527 map.centerZoom = function (loc2, z2) {
68528 if (setCenterZoom(loc2, z2)) {
68529 dispatch.call('move', this, map);
68536 map.zoomTo = function (entity) {
68537 var extent = entity.extent(context.graph());
68538 if (!isFinite(extent.area())) return map;
68539 var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
68540 return map.centerZoom(extent.center(), z2);
68543 map.centerEase = function (loc2, duration) {
68544 duration = duration || 250;
68545 setCenterZoom(loc2, map.zoom(), duration);
68549 map.zoomEase = function (z2, duration) {
68550 duration = duration || 250;
68551 setCenterZoom(map.center(), z2, duration, false);
68555 map.centerZoomEase = function (loc2, z2, duration) {
68556 duration = duration || 250;
68557 setCenterZoom(loc2, z2, duration, false);
68561 map.transformEase = function (t2, duration) {
68562 duration = duration || 250;
68563 setTransform(t2, duration, false
68569 map.zoomToEase = function (obj, duration) {
68572 if (Array.isArray(obj)) {
68573 obj.forEach(function (entity) {
68574 var entityExtent = entity.extent(context.graph());
68577 extent = entityExtent;
68579 extent = extent.extend(entityExtent);
68583 extent = obj.extent(context.graph());
68586 if (!isFinite(extent.area())) return map;
68587 var z2 = clamp$1(map.trimmedExtentZoom(extent), 0, 20);
68588 return map.centerZoomEase(extent.center(), z2, duration);
68591 map.startEase = function () {
68592 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
68598 map.cancelEase = function () {
68599 _selection.interrupt();
68604 map.extent = function (val) {
68605 if (!arguments.length) {
68606 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
68608 var extent = geoExtent(val);
68609 map.centerZoom(extent.center(), map.extentZoom(extent));
68613 map.trimmedExtent = function (val) {
68614 if (!arguments.length) {
68618 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
68620 var extent = geoExtent(val);
68621 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68625 function calcExtentZoom(extent, dim) {
68626 var tl = projection([extent[0][0], extent[1][1]]);
68627 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
68629 var hFactor = (br[0] - tl[0]) / dim[0];
68630 var vFactor = (br[1] - tl[1]) / dim[1];
68631 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
68632 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
68633 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
68637 map.extentZoom = function (val) {
68638 return calcExtentZoom(geoExtent(val), _dimensions);
68641 map.trimmedExtentZoom = function (val) {
68644 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
68645 return calcExtentZoom(geoExtent(val), trimmed);
68648 map.withinEditableZoom = function () {
68649 return map.zoom() >= context.minEditableZoom();
68652 map.isInWideSelection = function () {
68653 return !map.withinEditableZoom() && context.selectedIDs().length;
68656 map.editableDataEnabled = function (skipZoomCheck) {
68657 var layer = context.layers().layer('osm');
68658 if (!layer || !layer.enabled()) return false;
68659 return skipZoomCheck || map.withinEditableZoom();
68662 map.notesEditable = function () {
68663 var layer = context.layers().layer('notes');
68664 if (!layer || !layer.enabled()) return false;
68665 return map.withinEditableZoom();
68668 map.minzoom = function (val) {
68669 if (!arguments.length) return _minzoom;
68674 map.toggleHighlightEdited = function () {
68675 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
68676 map.pan([0, 0]); // trigger a redraw
68678 dispatch.call('changeHighlighting', this);
68681 map.areaFillOptions = ['wireframe', 'partial', 'full'];
68683 map.activeAreaFill = function (val) {
68684 if (!arguments.length) return corePreferences('area-fill') || 'partial';
68685 corePreferences('area-fill', val);
68687 if (val !== 'wireframe') {
68688 corePreferences('area-fill-toggle', val);
68692 map.pan([0, 0]); // trigger a redraw
68694 dispatch.call('changeAreaFill', this);
68698 map.toggleWireframe = function () {
68699 var activeFill = map.activeAreaFill();
68701 if (activeFill === 'wireframe') {
68702 activeFill = corePreferences('area-fill-toggle') || 'partial';
68704 activeFill = 'wireframe';
68707 map.activeAreaFill(activeFill);
68710 function updateAreaFill() {
68711 var activeFill = map.activeAreaFill();
68712 map.areaFillOptions.forEach(function (opt) {
68713 surface.classed('fill-' + opt, Boolean(opt === activeFill));
68717 map.layers = function () {
68721 map.doubleUpHandler = function () {
68722 return _doubleUpHandler;
68725 return utilRebind(map, dispatch, 'on');
68728 function rendererPhotos(context) {
68729 var dispatch = dispatch$8('change');
68730 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
68731 var _allPhotoTypes = ['flat', 'panoramic'];
68733 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
68736 var _dateFilters = ['fromDate', 'toDate'];
68744 function photos() {}
68746 function updateStorage() {
68747 if (window.mocha) return;
68748 var hash = utilStringQs(window.location.hash);
68749 var enabled = context.layers().all().filter(function (d) {
68750 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
68751 }).map(function (d) {
68755 if (enabled.length) {
68756 hash.photo_overlay = enabled.join(',');
68758 delete hash.photo_overlay;
68761 window.location.replace('#' + utilQsString(hash, true));
68764 photos.overlayLayerIDs = function () {
68768 photos.allPhotoTypes = function () {
68769 return _allPhotoTypes;
68772 photos.dateFilters = function () {
68773 return _dateFilters;
68776 photos.dateFilterValue = function (val) {
68777 return val === _dateFilters[0] ? _fromDate : _toDate;
68780 photos.setDateFilter = function (type, val, updateUrl) {
68781 // validate the date
68782 var date = val && new Date(val);
68784 if (date && !isNaN(date)) {
68785 val = date.toISOString().substr(0, 10);
68790 if (type === _dateFilters[0]) {
68793 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
68794 _toDate = _fromDate;
68798 if (type === _dateFilters[1]) {
68801 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
68802 _fromDate = _toDate;
68806 dispatch.call('change', this);
68811 if (_fromDate || _toDate) {
68812 rangeString = (_fromDate || '') + '_' + (_toDate || '');
68815 setUrlFilterValue('photo_dates', rangeString);
68819 photos.setUsernameFilter = function (val, updateUrl) {
68820 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
68823 val = val.map(function (d) {
68825 }).filter(Boolean);
68833 dispatch.call('change', this);
68839 hashString = _usernames.join(',');
68842 setUrlFilterValue('photo_username', hashString);
68846 function setUrlFilterValue(property, val) {
68847 if (!window.mocha) {
68848 var hash = utilStringQs(window.location.hash);
68851 if (hash[property] === val) return;
68852 hash[property] = val;
68854 if (!(property in hash)) return;
68855 delete hash[property];
68858 window.location.replace('#' + utilQsString(hash, true));
68862 function showsLayer(id) {
68863 var layer = context.layers().layer(id);
68864 return layer && layer.supported() && layer.enabled();
68867 photos.shouldFilterByDate = function () {
68868 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
68871 photos.shouldFilterByPhotoType = function () {
68872 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
68875 photos.shouldFilterByUsername = function () {
68876 return !showsLayer('mapillary') && showsLayer('openstreetcam') && !showsLayer('streetside');
68879 photos.showsPhotoType = function (val) {
68880 if (!photos.shouldFilterByPhotoType()) return true;
68881 return _shownPhotoTypes.indexOf(val) !== -1;
68884 photos.showsFlat = function () {
68885 return photos.showsPhotoType('flat');
68888 photos.showsPanoramic = function () {
68889 return photos.showsPhotoType('panoramic');
68892 photos.fromDate = function () {
68896 photos.toDate = function () {
68900 photos.togglePhotoType = function (val) {
68901 var index = _shownPhotoTypes.indexOf(val);
68903 if (index !== -1) {
68904 _shownPhotoTypes.splice(index, 1);
68906 _shownPhotoTypes.push(val);
68909 dispatch.call('change', this);
68913 photos.usernames = function () {
68917 photos.init = function () {
68918 var hash = utilStringQs(window.location.hash);
68920 if (hash.photo_dates) {
68921 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
68922 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
68923 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
68924 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
68927 if (hash.photo_username) {
68928 this.setUsernameFilter(hash.photo_username, false);
68931 if (hash.photo_overlay) {
68932 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
68933 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
68934 hashOverlayIDs.forEach(function (id) {
68935 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
68936 if (layer && !layer.enabled()) layer.enabled(true);
68941 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
68942 var photoIds = hash.photo.replace(/;/g, ',').split(',');
68943 var photoId = photoIds.length && photoIds[0].trim();
68944 var results = /(.*)\/(.*)/g.exec(photoId);
68946 if (results && results.length >= 3) {
68947 var serviceId = results[1];
68948 var photoKey = results[2];
68949 var service = services[serviceId];
68951 if (service && service.ensureViewerLoaded) {
68952 // if we're showing a photo then make sure its layer is enabled too
68953 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
68954 if (layer && !layer.enabled()) layer.enabled(true);
68955 var baselineTime = Date.now();
68956 service.on('loadedImages.rendererPhotos', function () {
68957 // don't open the viewer if too much time has elapsed
68958 if (Date.now() - baselineTime > 45000) {
68959 service.on('loadedImages.rendererPhotos', null);
68963 if (!service.cachedImage(photoKey)) return;
68964 service.on('loadedImages.rendererPhotos', null);
68965 service.ensureViewerLoaded(context).then(function () {
68966 service.selectImage(context, photoKey).showViewer(context);
68973 context.layers().on('change.rendererPhotos', updateStorage);
68976 return utilRebind(photos, dispatch, 'on');
68979 function uiAccount(context) {
68980 var osm = context.connection();
68982 function update(selection) {
68985 if (!osm.authenticated()) {
68986 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
68990 osm.userDetails(function (err, details) {
68991 var userLink = selection.select('.userLink'),
68992 logoutLink = selection.select('.logoutLink');
68994 logoutLink.html('');
68995 if (err || !details) return;
68996 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
68998 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
69000 if (details.image_url) {
69001 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
69003 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
69007 userLinkA.append('span').attr('class', 'label').html(details.display_name);
69008 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
69009 d3_event.preventDefault();
69015 return function (selection) {
69016 selection.append('li').attr('class', 'userLink').classed('hide', true);
69017 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
69020 osm.on('change.account', function () {
69028 function uiAttribution(context) {
69029 var _selection = select(null);
69031 function render(selection, data, klass) {
69032 var div = selection.selectAll(".".concat(klass)).data([0]);
69033 div = div.enter().append('div').attr('class', klass).merge(div);
69034 var attributions = div.selectAll('.attribution').data(data, function (d) {
69037 attributions.exit().remove();
69038 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
69039 var attribution = select(nodes[i]);
69041 if (d.terms_html) {
69042 attribution.html(d.terms_html);
69047 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
69050 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
69051 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
69052 "default": d.terms_text || d.id || d.name()
69055 if (d.icon && !d.overlay) {
69056 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
69059 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
69060 }).merge(attributions);
69061 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
69062 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
69063 return notice ? [notice] : [];
69065 copyright.exit().remove();
69066 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
69067 copyright.html(String);
69070 function update() {
69071 var baselayer = context.background().baseLayerSource();
69073 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
69075 var z = context.map().zoom();
69076 var overlays = context.background().overlayLayerSources() || [];
69078 _selection.call(render, overlays.filter(function (s) {
69079 return s.validZoom(z);
69080 }), 'overlay-layer-attribution');
69083 return function (selection) {
69084 _selection = selection;
69085 context.background().on('change.attribution', update);
69086 context.map().on('move.attribution', throttle(update, 400, {
69093 function uiContributors(context) {
69094 var osm = context.connection(),
69095 debouncedUpdate = debounce(function () {
69100 wrap = select(null);
69102 function update() {
69105 entities = context.history().intersects(context.map().extent());
69106 entities.forEach(function (entity) {
69107 if (entity && entity.user) users[entity.user] = true;
69109 var u = Object.keys(users),
69110 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
69111 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
69112 var userList = select(document.createElement('span'));
69113 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
69114 return osm.userURL(d);
69115 }).attr('target', '_blank').html(String);
69117 if (u.length > limit) {
69118 var count = select(document.createElement('span'));
69119 var othersNum = u.length - limit + 1;
69120 count.append('a').attr('target', '_blank').attr('href', function () {
69121 return osm.changesetsURL(context.map().center(), context.map().zoom());
69122 }).html(othersNum);
69123 wrap.append('span').html(_t.html('contributors.truncated_list', {
69125 users: userList.html(),
69126 count: count.html()
69129 wrap.append('span').html(_t.html('contributors.list', {
69130 users: userList.html()
69136 wrap.transition().style('opacity', 0);
69137 } else if (hidden) {
69138 wrap.transition().style('opacity', 1);
69142 return function (selection) {
69146 osm.on('loaded.contributors', debouncedUpdate);
69147 context.map().on('move.contributors', debouncedUpdate);
69151 var _popoverID = 0;
69152 function uiPopover(klass) {
69153 var _id = _popoverID++;
69155 var _anchorSelection = select(null);
69157 var popover = function popover(selection) {
69158 _anchorSelection = selection;
69159 selection.each(setup);
69162 var _animation = utilFunctor(false);
69164 var _placement = utilFunctor('top'); // top, bottom, left, right
69167 var _alignment = utilFunctor('center'); // leading, center, trailing
69170 var _scrollContainer = utilFunctor(select(null));
69174 var _displayType = utilFunctor('');
69176 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
69179 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
69181 popover.displayType = function (val) {
69182 if (arguments.length) {
69183 _displayType = utilFunctor(val);
69186 return _displayType;
69190 popover.hasArrow = function (val) {
69191 if (arguments.length) {
69192 _hasArrow = utilFunctor(val);
69199 popover.placement = function (val) {
69200 if (arguments.length) {
69201 _placement = utilFunctor(val);
69208 popover.alignment = function (val) {
69209 if (arguments.length) {
69210 _alignment = utilFunctor(val);
69217 popover.scrollContainer = function (val) {
69218 if (arguments.length) {
69219 _scrollContainer = utilFunctor(val);
69222 return _scrollContainer;
69226 popover.content = function (val) {
69227 if (arguments.length) {
69235 popover.isShown = function () {
69236 var popoverSelection = _anchorSelection.select('.popover-' + _id);
69238 return !popoverSelection.empty() && popoverSelection.classed('in');
69241 popover.show = function () {
69242 _anchorSelection.each(show);
69245 popover.updateContent = function () {
69246 _anchorSelection.each(updateContent);
69249 popover.hide = function () {
69250 _anchorSelection.each(hide);
69253 popover.toggle = function () {
69254 _anchorSelection.each(toggle);
69257 popover.destroy = function (selection, selector) {
69258 // by default, just destroy the current popover
69259 selector = selector || '.popover-' + _id;
69260 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 () {
69261 return this.getAttribute('data-original-title') || this.getAttribute('title');
69262 }).attr('data-original-title', null).selectAll(selector).remove();
69265 popover.destroyAny = function (selection) {
69266 selection.call(popover.destroy, '.popover');
69270 var anchor = select(this);
69272 var animate = _animation.apply(this, arguments);
69274 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
69275 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
69276 enter.append('div').attr('class', 'popover-arrow');
69277 enter.append('div').attr('class', 'popover-inner');
69278 popoverSelection = enter.merge(popoverSelection);
69281 popoverSelection.classed('fade', true);
69284 var display = _displayType.apply(this, arguments);
69286 if (display === 'hover') {
69287 var _lastNonMouseEnterTime;
69289 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
69290 if (d3_event.pointerType) {
69291 if (d3_event.pointerType !== 'mouse') {
69292 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
69295 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
69296 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
69297 // event for non-mouse interactions right after sending
69298 // the correct type pointerenter event. Workaround by discarding
69299 // any mouse event that occurs immediately after a non-mouse event.
69302 } // don't show if buttons are pressed, e.g. during click and drag of map
69305 if (d3_event.buttons !== 0) return;
69306 show.apply(this, arguments);
69307 }).on(_pointerPrefix + 'leave.popover', function () {
69308 hide.apply(this, arguments);
69309 }) // show on focus too for better keyboard navigation support
69310 .on('focus.popover', function () {
69311 show.apply(this, arguments);
69312 }).on('blur.popover', function () {
69313 hide.apply(this, arguments);
69315 } else if (display === 'clickFocus') {
69316 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
69317 d3_event.preventDefault();
69318 d3_event.stopPropagation();
69319 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
69320 d3_event.preventDefault();
69321 d3_event.stopPropagation();
69322 }).on('click.popover', toggle);
69323 popoverSelection // This attribute lets the popover take focus
69324 .attr('tabindex', 0).on('blur.popover', function () {
69325 anchor.each(function () {
69326 hide.apply(this, arguments);
69333 var anchor = select(this);
69334 var popoverSelection = anchor.selectAll('.popover-' + _id);
69336 if (popoverSelection.empty()) {
69337 // popover was removed somehow, put it back
69338 anchor.call(popover.destroy);
69339 anchor.each(setup);
69340 popoverSelection = anchor.selectAll('.popover-' + _id);
69343 popoverSelection.classed('in', true);
69345 var displayType = _displayType.apply(this, arguments);
69347 if (displayType === 'clickFocus') {
69348 anchor.classed('active', true);
69349 popoverSelection.node().focus();
69352 anchor.each(updateContent);
69355 function updateContent() {
69356 var anchor = select(this);
69359 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
69362 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
69363 // set before the dynamic popover size is calculated by the browser
69365 updatePosition.apply(this, arguments);
69366 updatePosition.apply(this, arguments);
69369 function updatePosition() {
69370 var anchor = select(this);
69371 var popoverSelection = anchor.selectAll('.popover-' + _id);
69373 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
69375 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
69376 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
69377 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
69379 var placement = _placement.apply(this, arguments);
69381 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
69383 var alignment = _alignment.apply(this, arguments);
69385 var alignFactor = 0.5;
69387 if (alignment === 'leading') {
69389 } else if (alignment === 'trailing') {
69393 var anchorFrame = getFrame(anchor.node());
69394 var popoverFrame = getFrame(popoverSelection.node());
69397 switch (placement) {
69400 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
69401 y: anchorFrame.y - popoverFrame.h
69407 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
69408 y: anchorFrame.y + anchorFrame.h
69414 x: anchorFrame.x - popoverFrame.w,
69415 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
69421 x: anchorFrame.x + anchorFrame.w,
69422 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
69428 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
69429 var initialPosX = position.x;
69431 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
69432 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
69433 } else if (position.x < 10) {
69437 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
69439 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
69440 arrow.style('left', ~~arrowPosX + 'px');
69443 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
69445 popoverSelection.style('left', null).style('top', null);
69448 function getFrame(node) {
69449 var positionStyle = select(node).style('position');
69451 if (positionStyle === 'absolute' || positionStyle === 'static') {
69453 x: node.offsetLeft - scrollLeft,
69454 y: node.offsetTop - scrollTop,
69455 w: node.offsetWidth,
69456 h: node.offsetHeight
69462 w: node.offsetWidth,
69463 h: node.offsetHeight
69470 var anchor = select(this);
69472 if (_displayType.apply(this, arguments) === 'clickFocus') {
69473 anchor.classed('active', false);
69476 anchor.selectAll('.popover-' + _id).classed('in', false);
69479 function toggle() {
69480 if (select(this).select('.popover-' + _id).classed('in')) {
69481 hide.apply(this, arguments);
69483 show.apply(this, arguments);
69490 function uiTooltip(klass) {
69491 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
69493 var _title = function _title() {
69494 var title = this.getAttribute('data-original-title');
69499 title = this.getAttribute('title');
69500 this.removeAttribute('title');
69501 this.setAttribute('data-original-title', title);
69507 var _heading = utilFunctor(null);
69509 var _keys = utilFunctor(null);
69511 tooltip.title = function (val) {
69512 if (!arguments.length) return _title;
69513 _title = utilFunctor(val);
69517 tooltip.heading = function (val) {
69518 if (!arguments.length) return _heading;
69519 _heading = utilFunctor(val);
69523 tooltip.keys = function (val) {
69524 if (!arguments.length) return _keys;
69525 _keys = utilFunctor(val);
69529 tooltip.content(function () {
69530 var heading = _heading.apply(this, arguments);
69532 var text = _title.apply(this, arguments);
69534 var keys = _keys.apply(this, arguments);
69536 return function (selection) {
69537 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
69538 headingSelect.exit().remove();
69539 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
69540 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
69541 textSelect.exit().remove();
69542 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
69543 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
69544 keyhintWrap.exit().remove();
69545 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
69546 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
69547 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
69548 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
69556 function uiEditMenu(context) {
69557 var dispatch = dispatch$8('toggled');
69559 var _menu = select(null);
69561 var _operations = []; // the position the menu should be displayed relative to
69563 var _anchorLoc = [0, 0];
69564 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
69566 var _triggerType = '';
69567 var _vpTopMargin = 85; // viewport top margin
69569 var _vpBottomMargin = 45; // viewport bottom margin
69571 var _vpSideMargin = 35; // viewport side margin
69573 var _menuTop = false;
69577 var _menuWidth; // hardcode these values to make menu positioning easier
69580 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
69582 var _tooltipWidth = 210; // offset the menu slightly from the target location
69584 var _menuSideMargin = 10;
69585 var _tooltips = [];
69587 var editMenu = function editMenu(selection) {
69588 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
69590 var ops = _operations.filter(function (op) {
69591 return !isTouchMenu || !op.mouseOnly;
69594 if (!ops.length) return;
69595 _tooltips = []; // Position the menu above the anchor for stylus and finger input
69596 // since the mapper's hand likely obscures the screen below the anchor
69598 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
69600 var showLabels = isTouchMenu;
69601 var buttonHeight = showLabels ? 32 : 34;
69604 // Get a general idea of the width based on the length of the label
69605 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
69606 return op.title.length;
69612 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
69613 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
69615 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
69618 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
69619 return 'edit-menu-item edit-menu-item-' + d.id;
69620 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
69621 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
69622 // don't let button presses also act as map input - #1869
69623 d3_event.stopPropagation();
69624 }).on('mouseenter.highlight', function (d3_event, d) {
69625 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
69626 utilHighlightEntities(d.relatedEntityIds(), true, context);
69627 }).on('mouseleave.highlight', function (d3_event, d) {
69628 if (!d.relatedEntityIds) return;
69629 utilHighlightEntities(d.relatedEntityIds(), false, context);
69631 buttonsEnter.each(function (d) {
69632 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
69634 _tooltips.push(tooltip);
69636 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
69640 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
69646 buttonsEnter.merge(buttons).classed('disabled', function (d) {
69647 return d.disabled();
69650 var initialScale = context.projection.scale();
69651 context.map().on('move.edit-menu', function () {
69652 if (initialScale !== context.projection.scale()) {
69655 }).on('drawn.edit-menu', function (info) {
69656 if (info.full) updatePosition();
69658 var lastPointerUpType; // `pointerup` is always called before `click`
69660 function pointerup(d3_event) {
69661 lastPointerUpType = d3_event.pointerType;
69664 function click(d3_event, operation) {
69665 d3_event.stopPropagation();
69667 if (operation.relatedEntityIds) {
69668 utilHighlightEntities(operation.relatedEntityIds(), false, context);
69671 if (operation.disabled()) {
69672 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
69673 // there are no tooltips for touch interactions so flash feedback instead
69674 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
69677 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
69678 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
69685 lastPointerUpType = null;
69688 dispatch.call('toggled', this, true);
69691 function updatePosition() {
69692 if (!_menu || _menu.empty()) return;
69693 var anchorLoc = context.projection(_anchorLocLonLat);
69694 var viewport = context.surfaceRect();
69696 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
69697 // close the menu if it's gone offscreen
69702 var menuLeft = displayOnLeft(viewport);
69703 var offset = [0, 0];
69704 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
69707 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
69708 // menu is near top viewport edge, shift downward
69709 offset[1] = -anchorLoc[1] + _vpTopMargin;
69711 offset[1] = -_menuHeight;
69714 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
69715 // menu is near bottom viewport edge, shift upwards
69716 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
69722 var origin = geoVecAdd(anchorLoc, offset);
69724 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
69726 var tooltipSide = tooltipPosition(viewport, menuLeft);
69728 _tooltips.forEach(function (tooltip) {
69729 tooltip.placement(tooltipSide);
69732 function displayOnLeft(viewport) {
69733 if (_mainLocalizer.textDirection() === 'ltr') {
69734 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
69735 // right menu would be too close to the right viewport edge, go left
69737 } // prefer right menu
69743 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
69744 // left menu would be too close to the left viewport edge, go right
69746 } // prefer left menu
69753 function tooltipPosition(viewport, menuLeft) {
69754 if (_mainLocalizer.textDirection() === 'ltr') {
69756 // if there's not room for a right-side menu then there definitely
69757 // isn't room for right-side tooltips
69761 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
69762 // right tooltips would be too close to the right viewport edge, go left
69764 } // prefer right tooltips
69774 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
69775 // left tooltips would be too close to the left viewport edge, go right
69777 } // prefer left tooltips
69785 editMenu.close = function () {
69786 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
69791 dispatch.call('toggled', this, false);
69794 editMenu.anchorLoc = function (val) {
69795 if (!arguments.length) return _anchorLoc;
69797 _anchorLocLonLat = context.projection.invert(_anchorLoc);
69801 editMenu.triggerType = function (val) {
69802 if (!arguments.length) return _triggerType;
69803 _triggerType = val;
69807 editMenu.operations = function (val) {
69808 if (!arguments.length) return _operations;
69813 return utilRebind(editMenu, dispatch, 'on');
69816 function uiFeatureInfo(context) {
69817 function update(selection) {
69818 var features = context.features();
69819 var stats = features.stats();
69821 var hiddenList = features.hidden().map(function (k) {
69824 return _t('inspector.title_count', {
69825 title: _t.html('feature.' + k + '.description'),
69831 }).filter(Boolean);
69832 selection.html('');
69834 if (hiddenList.length) {
69835 var tooltipBehavior = uiTooltip().placement('top').title(function () {
69836 return hiddenList.join('<br/>');
69838 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
69840 })).call(tooltipBehavior).on('click', function (d3_event) {
69841 tooltipBehavior.hide();
69842 d3_event.preventDefault(); // open the Map Data pane
69844 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
69848 selection.classed('hide', !hiddenList.length);
69851 return function (selection) {
69853 context.features().on('change.feature_info', function () {
69859 function uiFlash(context) {
69862 var _duration = 2000;
69863 var _iconName = '#iD-icon-no';
69864 var _iconClass = 'disabled';
69869 _flashTimer.stop();
69872 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
69873 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
69874 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
69876 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
69877 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
69878 iconEnter.append('circle').attr('r', 9);
69879 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
69880 contentEnter.append('div').attr('class', 'flash-text'); // Update
69882 content = content.merge(contentEnter);
69883 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
69884 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
69885 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
69886 _flashTimer = d3_timeout(function () {
69887 _flashTimer = null;
69888 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
69889 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
69894 flash.duration = function (_) {
69895 if (!arguments.length) return _duration;
69900 flash.label = function (_) {
69901 if (!arguments.length) return _label;
69906 flash.iconName = function (_) {
69907 if (!arguments.length) return _iconName;
69912 flash.iconClass = function (_) {
69913 if (!arguments.length) return _iconClass;
69921 function uiFullScreen(context) {
69922 var element = context.container().node(); // var button = d3_select(null);
69924 function getFullScreenFn() {
69925 if (element.requestFullscreen) {
69926 return element.requestFullscreen;
69927 } else if (element.msRequestFullscreen) {
69928 return element.msRequestFullscreen;
69929 } else if (element.mozRequestFullScreen) {
69930 return element.mozRequestFullScreen;
69931 } else if (element.webkitRequestFullscreen) {
69932 return element.webkitRequestFullscreen;
69936 function getExitFullScreenFn() {
69937 if (document.exitFullscreen) {
69938 return document.exitFullscreen;
69939 } else if (document.msExitFullscreen) {
69940 return document.msExitFullscreen;
69941 } else if (document.mozCancelFullScreen) {
69942 return document.mozCancelFullScreen;
69943 } else if (document.webkitExitFullscreen) {
69944 return document.webkitExitFullscreen;
69948 function isFullScreen() {
69949 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
69952 function isSupported() {
69953 return !!getFullScreenFn();
69956 function fullScreen(d3_event) {
69957 d3_event.preventDefault();
69959 if (!isFullScreen()) {
69960 // button.classed('active', true);
69961 getFullScreenFn().apply(element);
69963 // button.classed('active', false);
69964 getExitFullScreenFn().apply(document);
69968 return function () {
69970 if (!isSupported()) return; // button = selection.append('button')
69971 // .attr('title', t('full_screen'))
69972 // .on('click', fullScreen)
69974 // button.append('span')
69975 // .attr('class', 'icon full-screen');
69977 var detected = utilDetect();
69978 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
69979 context.keybinding().on(keys, fullScreen);
69983 function uiGeolocate(context) {
69984 var _geolocationOptions = {
69985 // prioritize speed and power usage over precision
69986 enableHighAccuracy: false,
69987 // don't hang indefinitely getting the location
69988 timeout: 6000 // 6sec
69992 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
69994 var _layer = context.layers().layer('geolocate');
70002 var _button = select(null);
70005 if (context.inIntro()) return;
70007 if (!_layer.enabled() && !_locating.isShown()) {
70008 // This timeout ensures that we still call finish() even if
70009 // the user declines to share their location in Firefox
70010 _timeoutID = setTimeout(error, 10000
70013 context.container().call(_locating); // get the latest position even if we already have one
70015 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
70019 _layer.enabled(null, false);
70021 updateButtonState();
70025 function zoomTo() {
70026 context.enter(modeBrowse(context));
70027 var map = context.map();
70029 _layer.enabled(_position, true);
70031 updateButtonState();
70032 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
70035 function success(geolocation) {
70036 _position = geolocation;
70037 var coords = _position.coords;
70038 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
70045 // use the position from a previous call if we have one
70048 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
70054 function finish() {
70055 _locating.close(); // unblock ui
70059 clearTimeout(_timeoutID);
70062 _timeoutID = undefined;
70065 function updateButtonState() {
70066 _button.classed('active', _layer.enabled());
70069 return function (selection) {
70070 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
70071 _button = selection.append('button').on('click', click).call(svgIcon('#iD-icon-geolocate', 'light')).call(uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_t.html('geolocate.title')).keys([_t('geolocate.key')]));
70072 context.keybinding().on(_t('geolocate.key'), click);
70076 function uiPanelBackground(context) {
70077 var background = context.background();
70078 var _currSourceName = null;
70079 var _metadata = {};
70080 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
70082 var debouncedRedraw = debounce(redraw, 250);
70084 function redraw(selection) {
70085 var source = background.baseLayerSource();
70086 if (!source) return;
70087 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
70088 var sourceLabel = source.label();
70090 if (_currSourceName !== sourceLabel) {
70091 _currSourceName = sourceLabel;
70095 selection.html('');
70096 var list = selection.append('ul').attr('class', 'background-info');
70097 list.append('li').html(_currSourceName);
70099 _metadataKeys.forEach(function (k) {
70100 // DigitalGlobe vintage is available in raster layers for now.
70101 if (isDG && k === 'vintage') return;
70102 list.append('li').attr('class', 'background-info-list-' + k).classed('hide', !_metadata[k]).html(_t.html('info_panels.background.' + k) + ':').append('span').attr('class', 'background-info-span-' + k).html(_metadata[k]);
70105 debouncedGetMetadata(selection);
70106 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
70107 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
70108 d3_event.preventDefault();
70109 context.setDebug('tile', !context.getDebug('tile'));
70110 selection.call(redraw);
70114 var key = source.id + '-vintage';
70115 var sourceVintage = context.background().findSource(key);
70116 var showsVintage = context.background().showsLayer(sourceVintage);
70117 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
70118 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
70119 d3_event.preventDefault();
70120 context.background().toggleOverlayLayer(sourceVintage);
70121 selection.call(redraw);
70123 } // disable if necessary
70126 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
70127 if (source.id !== layerId) {
70128 var key = layerId + '-vintage';
70129 var sourceVintage = context.background().findSource(key);
70131 if (context.background().showsLayer(sourceVintage)) {
70132 context.background().toggleOverlayLayer(sourceVintage);
70138 var debouncedGetMetadata = debounce(getMetadata, 250);
70140 function getMetadata(selection) {
70141 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
70143 if (tile.empty()) return;
70144 var sourceName = _currSourceName;
70145 var d = tile.datum();
70146 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
70147 var center = context.map().center(); // update zoom
70149 _metadata.zoom = String(zoom);
70150 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
70151 if (!d || !d.length >= 3) return;
70152 background.baseLayerSource().getMetadata(center, d, function (err, result) {
70153 if (err || _currSourceName !== sourceName) return; // update vintage
70155 var vintage = result.vintage;
70156 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
70157 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
70159 _metadataKeys.forEach(function (k) {
70160 if (k === 'zoom' || k === 'vintage') return; // done already
70162 var val = result[k];
70163 _metadata[k] = val;
70164 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
70169 var panel = function panel(selection) {
70170 selection.call(redraw);
70171 context.map().on('drawn.info-background', function () {
70172 selection.call(debouncedRedraw);
70173 }).on('move.info-background', function () {
70174 selection.call(debouncedGetMetadata);
70178 panel.off = function () {
70179 context.map().on('drawn.info-background', null).on('move.info-background', null);
70182 panel.id = 'background';
70183 panel.label = _t.html('info_panels.background.title');
70184 panel.key = _t('info_panels.background.key');
70188 function uiPanelHistory(context) {
70191 function displayTimestamp(timestamp) {
70192 if (!timestamp) return _t('info_panels.history.unknown');
70201 var d = new Date(timestamp);
70202 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
70203 return d.toLocaleString(_mainLocalizer.localeCode(), options);
70206 function displayUser(selection, userName) {
70208 selection.append('span').html(_t.html('info_panels.history.unknown'));
70212 selection.append('span').attr('class', 'user-name').html(userName);
70213 var links = selection.append('div').attr('class', 'links');
70216 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
70219 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
70222 function displayChangeset(selection, changeset) {
70224 selection.append('span').html(_t.html('info_panels.history.unknown'));
70228 selection.append('span').attr('class', 'changeset-id').html(changeset);
70229 var links = selection.append('div').attr('class', 'links');
70232 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
70235 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
70236 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
70239 function redraw(selection) {
70240 var selectedNoteID = context.selectedNoteID();
70241 osm = context.connection();
70242 var selected, note, entity;
70244 if (selectedNoteID && osm) {
70246 selected = [_t('note.note') + ' ' + selectedNoteID];
70247 note = osm.getNote(selectedNoteID);
70249 // selected 1..n entities
70250 selected = context.selectedIDs().filter(function (e) {
70251 return context.hasEntity(e);
70254 if (selected.length) {
70255 entity = context.entity(selected[0]);
70259 var singular = selected.length === 1 ? selected[0] : null;
70260 selection.html('');
70261 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
70264 if (!singular) return;
70267 selection.call(redrawEntity, entity);
70269 selection.call(redrawNote, note);
70273 function redrawNote(selection, note) {
70274 if (!note || note.isNew()) {
70275 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
70279 var list = selection.append('ul');
70280 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
70282 if (note.comments.length) {
70283 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
70284 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
70288 selection.append('a').attr('class', 'view-history-on-osm').attr('target', '_blank').attr('href', osm.noteURL(note)).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('info_panels.history.note_link_text'));
70292 function redrawEntity(selection, entity) {
70293 if (!entity || entity.isNew()) {
70294 selection.append('div').html(_t.html('info_panels.history.no_history'));
70298 var links = selection.append('div').attr('class', 'links');
70301 links.append('a').attr('class', 'view-history-on-osm').attr('href', osm.historyURL(entity)).attr('target', '_blank').attr('title', _t('info_panels.history.link_text')).html('OSM');
70304 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).html('PeWu');
70305 var list = selection.append('ul');
70306 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
70307 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
70308 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
70309 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
70312 var panel = function panel(selection) {
70313 selection.call(redraw);
70314 context.map().on('drawn.info-history', function () {
70315 selection.call(redraw);
70317 context.on('enter.info-history', function () {
70318 selection.call(redraw);
70322 panel.off = function () {
70323 context.map().on('drawn.info-history', null);
70324 context.on('enter.info-history', null);
70327 panel.id = 'history';
70328 panel.label = _t.html('info_panels.history.title');
70329 panel.key = _t('info_panels.history.key');
70333 var OSM_PRECISION = 7;
70335 * Returns a localized representation of the given length measurement.
70337 * @param {Number} m area in meters
70338 * @param {Boolean} isImperial true for U.S. customary units; false for metric
70341 function displayLength(m, isImperial) {
70342 var d = m * (isImperial ? 3.28084 : 1);
70355 unit = 'kilometers';
70361 return _t('units.' + unit, {
70362 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
70363 maximumSignificantDigits: 4
70368 * Returns a localized representation of the given area measurement.
70370 * @param {Number} m2 area in square meters
70371 * @param {Boolean} isImperial true for U.S. customary units; false for metric
70374 function displayArea(m2, isImperial) {
70375 var locale = _mainLocalizer.localeCode();
70376 var d = m2 * (isImperial ? 10.7639111056 : 1);
70382 if (d >= 6969600) {
70383 // > 0.25mi² show mi²
70385 unit1 = 'square_miles';
70388 unit1 = 'square_feet';
70391 if (d > 4356 && d < 43560000) {
70392 // 0.1 - 1000 acres
70398 // > 0.25km² show km²
70400 unit1 = 'square_kilometers';
70403 unit1 = 'square_meters';
70406 if (d > 1000 && d < 10000000) {
70407 // 0.1 - 1000 hectares
70409 unit2 = 'hectares';
70413 area = _t('units.' + unit1, {
70414 quantity: d1.toLocaleString(locale, {
70415 maximumSignificantDigits: 4
70420 return _t('units.area_pair', {
70422 area2: _t('units.' + unit2, {
70423 quantity: d2.toLocaleString(locale, {
70424 maximumSignificantDigits: 2
70433 function wrap(x, min, max) {
70435 return ((x - min) % d + d) % d + min;
70438 function clamp(x, min, max) {
70439 return Math.max(min, Math.min(x, max));
70442 function displayCoordinate(deg, pos, neg) {
70443 var locale = _mainLocalizer.localeCode();
70444 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
70445 var sec = (min - Math.floor(min)) * 60;
70446 var displayDegrees = _t('units.arcdegrees', {
70447 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
70449 var displayCoordinate;
70451 if (Math.floor(sec) > 0) {
70452 displayCoordinate = displayDegrees + _t('units.arcminutes', {
70453 quantity: Math.floor(min).toLocaleString(locale)
70454 }) + _t('units.arcseconds', {
70455 quantity: Math.round(sec).toLocaleString(locale)
70457 } else if (Math.floor(min) > 0) {
70458 displayCoordinate = displayDegrees + _t('units.arcminutes', {
70459 quantity: Math.round(min).toLocaleString(locale)
70462 displayCoordinate = _t('units.arcdegrees', {
70463 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
70468 return displayCoordinate;
70470 return _t('units.coordinate', {
70471 coordinate: displayCoordinate,
70472 direction: _t('units.' + (deg > 0 ? pos : neg))
70477 * Returns given coordinate pair in degree-minute-second format.
70479 * @param {Array<Number>} coord longitude and latitude
70483 function dmsCoordinatePair(coord) {
70484 return _t('units.coordinate_pair', {
70485 latitude: displayCoordinate(clamp(coord[1], -90, 90), 'north', 'south'),
70486 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
70490 * Returns the given coordinate pair in decimal format.
70491 * note: unlocalized to avoid comma ambiguity - see #4765
70493 * @param {Array<Number>} coord longitude and latitude
70496 function decimalCoordinatePair(coord) {
70497 return _t('units.coordinate_pair', {
70498 latitude: clamp(coord[1], -90, 90).toFixed(OSM_PRECISION),
70499 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
70503 function uiPanelLocation(context) {
70504 var currLocation = '';
70506 function redraw(selection) {
70507 selection.html('');
70508 var list = selection.append('ul'); // Mouse coordinates
70510 var coord = context.map().mouseCoordinates();
70512 if (coord.some(isNaN)) {
70513 coord = context.map().center();
70516 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
70518 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
70519 debouncedGetLocation(selection, coord);
70522 var debouncedGetLocation = debounce(getLocation, 250);
70524 function getLocation(selection, coord) {
70525 if (!services.geocoder) {
70526 currLocation = _t('info_panels.location.unknown_location');
70527 selection.selectAll('.location-info').html(currLocation);
70529 services.geocoder.reverse(coord, function (err, result) {
70530 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
70531 selection.selectAll('.location-info').html(currLocation);
70536 var panel = function panel(selection) {
70537 selection.call(redraw);
70538 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
70539 selection.call(redraw);
70543 panel.off = function () {
70544 context.surface().on('.info-location', null);
70547 panel.id = 'location';
70548 panel.label = _t.html('info_panels.location.title');
70549 panel.key = _t('info_panels.location.key');
70553 function uiPanelMeasurement(context) {
70554 function radiansToMeters(r) {
70555 // using WGS84 authalic radius (6371007.1809 m)
70556 return r * 6371007.1809;
70559 function steradiansToSqmeters(r) {
70560 // http://gis.stackexchange.com/a/124857/40446
70561 return r / (4 * Math.PI) * 510065621724000;
70564 function toLineString(feature) {
70565 if (feature.type === 'LineString') return feature;
70567 type: 'LineString',
70571 if (feature.type === 'Polygon') {
70572 result.coordinates = feature.coordinates[0];
70573 } else if (feature.type === 'MultiPolygon') {
70574 result.coordinates = feature.coordinates[0][0];
70580 var _isImperial = !_mainLocalizer.usesMetric();
70582 function redraw(selection) {
70583 var graph = context.graph();
70584 var selectedNoteID = context.selectedNoteID();
70585 var osm = services.osm;
70586 var localeCode = _mainLocalizer.localeCode();
70588 var center, location, centroid;
70589 var closed, geometry;
70590 var totalNodeCount,
70595 if (selectedNoteID && osm) {
70597 var note = osm.getNote(selectedNoteID);
70598 heading = _t('note.note') + ' ' + selectedNoteID;
70599 location = note.loc;
70602 // selected 1..n entities
70603 var selectedIDs = context.selectedIDs().filter(function (id) {
70604 return context.hasEntity(id);
70606 var selected = selectedIDs.map(function (id) {
70607 return context.entity(id);
70609 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
70613 if (selected.length) {
70614 var extent = geoExtent();
70616 for (var i in selected) {
70617 var entity = selected[i];
70619 extent._extend(entity.extent(graph));
70621 geometry = entity.geometry(graph);
70623 if (geometry === 'line' || geometry === 'area') {
70624 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
70625 var feature = entity.asGeoJSON(graph);
70626 length += radiansToMeters(d3_geoLength(toLineString(feature)));
70627 centroid = d3_geoPath(context.projection).centroid(entity.asGeoJSON(graph));
70628 centroid = centroid && context.projection.invert(centroid);
70630 if (!centroid || !isFinite(centroid[0]) || !isFinite(centroid[1])) {
70631 centroid = entity.extent(graph).center();
70635 area += steradiansToSqmeters(entity.area(graph));
70640 if (selected.length > 1) {
70646 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
70647 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
70650 if (selected.length === 1 && selected[0].type === 'node') {
70651 location = selected[0].loc;
70653 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
70656 if (!location && !centroid) {
70657 center = extent.center();
70662 selection.html('');
70665 selection.append('h4').attr('class', 'measurement-heading').html(heading);
70668 var list = selection.append('ul');
70672 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
70675 if (totalNodeCount) {
70676 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
70680 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
70684 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
70687 if (typeof distance === 'number') {
70688 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
70692 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
70693 coordItem.append('span').html(dmsCoordinatePair(location));
70694 coordItem.append('span').html(decimalCoordinatePair(location));
70698 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
70699 coordItem.append('span').html(dmsCoordinatePair(centroid));
70700 coordItem.append('span').html(decimalCoordinatePair(centroid));
70704 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
70705 coordItem.append('span').html(dmsCoordinatePair(center));
70706 coordItem.append('span').html(decimalCoordinatePair(center));
70709 if (length || area || typeof distance === 'number') {
70710 var toggle = _isImperial ? 'imperial' : 'metric';
70711 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
70712 d3_event.preventDefault();
70713 _isImperial = !_isImperial;
70714 selection.call(redraw);
70719 var panel = function panel(selection) {
70720 selection.call(redraw);
70721 context.map().on('drawn.info-measurement', function () {
70722 selection.call(redraw);
70724 context.on('enter.info-measurement', function () {
70725 selection.call(redraw);
70729 panel.off = function () {
70730 context.map().on('drawn.info-measurement', null);
70731 context.on('enter.info-measurement', null);
70734 panel.id = 'measurement';
70735 panel.label = _t.html('info_panels.measurement.title');
70736 panel.key = _t('info_panels.measurement.key');
70740 var uiInfoPanels = {
70741 background: uiPanelBackground,
70742 history: uiPanelHistory,
70743 location: uiPanelLocation,
70744 measurement: uiPanelMeasurement
70747 function uiInfo(context) {
70748 var ids = Object.keys(uiInfoPanels);
70749 var wasActive = ['measurement'];
70751 var active = {}; // create panels
70753 ids.forEach(function (k) {
70755 panels[k] = uiInfoPanels[k](context);
70760 function info(selection) {
70761 function redraw() {
70762 var activeids = ids.filter(function (k) {
70765 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
70768 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
70769 select(this).call(panels[d].off).remove();
70771 var enter = containers.enter().append('div').attr('class', function (d) {
70772 return 'fillD2 panel-container panel-container-' + d;
70774 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
70775 var title = enter.append('div').attr('class', 'panel-title fillD2');
70776 title.append('h3').html(function (d) {
70777 return panels[d].label;
70779 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
70780 d3_event.stopImmediatePropagation();
70781 d3_event.preventDefault();
70783 }).call(svgIcon('#iD-icon-close'));
70784 enter.append('div').attr('class', function (d) {
70785 return 'panel-content panel-content-' + d;
70786 }); // redraw the panels
70788 infoPanels.selectAll('.panel-content').each(function (d) {
70789 select(this).call(panels[d]);
70793 info.toggle = function (which) {
70794 var activeids = ids.filter(function (k) {
70800 active[which] = !active[which];
70802 if (activeids.length === 1 && activeids[0] === which) {
70803 // none active anymore
70804 wasActive = [which];
70807 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
70810 if (activeids.length) {
70811 wasActive = activeids;
70812 activeids.forEach(function (k) {
70816 wasActive.forEach(function (k) {
70825 var infoPanels = selection.selectAll('.info-panels').data([0]);
70826 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
70828 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
70829 d3_event.stopImmediatePropagation();
70830 d3_event.preventDefault();
70833 ids.forEach(function (k) {
70834 var key = _t('info_panels.' + k + '.key', {
70838 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
70839 d3_event.stopImmediatePropagation();
70840 d3_event.preventDefault();
70849 function pointBox(loc, context) {
70850 var rect = context.surfaceRect();
70851 var point = context.curtainProjection(loc);
70853 left: point[0] + rect.left - 40,
70854 top: point[1] + rect.top - 60,
70859 function pad(locOrBox, padding, context) {
70862 if (locOrBox instanceof Array) {
70863 var rect = context.surfaceRect();
70864 var point = context.curtainProjection(locOrBox);
70866 left: point[0] + rect.left,
70867 top: point[1] + rect.top
70874 left: box.left - padding,
70875 top: box.top - padding,
70876 width: (box.width || 0) + 2 * padding,
70877 height: (box.width || 0) + 2 * padding
70880 function icon(name, svgklass, useklass) {
70881 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
70883 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
70884 // label replacements suitable for tutorials and documentation. Optionally supplemented
70885 // with custom `replacements`
70887 function helpHtml(id, replacements) {
70888 // only load these the first time
70889 if (!helpStringReplacements) {
70890 helpStringReplacements = {
70891 // insert icons corresponding to various UI elements
70892 point_icon: icon('#iD-icon-point', 'inline'),
70893 line_icon: icon('#iD-icon-line', 'inline'),
70894 area_icon: icon('#iD-icon-area', 'inline'),
70895 note_icon: icon('#iD-icon-note', 'inline add-note'),
70896 plus: icon('#iD-icon-plus', 'inline'),
70897 minus: icon('#iD-icon-minus', 'inline'),
70898 layers_icon: icon('#iD-icon-layers', 'inline'),
70899 data_icon: icon('#iD-icon-data', 'inline'),
70900 inspect: icon('#iD-icon-inspect', 'inline'),
70901 help_icon: icon('#iD-icon-help', 'inline'),
70902 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
70903 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
70904 save_icon: icon('#iD-icon-save', 'inline'),
70906 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
70907 continue_icon: icon('#iD-operation-continue', 'inline operation'),
70908 copy_icon: icon('#iD-operation-copy', 'inline operation'),
70909 delete_icon: icon('#iD-operation-delete', 'inline operation'),
70910 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
70911 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
70912 extract_icon: icon('#iD-operation-extract', 'inline operation'),
70913 merge_icon: icon('#iD-operation-merge', 'inline operation'),
70914 move_icon: icon('#iD-operation-move', 'inline operation'),
70915 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
70916 paste_icon: icon('#iD-operation-paste', 'inline operation'),
70917 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
70918 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
70919 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
70920 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
70921 split_icon: icon('#iD-operation-split', 'inline operation'),
70922 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
70923 // interaction icons
70924 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
70925 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
70926 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
70927 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
70928 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
70929 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
70930 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
70931 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
70932 // insert keys; may be localized and platform-dependent
70933 shift: uiCmd.display('⇧'),
70934 alt: uiCmd.display('⌥'),
70935 "return": uiCmd.display('↵'),
70936 esc: _t.html('shortcuts.key.esc'),
70937 space: _t.html('shortcuts.key.space'),
70938 add_note_key: _t.html('modes.add_note.key'),
70939 help_key: _t.html('help.key'),
70940 shortcuts_key: _t.html('shortcuts.toggle.key'),
70941 // reference localized UI labels directly so that they'll always match
70942 save: _t.html('save.title'),
70943 undo: _t.html('undo.title'),
70944 redo: _t.html('redo.title'),
70945 upload: _t.html('commit.save'),
70946 point: _t.html('modes.add_point.title'),
70947 line: _t.html('modes.add_line.title'),
70948 area: _t.html('modes.add_area.title'),
70949 note: _t.html('modes.add_note.label'),
70950 circularize: _t.html('operations.circularize.title'),
70951 "continue": _t.html('operations.continue.title'),
70952 copy: _t.html('operations.copy.title'),
70953 "delete": _t.html('operations.delete.title'),
70954 disconnect: _t.html('operations.disconnect.title'),
70955 downgrade: _t.html('operations.downgrade.title'),
70956 extract: _t.html('operations.extract.title'),
70957 merge: _t.html('operations.merge.title'),
70958 move: _t.html('operations.move.title'),
70959 orthogonalize: _t.html('operations.orthogonalize.title'),
70960 paste: _t.html('operations.paste.title'),
70961 reflect_long: _t.html('operations.reflect.title.long'),
70962 reflect_short: _t.html('operations.reflect.title.short'),
70963 reverse: _t.html('operations.reverse.title'),
70964 rotate: _t.html('operations.rotate.title'),
70965 split: _t.html('operations.split.title'),
70966 straighten: _t.html('operations.straighten.title'),
70967 map_data: _t.html('map_data.title'),
70968 osm_notes: _t.html('map_data.layers.notes.title'),
70969 fields: _t.html('inspector.fields'),
70970 tags: _t.html('inspector.tags'),
70971 relations: _t.html('inspector.relations'),
70972 new_relation: _t.html('inspector.new_relation'),
70973 turn_restrictions: _t.html('_tagging.presets.fields.restrictions.label'),
70974 background_settings: _t.html('background.description'),
70975 imagery_offset: _t.html('background.fix_misalignment'),
70976 start_the_walkthrough: _t.html('splash.walkthrough'),
70977 help: _t.html('help.title'),
70978 ok: _t.html('intro.ok')
70984 if (replacements) {
70985 reps = Object.assign(replacements, helpStringReplacements);
70987 reps = helpStringReplacements;
70990 return _t.html(id, reps) // use keyboard key styling for shortcuts
70991 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
70994 function slugify(text) {
70995 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
70996 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
70997 .replace(/\-\-+/g, '-') // Replace multiple - with single -
70998 .replace(/^-+/, '') // Trim - from start of text
70999 .replace(/-+$/, ''); // Trim - from end of text
71000 } // console warning for missing walkthrough names
71003 var missingStrings = {};
71005 function checkKey(key, text) {
71007 "default": undefined
71008 }) === undefined) {
71009 if (missingStrings.hasOwnProperty(key)) return; // warn once
71011 missingStrings[key] = text;
71012 var missing = key + ': ' + text;
71013 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
71017 function localize(obj) {
71018 var key; // Assign name if entity has one..
71020 var name = obj.tags && obj.tags.name;
71023 key = 'intro.graph.name.' + slugify(name);
71024 obj.tags.name = _t(key, {
71027 checkKey(key, name);
71028 } // Assign street name if entity has one..
71031 var street = obj.tags && obj.tags['addr:street'];
71034 key = 'intro.graph.name.' + slugify(street);
71035 obj.tags['addr:street'] = _t(key, {
71038 checkKey(key, street); // Add address details common across walkthrough..
71040 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
71041 addrTags.forEach(function (k) {
71042 var key = 'intro.graph.' + k;
71043 var tag = 'addr:' + k;
71044 var val = obj.tags && obj.tags[tag];
71045 var str = _t(key, {
71050 if (str.match(/^<.*>$/) !== null) {
71051 delete obj.tags[tag];
71053 obj.tags[tag] = str;
71060 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
71062 function isMostlySquare(points) {
71063 // note: uses 15 here instead of the 12 from actionOrthogonalize because
71064 // actionOrthogonalize can actually straighten some larger angles as it iterates
71065 var threshold = 15; // degrees within right or straight
71067 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
71069 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
71071 for (var i = 0; i < points.length; i++) {
71072 var a = points[(i - 1 + points.length) % points.length];
71073 var origin = points[i];
71074 var b = points[(i + 1) % points.length];
71075 var dotp = geoVecNormalizedDot(a, b, origin);
71076 var mag = Math.abs(dotp);
71078 if (mag > lowerBound && mag < upperBound) {
71085 function selectMenuItem(context, operation) {
71086 return context.container().select('.edit-menu .edit-menu-item-' + operation);
71088 function transitionTime(point1, point2) {
71089 var distance = geoSphericalDistance(point1, point2);
71091 if (distance === 0) {
71093 } else if (distance < 80) {
71100 // hide class, which sets display=none, and a d3 transition for opacity.
71101 // this will cause blinking when called repeatedly, so check that the
71102 // value actually changes between calls.
71104 function uiToggle(show, callback) {
71105 return function (selection) {
71106 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
71107 select(this).classed('hide', !show).style('opacity', null);
71108 if (callback) callback.apply(this);
71113 function uiCurtain(containerNode) {
71114 var surface = select(null),
71115 tooltip = select(null),
71116 darkness = select(null);
71118 function curtain(selection) {
71119 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
71120 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
71121 select(window).on('resize.curtain', resize);
71122 tooltip = selection.append('div').attr('class', 'tooltip');
71123 tooltip.append('div').attr('class', 'popover-arrow');
71124 tooltip.append('div').attr('class', 'popover-inner');
71127 function resize() {
71128 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
71129 curtain.cut(darkness.datum());
71133 * Reveal cuts the curtain to highlight the given box,
71134 * and shows a tooltip with instructions next to the box.
71136 * @param {String|ClientRect} [box] box used to cut the curtain
71137 * @param {String} [text] text for a tooltip
71138 * @param {Object} [options]
71139 * @param {string} [options.tooltipClass] optional class to add to the tooltip
71140 * @param {integer} [options.duration] transition time in milliseconds
71141 * @param {string} [options.buttonText] if set, create a button with this text label
71142 * @param {function} [options.buttonCallback] if set, the callback for the button
71143 * @param {function} [options.padding] extra margin in px to put around bbox
71144 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
71148 curtain.reveal = function (box, html, options) {
71149 options = options || {};
71151 if (typeof box === 'string') {
71152 box = select(box).node();
71155 if (box && box.getBoundingClientRect) {
71156 box = copyBox(box.getBoundingClientRect());
71157 var containerRect = containerNode.getBoundingClientRect();
71158 box.top -= containerRect.top;
71159 box.left -= containerRect.left;
71162 if (box && options.padding) {
71163 box.top -= options.padding;
71164 box.left -= options.padding;
71165 box.bottom += options.padding;
71166 box.right += options.padding;
71167 box.height += options.padding * 2;
71168 box.width += options.padding * 2;
71173 if (options.tooltipBox) {
71174 tooltipBox = options.tooltipBox;
71176 if (typeof tooltipBox === 'string') {
71177 tooltipBox = select(tooltipBox).node();
71180 if (tooltipBox && tooltipBox.getBoundingClientRect) {
71181 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
71187 if (tooltipBox && html) {
71188 if (html.indexOf('**') !== -1) {
71189 if (html.indexOf('<span') === 0) {
71190 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
71192 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
71193 } // pseudo markdown bold text for the instruction section..
71196 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
71199 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
71201 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
71203 if (options.buttonText && options.buttonCallback) {
71204 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
71207 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
71208 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
71210 if (options.buttonText && options.buttonCallback) {
71211 var button = tooltip.selectAll('.button-section .button.action');
71212 button.on('click', function (d3_event) {
71213 d3_event.preventDefault();
71214 options.buttonCallback();
71218 var tip = copyBox(tooltip.node().getBoundingClientRect()),
71219 w = containerNode.clientWidth,
71220 h = containerNode.clientHeight,
71221 tooltipWidth = 200,
71224 pos; // hack: this will have bottom placement,
71225 // so need to reserve extra space for the tooltip illustration.
71227 if (options.tooltipClass === 'intro-mouse') {
71229 } // trim box dimensions to just the portion that fits in the container..
71232 if (tooltipBox.top + tooltipBox.height > h) {
71233 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
71236 if (tooltipBox.left + tooltipBox.width > w) {
71237 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
71238 } // determine tooltip placement..
71241 if (tooltipBox.top + tooltipBox.height < 100) {
71242 // tooltip below box..
71244 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
71245 } else if (tooltipBox.top > h - 140) {
71246 // tooltip above box..
71248 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
71250 // tooltip to the side of the tooltipBox..
71251 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
71253 if (_mainLocalizer.textDirection() === 'rtl') {
71254 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
71256 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
71259 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
71262 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
71264 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
71267 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
71272 if (options.duration !== 0 || !tooltip.classed(side)) {
71273 tooltip.call(uiToggle(true));
71276 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
71277 // (doesn't affect the placement of the popover-arrow)
71281 if (side === 'left' || side === 'right') {
71283 shiftY = 60 - pos[1];
71284 } else if (pos[1] + tip.height > h - 100) {
71285 shiftY = h - pos[1] - tip.height - 100;
71289 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
71291 tooltip.classed('in', false).call(uiToggle(false));
71294 curtain.cut(box, options.duration);
71298 curtain.cut = function (datum, duration) {
71299 darkness.datum(datum).interrupt();
71302 if (duration === 0) {
71303 selection = darkness;
71305 selection = darkness.transition().duration(duration || 600).ease(linear$1);
71308 selection.attr('d', function (d) {
71309 var containerWidth = containerNode.clientWidth;
71310 var containerHeight = containerNode.clientHeight;
71311 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
71312 if (!d) return string;
71313 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';
71317 curtain.remove = function () {
71320 select(window).on('resize.curtain', null);
71321 }; // ClientRects are immutable, so copy them to an object,
71322 // in case we need to trim the height/width.
71325 function copyBox(src) {
71329 bottom: src.bottom,
71339 function uiIntroWelcome(context, reveal) {
71340 var dispatch = dispatch$8('done');
71342 title: 'intro.welcome.title'
71345 function welcome() {
71346 context.map().centerZoom([-85.63591, 41.94285], 19);
71347 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
71348 buttonText: _t.html('intro.ok'),
71349 buttonCallback: practice
71353 function practice() {
71354 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
71355 buttonText: _t.html('intro.ok'),
71356 buttonCallback: words
71361 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
71362 buttonText: _t.html('intro.ok'),
71363 buttonCallback: chapters
71367 function chapters() {
71368 dispatch.call('done');
71369 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
71370 next: _t('intro.navigation.title')
71374 chapter.enter = function () {
71378 chapter.exit = function () {
71379 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
71382 chapter.restart = function () {
71387 return utilRebind(chapter, dispatch, 'on');
71390 function uiIntroNavigation(context, reveal) {
71391 var dispatch = dispatch$8('done');
71393 var hallId = 'n2061';
71394 var townHall = [-85.63591, 41.94285];
71395 var springStreetId = 'w397';
71396 var springStreetEndId = 'n1834';
71397 var springStreet = [-85.63582, 41.94255];
71398 var onewayField = _mainPresetIndex.field('oneway');
71399 var maxspeedField = _mainPresetIndex.field('maxspeed');
71401 title: 'intro.navigation.title'
71404 function timeout(f, t) {
71405 timeouts.push(window.setTimeout(f, t));
71408 function eventCancel(d3_event) {
71409 d3_event.stopPropagation();
71410 d3_event.preventDefault();
71413 function isTownHallSelected() {
71414 var ids = context.selectedIDs();
71415 return ids.length === 1 && ids[0] === hallId;
71418 function dragMap() {
71419 context.enter(modeBrowse(context));
71420 context.history().reset('initial');
71421 var msec = transitionTime(townHall, context.map().center());
71424 reveal(null, null, {
71429 context.map().centerZoomEase(townHall, 19, msec);
71430 timeout(function () {
71431 var centerStart = context.map().center();
71432 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
71433 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
71434 reveal('.surface', dragString);
71435 context.map().on('drawn.intro', function () {
71436 reveal('.surface', dragString, {
71440 context.map().on('move.intro', function () {
71441 var centerNow = context.map().center();
71443 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
71444 context.map().on('move.intro', null);
71445 timeout(function () {
71446 continueTo(zoomMap);
71452 function continueTo(nextStep) {
71453 context.map().on('move.intro drawn.intro', null);
71458 function zoomMap() {
71459 var zoomStart = context.map().zoom();
71460 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
71461 var zoomString = helpHtml('intro.navigation.' + textId);
71462 reveal('.surface', zoomString);
71463 context.map().on('drawn.intro', function () {
71464 reveal('.surface', zoomString, {
71468 context.map().on('move.intro', function () {
71469 if (context.map().zoom() !== zoomStart) {
71470 context.map().on('move.intro', null);
71471 timeout(function () {
71472 continueTo(features);
71477 function continueTo(nextStep) {
71478 context.map().on('move.intro drawn.intro', null);
71483 function features() {
71484 var onClick = function onClick() {
71485 continueTo(pointsLinesAreas);
71488 reveal('.surface', helpHtml('intro.navigation.features'), {
71489 buttonText: _t.html('intro.ok'),
71490 buttonCallback: onClick
71492 context.map().on('drawn.intro', function () {
71493 reveal('.surface', helpHtml('intro.navigation.features'), {
71495 buttonText: _t.html('intro.ok'),
71496 buttonCallback: onClick
71500 function continueTo(nextStep) {
71501 context.map().on('drawn.intro', null);
71506 function pointsLinesAreas() {
71507 var onClick = function onClick() {
71508 continueTo(nodesWays);
71511 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
71512 buttonText: _t.html('intro.ok'),
71513 buttonCallback: onClick
71515 context.map().on('drawn.intro', function () {
71516 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
71518 buttonText: _t.html('intro.ok'),
71519 buttonCallback: onClick
71523 function continueTo(nextStep) {
71524 context.map().on('drawn.intro', null);
71529 function nodesWays() {
71530 var onClick = function onClick() {
71531 continueTo(clickTownHall);
71534 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
71535 buttonText: _t.html('intro.ok'),
71536 buttonCallback: onClick
71538 context.map().on('drawn.intro', function () {
71539 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
71541 buttonText: _t.html('intro.ok'),
71542 buttonCallback: onClick
71546 function continueTo(nextStep) {
71547 context.map().on('drawn.intro', null);
71552 function clickTownHall() {
71553 context.enter(modeBrowse(context));
71554 context.history().reset('initial');
71555 var entity = context.hasEntity(hallId);
71556 if (!entity) return;
71557 reveal(null, null, {
71560 context.map().centerZoomEase(entity.loc, 19, 500);
71561 timeout(function () {
71562 var entity = context.hasEntity(hallId);
71563 if (!entity) return;
71564 var box = pointBox(entity.loc, context);
71565 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
71566 reveal(box, helpHtml('intro.navigation.' + textId));
71567 context.map().on('move.intro drawn.intro', function () {
71568 var entity = context.hasEntity(hallId);
71569 if (!entity) return;
71570 var box = pointBox(entity.loc, context);
71571 reveal(box, helpHtml('intro.navigation.' + textId), {
71575 context.on('enter.intro', function () {
71576 if (isTownHallSelected()) continueTo(selectedTownHall);
71578 }, 550); // after centerZoomEase
71580 context.history().on('change.intro', function () {
71581 if (!context.hasEntity(hallId)) {
71582 continueTo(clickTownHall);
71586 function continueTo(nextStep) {
71587 context.on('enter.intro', null);
71588 context.map().on('move.intro drawn.intro', null);
71589 context.history().on('change.intro', null);
71594 function selectedTownHall() {
71595 if (!isTownHallSelected()) return clickTownHall();
71596 var entity = context.hasEntity(hallId);
71597 if (!entity) return clickTownHall();
71598 var box = pointBox(entity.loc, context);
71600 var onClick = function onClick() {
71601 continueTo(editorTownHall);
71604 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
71605 buttonText: _t.html('intro.ok'),
71606 buttonCallback: onClick
71608 context.map().on('move.intro drawn.intro', function () {
71609 var entity = context.hasEntity(hallId);
71610 if (!entity) return;
71611 var box = pointBox(entity.loc, context);
71612 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
71614 buttonText: _t.html('intro.ok'),
71615 buttonCallback: onClick
71618 context.history().on('change.intro', function () {
71619 if (!context.hasEntity(hallId)) {
71620 continueTo(clickTownHall);
71624 function continueTo(nextStep) {
71625 context.map().on('move.intro drawn.intro', null);
71626 context.history().on('change.intro', null);
71631 function editorTownHall() {
71632 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
71634 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
71636 var onClick = function onClick() {
71637 continueTo(presetTownHall);
71640 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
71641 buttonText: _t.html('intro.ok'),
71642 buttonCallback: onClick
71644 context.on('exit.intro', function () {
71645 continueTo(clickTownHall);
71647 context.history().on('change.intro', function () {
71648 if (!context.hasEntity(hallId)) {
71649 continueTo(clickTownHall);
71653 function continueTo(nextStep) {
71654 context.on('exit.intro', null);
71655 context.history().on('change.intro', null);
71656 context.container().select('.inspector-wrap').on('wheel.intro', null);
71661 function presetTownHall() {
71662 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
71664 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
71666 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
71668 var entity = context.entity(context.selectedIDs()[0]);
71669 var preset = _mainPresetIndex.match(entity, context.graph());
71671 var onClick = function onClick() {
71672 continueTo(fieldsTownHall);
71675 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
71676 preset: preset.name()
71678 buttonText: _t.html('intro.ok'),
71679 buttonCallback: onClick
71681 context.on('exit.intro', function () {
71682 continueTo(clickTownHall);
71684 context.history().on('change.intro', function () {
71685 if (!context.hasEntity(hallId)) {
71686 continueTo(clickTownHall);
71690 function continueTo(nextStep) {
71691 context.on('exit.intro', null);
71692 context.history().on('change.intro', null);
71693 context.container().select('.inspector-wrap').on('wheel.intro', null);
71698 function fieldsTownHall() {
71699 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
71701 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
71703 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
71705 var onClick = function onClick() {
71706 continueTo(closeTownHall);
71709 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
71710 buttonText: _t.html('intro.ok'),
71711 buttonCallback: onClick
71713 context.on('exit.intro', function () {
71714 continueTo(clickTownHall);
71716 context.history().on('change.intro', function () {
71717 if (!context.hasEntity(hallId)) {
71718 continueTo(clickTownHall);
71722 function continueTo(nextStep) {
71723 context.on('exit.intro', null);
71724 context.history().on('change.intro', null);
71725 context.container().select('.inspector-wrap').on('wheel.intro', null);
71730 function closeTownHall() {
71731 if (!isTownHallSelected()) return clickTownHall();
71732 var selector = '.entity-editor-pane button.close svg use';
71733 var href = select(selector).attr('href') || '#iD-icon-close';
71734 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
71735 button: icon(href, 'inline')
71737 context.on('exit.intro', function () {
71738 continueTo(searchStreet);
71740 context.history().on('change.intro', function () {
71741 // update the close icon in the tooltip if the user edits something.
71742 var selector = '.entity-editor-pane button.close svg use';
71743 var href = select(selector).attr('href') || '#iD-icon-close';
71744 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
71745 button: icon(href, 'inline')
71751 function continueTo(nextStep) {
71752 context.on('exit.intro', null);
71753 context.history().on('change.intro', null);
71758 function searchStreet() {
71759 context.enter(modeBrowse(context));
71760 context.history().reset('initial'); // ensure spring street exists
71762 var msec = transitionTime(springStreet, context.map().center());
71765 reveal(null, null, {
71770 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
71772 timeout(function () {
71773 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
71774 name: _t('intro.graph.name.spring-street')
71776 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
71780 function checkSearchResult() {
71781 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
71783 var firstName = first.select('.entity-name');
71784 var name = _t('intro.graph.name.spring-street');
71786 if (!firstName.empty() && firstName.html() === name) {
71787 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
71792 context.on('exit.intro', function () {
71793 continueTo(selectedStreet);
71795 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
71798 function continueTo(nextStep) {
71799 context.on('exit.intro', null);
71800 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
71805 function selectedStreet() {
71806 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
71807 return searchStreet();
71810 var onClick = function onClick() {
71811 continueTo(editorStreet);
71814 var entity = context.entity(springStreetEndId);
71815 var box = pointBox(entity.loc, context);
71817 reveal(box, helpHtml('intro.navigation.selected_street', {
71818 name: _t('intro.graph.name.spring-street')
71821 buttonText: _t.html('intro.ok'),
71822 buttonCallback: onClick
71824 timeout(function () {
71825 context.map().on('move.intro drawn.intro', function () {
71826 var entity = context.hasEntity(springStreetEndId);
71827 if (!entity) return;
71828 var box = pointBox(entity.loc, context);
71830 reveal(box, helpHtml('intro.navigation.selected_street', {
71831 name: _t('intro.graph.name.spring-street')
71834 buttonText: _t.html('intro.ok'),
71835 buttonCallback: onClick
71838 }, 600); // after reveal.
71840 context.on('enter.intro', function (mode) {
71841 if (!context.hasEntity(springStreetId)) {
71842 return continueTo(searchStreet);
71845 var ids = context.selectedIDs();
71847 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
71848 // keep Spring Street selected..
71849 context.enter(modeSelect(context, [springStreetId]));
71852 context.history().on('change.intro', function () {
71853 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
71854 timeout(function () {
71855 continueTo(searchStreet);
71856 }, 300); // after any transition (e.g. if user deleted intersection)
71860 function continueTo(nextStep) {
71861 context.map().on('move.intro drawn.intro', null);
71862 context.on('enter.intro', null);
71863 context.history().on('change.intro', null);
71868 function editorStreet() {
71869 var selector = '.entity-editor-pane button.close svg use';
71870 var href = select(selector).attr('href') || '#iD-icon-close';
71871 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
71872 button: icon(href, 'inline'),
71873 field1: onewayField.label(),
71874 field2: maxspeedField.label()
71876 context.on('exit.intro', function () {
71879 context.history().on('change.intro', function () {
71880 // update the close icon in the tooltip if the user edits something.
71881 var selector = '.entity-editor-pane button.close svg use';
71882 var href = select(selector).attr('href') || '#iD-icon-close';
71883 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
71884 button: icon(href, 'inline'),
71885 field1: onewayField.label(),
71886 field2: maxspeedField.label()
71892 function continueTo(nextStep) {
71893 context.on('exit.intro', null);
71894 context.history().on('change.intro', null);
71900 dispatch.call('done');
71901 reveal('.ideditor', helpHtml('intro.navigation.play', {
71902 next: _t('intro.points.title')
71904 tooltipBox: '.intro-nav-wrap .chapter-point',
71905 buttonText: _t.html('intro.ok'),
71906 buttonCallback: function buttonCallback() {
71907 reveal('.ideditor');
71912 chapter.enter = function () {
71916 chapter.exit = function () {
71917 timeouts.forEach(window.clearTimeout);
71918 context.on('enter.intro exit.intro', null);
71919 context.map().on('move.intro drawn.intro', null);
71920 context.history().on('change.intro', null);
71921 context.container().select('.inspector-wrap').on('wheel.intro', null);
71922 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
71925 chapter.restart = function () {
71930 return utilRebind(chapter, dispatch, 'on');
71933 function uiIntroPoint(context, reveal) {
71934 var dispatch = dispatch$8('done');
71936 var intersection = [-85.63279, 41.94394];
71937 var building = [-85.632422, 41.944045];
71938 var cafePreset = _mainPresetIndex.item('amenity/cafe');
71939 var _pointID = null;
71941 title: 'intro.points.title'
71944 function timeout(f, t) {
71945 timeouts.push(window.setTimeout(f, t));
71948 function eventCancel(d3_event) {
71949 d3_event.stopPropagation();
71950 d3_event.preventDefault();
71953 function addPoint() {
71954 context.enter(modeBrowse(context));
71955 context.history().reset('initial');
71956 var msec = transitionTime(intersection, context.map().center());
71959 reveal(null, null, {
71964 context.map().centerZoomEase(intersection, 19, msec);
71965 timeout(function () {
71966 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
71968 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
71969 context.on('enter.intro', function (mode) {
71970 if (mode.id !== 'add-point') return;
71971 continueTo(placePoint);
71975 function continueTo(nextStep) {
71976 context.on('enter.intro', null);
71981 function placePoint() {
71982 if (context.mode().id !== 'add-point') {
71983 return chapter.restart();
71986 var pointBox = pad(building, 150, context);
71987 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
71988 reveal(pointBox, helpHtml('intro.points.' + textId));
71989 context.map().on('move.intro drawn.intro', function () {
71990 pointBox = pad(building, 150, context);
71991 reveal(pointBox, helpHtml('intro.points.' + textId), {
71995 context.on('enter.intro', function (mode) {
71996 if (mode.id !== 'select') return chapter.restart();
71997 _pointID = context.mode().selectedIDs()[0];
71998 continueTo(searchPreset);
72001 function continueTo(nextStep) {
72002 context.map().on('move.intro drawn.intro', null);
72003 context.on('enter.intro', null);
72008 function searchPreset() {
72009 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72011 } // disallow scrolling
72014 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72015 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72016 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
72017 preset: cafePreset.name()
72019 context.on('enter.intro', function (mode) {
72020 if (!_pointID || !context.hasEntity(_pointID)) {
72021 return continueTo(addPoint);
72024 var ids = context.selectedIDs();
72026 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
72027 // keep the user's point selected..
72028 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
72030 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72031 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72032 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
72033 preset: cafePreset.name()
72035 context.history().on('change.intro', null);
72039 function checkPresetSearch() {
72040 var first = context.container().select('.preset-list-item:first-child');
72042 if (first.classed('preset-amenity-cafe')) {
72043 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
72044 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
72045 preset: cafePreset.name()
72049 context.history().on('change.intro', function () {
72050 continueTo(aboutFeatureEditor);
72055 function continueTo(nextStep) {
72056 context.on('enter.intro', null);
72057 context.history().on('change.intro', null);
72058 context.container().select('.inspector-wrap').on('wheel.intro', null);
72059 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72064 function aboutFeatureEditor() {
72065 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72069 timeout(function () {
72070 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
72071 tooltipClass: 'intro-points-describe',
72072 buttonText: _t.html('intro.ok'),
72073 buttonCallback: function buttonCallback() {
72074 continueTo(addName);
72078 context.on('exit.intro', function () {
72079 // if user leaves select mode here, just continue with the tutorial.
72080 continueTo(reselectPoint);
72083 function continueTo(nextStep) {
72084 context.on('exit.intro', null);
72089 function addName() {
72090 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72092 } // reset pane, in case user happened to change it..
72095 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72096 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
72097 timeout(function () {
72098 // It's possible for the user to add a name in a previous step..
72099 // If so, don't tell them to add the name in this step.
72100 // Give them an OK button instead.
72101 var entity = context.entity(_pointID);
72103 if (entity.tags.name) {
72104 var tooltip = reveal('.entity-editor-pane', addNameString, {
72105 tooltipClass: 'intro-points-describe',
72106 buttonText: _t.html('intro.ok'),
72107 buttonCallback: function buttonCallback() {
72108 continueTo(addCloseEditor);
72111 tooltip.select('.instruction').style('display', 'none');
72113 reveal('.entity-editor-pane', addNameString, {
72114 tooltipClass: 'intro-points-describe'
72118 context.history().on('change.intro', function () {
72119 continueTo(addCloseEditor);
72121 context.on('exit.intro', function () {
72122 // if user leaves select mode here, just continue with the tutorial.
72123 continueTo(reselectPoint);
72126 function continueTo(nextStep) {
72127 context.on('exit.intro', null);
72128 context.history().on('change.intro', null);
72133 function addCloseEditor() {
72134 // reset pane, in case user happened to change it..
72135 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72136 var selector = '.entity-editor-pane button.close svg use';
72137 var href = select(selector).attr('href') || '#iD-icon-close';
72138 context.on('exit.intro', function () {
72139 continueTo(reselectPoint);
72141 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
72142 button: icon(href, 'inline')
72145 function continueTo(nextStep) {
72146 context.on('exit.intro', null);
72151 function reselectPoint() {
72152 if (!_pointID) return chapter.restart();
72153 var entity = context.hasEntity(_pointID);
72154 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
72156 var oldPreset = _mainPresetIndex.match(entity, context.graph());
72157 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
72158 context.enter(modeBrowse(context));
72159 var msec = transitionTime(entity.loc, context.map().center());
72162 reveal(null, null, {
72167 context.map().centerEase(entity.loc, msec);
72168 timeout(function () {
72169 var box = pointBox(entity.loc, context);
72170 reveal(box, helpHtml('intro.points.reselect'), {
72173 timeout(function () {
72174 context.map().on('move.intro drawn.intro', function () {
72175 var entity = context.hasEntity(_pointID);
72176 if (!entity) return chapter.restart();
72177 var box = pointBox(entity.loc, context);
72178 reveal(box, helpHtml('intro.points.reselect'), {
72182 }, 600); // after reveal..
72184 context.on('enter.intro', function (mode) {
72185 if (mode.id !== 'select') return;
72186 continueTo(updatePoint);
72190 function continueTo(nextStep) {
72191 context.map().on('move.intro drawn.intro', null);
72192 context.on('enter.intro', null);
72197 function updatePoint() {
72198 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72199 return continueTo(reselectPoint);
72200 } // reset pane, in case user happened to untag the point..
72203 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72204 context.on('exit.intro', function () {
72205 continueTo(reselectPoint);
72207 context.history().on('change.intro', function () {
72208 continueTo(updateCloseEditor);
72210 timeout(function () {
72211 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
72212 tooltipClass: 'intro-points-describe'
72216 function continueTo(nextStep) {
72217 context.on('exit.intro', null);
72218 context.history().on('change.intro', null);
72223 function updateCloseEditor() {
72224 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
72225 return continueTo(reselectPoint);
72226 } // reset pane, in case user happened to change it..
72229 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72230 context.on('exit.intro', function () {
72231 continueTo(rightClickPoint);
72233 timeout(function () {
72234 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
72235 button: icon('#iD-icon-close', 'inline')
72239 function continueTo(nextStep) {
72240 context.on('exit.intro', null);
72245 function rightClickPoint() {
72246 if (!_pointID) return chapter.restart();
72247 var entity = context.hasEntity(_pointID);
72248 if (!entity) return chapter.restart();
72249 context.enter(modeBrowse(context));
72250 var box = pointBox(entity.loc, context);
72251 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
72252 reveal(box, helpHtml('intro.points.' + textId), {
72255 timeout(function () {
72256 context.map().on('move.intro', function () {
72257 var entity = context.hasEntity(_pointID);
72258 if (!entity) return chapter.restart();
72259 var box = pointBox(entity.loc, context);
72260 reveal(box, helpHtml('intro.points.' + textId), {
72264 }, 600); // after reveal
72266 context.on('enter.intro', function (mode) {
72267 if (mode.id !== 'select') return;
72268 var ids = context.selectedIDs();
72269 if (ids.length !== 1 || ids[0] !== _pointID) return;
72270 timeout(function () {
72271 var node = selectMenuItem(context, 'delete').node();
72273 continueTo(enterDelete);
72274 }, 50); // after menu visible
72277 function continueTo(nextStep) {
72278 context.on('enter.intro', null);
72279 context.map().on('move.intro', null);
72284 function enterDelete() {
72285 if (!_pointID) return chapter.restart();
72286 var entity = context.hasEntity(_pointID);
72287 if (!entity) return chapter.restart();
72288 var node = selectMenuItem(context, 'delete').node();
72291 return continueTo(rightClickPoint);
72294 reveal('.edit-menu', helpHtml('intro.points.delete'), {
72297 timeout(function () {
72298 context.map().on('move.intro', function () {
72299 reveal('.edit-menu', helpHtml('intro.points.delete'), {
72304 }, 300); // after menu visible
72306 context.on('exit.intro', function () {
72307 if (!_pointID) return chapter.restart();
72308 var entity = context.hasEntity(_pointID);
72309 if (entity) return continueTo(rightClickPoint); // point still exists
72311 context.history().on('change.intro', function (changed) {
72312 if (changed.deleted().length) {
72317 function continueTo(nextStep) {
72318 context.map().on('move.intro', null);
72319 context.history().on('change.intro', null);
72320 context.on('exit.intro', null);
72326 context.history().on('change.intro', function () {
72329 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
72331 function continueTo(nextStep) {
72332 context.history().on('change.intro', null);
72338 dispatch.call('done');
72339 reveal('.ideditor', helpHtml('intro.points.play', {
72340 next: _t('intro.areas.title')
72342 tooltipBox: '.intro-nav-wrap .chapter-area',
72343 buttonText: _t.html('intro.ok'),
72344 buttonCallback: function buttonCallback() {
72345 reveal('.ideditor');
72350 chapter.enter = function () {
72354 chapter.exit = function () {
72355 timeouts.forEach(window.clearTimeout);
72356 context.on('enter.intro exit.intro', null);
72357 context.map().on('move.intro drawn.intro', null);
72358 context.history().on('change.intro', null);
72359 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72360 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72363 chapter.restart = function () {
72368 return utilRebind(chapter, dispatch, 'on');
72371 function uiIntroArea(context, reveal) {
72372 var dispatch = dispatch$8('done');
72373 var playground = [-85.63552, 41.94159];
72374 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
72375 var nameField = _mainPresetIndex.field('name');
72376 var descriptionField = _mainPresetIndex.field('description');
72382 title: 'intro.areas.title'
72385 function timeout(f, t) {
72386 timeouts.push(window.setTimeout(f, t));
72389 function eventCancel(d3_event) {
72390 d3_event.stopPropagation();
72391 d3_event.preventDefault();
72394 function revealPlayground(center, text, options) {
72395 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
72396 var box = pad(center, padding, context);
72397 reveal(box, text, options);
72400 function addArea() {
72401 context.enter(modeBrowse(context));
72402 context.history().reset('initial');
72404 var msec = transitionTime(playground, context.map().center());
72407 reveal(null, null, {
72412 context.map().centerZoomEase(playground, 19, msec);
72413 timeout(function () {
72414 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
72415 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
72416 context.on('enter.intro', function (mode) {
72417 if (mode.id !== 'add-area') return;
72418 continueTo(startPlayground);
72422 function continueTo(nextStep) {
72423 context.on('enter.intro', null);
72428 function startPlayground() {
72429 if (context.mode().id !== 'add-area') {
72430 return chapter.restart();
72434 context.map().zoomEase(19.5, 500);
72435 timeout(function () {
72436 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
72437 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
72438 revealPlayground(playground, startDrawString, {
72441 timeout(function () {
72442 context.map().on('move.intro drawn.intro', function () {
72443 revealPlayground(playground, startDrawString, {
72447 context.on('enter.intro', function (mode) {
72448 if (mode.id !== 'draw-area') return chapter.restart();
72449 continueTo(continuePlayground);
72451 }, 250); // after reveal
72452 }, 550); // after easing
72454 function continueTo(nextStep) {
72455 context.map().on('move.intro drawn.intro', null);
72456 context.on('enter.intro', null);
72461 function continuePlayground() {
72462 if (context.mode().id !== 'draw-area') {
72463 return chapter.restart();
72467 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
72470 timeout(function () {
72471 context.map().on('move.intro drawn.intro', function () {
72472 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
72476 }, 250); // after reveal
72478 context.on('enter.intro', function (mode) {
72479 if (mode.id === 'draw-area') {
72480 var entity = context.hasEntity(context.selectedIDs()[0]);
72482 if (entity && entity.nodes.length >= 6) {
72483 return continueTo(finishPlayground);
72487 } else if (mode.id === 'select') {
72488 _areaID = context.selectedIDs()[0];
72489 return continueTo(searchPresets);
72491 return chapter.restart();
72495 function continueTo(nextStep) {
72496 context.map().on('move.intro drawn.intro', null);
72497 context.on('enter.intro', null);
72502 function finishPlayground() {
72503 if (context.mode().id !== 'draw-area') {
72504 return chapter.restart();
72508 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
72509 revealPlayground(playground, finishString, {
72512 timeout(function () {
72513 context.map().on('move.intro drawn.intro', function () {
72514 revealPlayground(playground, finishString, {
72518 }, 250); // after reveal
72520 context.on('enter.intro', function (mode) {
72521 if (mode.id === 'draw-area') {
72523 } else if (mode.id === 'select') {
72524 _areaID = context.selectedIDs()[0];
72525 return continueTo(searchPresets);
72527 return chapter.restart();
72531 function continueTo(nextStep) {
72532 context.map().on('move.intro drawn.intro', null);
72533 context.on('enter.intro', null);
72538 function searchPresets() {
72539 if (!_areaID || !context.hasEntity(_areaID)) {
72543 var ids = context.selectedIDs();
72545 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72546 context.enter(modeSelect(context, [_areaID]));
72547 } // disallow scrolling
72550 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72551 timeout(function () {
72552 // reset pane, in case user somehow happened to change it..
72553 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
72554 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72555 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
72556 preset: playgroundPreset.name()
72558 }, 400); // after preset list pane visible..
72560 context.on('enter.intro', function (mode) {
72561 if (!_areaID || !context.hasEntity(_areaID)) {
72562 return continueTo(addArea);
72565 var ids = context.selectedIDs();
72567 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
72568 // keep the user's area selected..
72569 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
72571 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
72573 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72574 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
72575 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
72576 preset: playgroundPreset.name()
72578 context.history().on('change.intro', null);
72582 function checkPresetSearch() {
72583 var first = context.container().select('.preset-list-item:first-child');
72585 if (first.classed('preset-leisure-playground')) {
72586 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
72587 preset: playgroundPreset.name()
72591 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
72592 context.history().on('change.intro', function () {
72593 continueTo(clickAddField);
72598 function continueTo(nextStep) {
72599 context.container().select('.inspector-wrap').on('wheel.intro', null);
72600 context.on('enter.intro', null);
72601 context.history().on('change.intro', null);
72602 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72607 function clickAddField() {
72608 if (!_areaID || !context.hasEntity(_areaID)) {
72612 var ids = context.selectedIDs();
72614 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72615 return searchPresets();
72618 if (!context.container().select('.form-field-description').empty()) {
72619 return continueTo(describePlayground);
72620 } // disallow scrolling
72623 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
72624 timeout(function () {
72625 // reset pane, in case user somehow happened to change it..
72626 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
72627 // If they did this already, just continue to next step.
72629 var entity = context.entity(_areaID);
72631 if (entity.tags.description) {
72632 return continueTo(play);
72633 } // scroll "Add field" into view
72636 var box = context.container().select('.more-fields').node().getBoundingClientRect();
72638 if (box.top > 300) {
72639 var pane = context.container().select('.entity-editor-pane .inspector-body');
72640 var start = pane.node().scrollTop;
72641 var end = start + (box.top - 300);
72642 pane.transition().duration(250).tween('scroll.inspector', function () {
72644 var i = d3_interpolateNumber(start, end);
72645 return function (t) {
72646 node.scrollTop = i(t);
72651 timeout(function () {
72652 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
72653 name: nameField.label(),
72654 description: descriptionField.label()
72658 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
72659 // Watch for the combobox to appear...
72661 watcher = window.setInterval(function () {
72662 if (!context.container().select('div.combobox').empty()) {
72663 window.clearInterval(watcher);
72664 continueTo(chooseDescriptionField);
72668 }, 300); // after "Add Field" visible
72669 }, 400); // after editor pane visible
72671 context.on('exit.intro', function () {
72672 return continueTo(searchPresets);
72675 function continueTo(nextStep) {
72676 context.container().select('.inspector-wrap').on('wheel.intro', null);
72677 context.container().select('.more-fields .combobox-input').on('click.intro', null);
72678 context.on('exit.intro', null);
72683 function chooseDescriptionField() {
72684 if (!_areaID || !context.hasEntity(_areaID)) {
72688 var ids = context.selectedIDs();
72690 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72691 return searchPresets();
72694 if (!context.container().select('.form-field-description').empty()) {
72695 return continueTo(describePlayground);
72696 } // Make sure combobox is ready..
72699 if (context.container().select('div.combobox').empty()) {
72700 return continueTo(clickAddField);
72701 } // Watch for the combobox to go away..
72705 watcher = window.setInterval(function () {
72706 if (context.container().select('div.combobox').empty()) {
72707 window.clearInterval(watcher);
72708 timeout(function () {
72709 if (context.container().select('.form-field-description').empty()) {
72710 continueTo(retryChooseDescription);
72712 continueTo(describePlayground);
72714 }, 300); // after description field added.
72717 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
72718 field: descriptionField.label()
72722 context.on('exit.intro', function () {
72723 return continueTo(searchPresets);
72726 function continueTo(nextStep) {
72727 if (watcher) window.clearInterval(watcher);
72728 context.on('exit.intro', null);
72733 function describePlayground() {
72734 if (!_areaID || !context.hasEntity(_areaID)) {
72738 var ids = context.selectedIDs();
72740 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72741 return searchPresets();
72742 } // reset pane, in case user happened to change it..
72745 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72747 if (context.container().select('.form-field-description').empty()) {
72748 return continueTo(retryChooseDescription);
72751 context.on('exit.intro', function () {
72754 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
72755 button: icon('#iD-icon-close', 'inline')
72760 function continueTo(nextStep) {
72761 context.on('exit.intro', null);
72766 function retryChooseDescription() {
72767 if (!_areaID || !context.hasEntity(_areaID)) {
72771 var ids = context.selectedIDs();
72773 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
72774 return searchPresets();
72775 } // reset pane, in case user happened to change it..
72778 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
72779 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
72780 field: descriptionField.label()
72782 buttonText: _t.html('intro.ok'),
72783 buttonCallback: function buttonCallback() {
72784 continueTo(clickAddField);
72787 context.on('exit.intro', function () {
72788 return continueTo(searchPresets);
72791 function continueTo(nextStep) {
72792 context.on('exit.intro', null);
72798 dispatch.call('done');
72799 reveal('.ideditor', helpHtml('intro.areas.play', {
72800 next: _t('intro.lines.title')
72802 tooltipBox: '.intro-nav-wrap .chapter-line',
72803 buttonText: _t.html('intro.ok'),
72804 buttonCallback: function buttonCallback() {
72805 reveal('.ideditor');
72810 chapter.enter = function () {
72814 chapter.exit = function () {
72815 timeouts.forEach(window.clearTimeout);
72816 context.on('enter.intro exit.intro', null);
72817 context.map().on('move.intro drawn.intro', null);
72818 context.history().on('change.intro', null);
72819 context.container().select('.inspector-wrap').on('wheel.intro', null);
72820 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
72821 context.container().select('.more-fields .combobox-input').on('click.intro', null);
72824 chapter.restart = function () {
72829 return utilRebind(chapter, dispatch, 'on');
72832 function uiIntroLine(context, reveal) {
72833 var dispatch = dispatch$8('done');
72835 var _tulipRoadID = null;
72836 var flowerRoadID = 'w646';
72837 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
72838 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
72839 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
72840 var roadCategory = _mainPresetIndex.item('category-road_minor');
72841 var residentialPreset = _mainPresetIndex.item('highway/residential');
72842 var woodRoadID = 'w525';
72843 var woodRoadEndID = 'n2862';
72844 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
72845 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
72846 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
72847 var washingtonStreetID = 'w522';
72848 var twelfthAvenueID = 'w1';
72849 var eleventhAvenueEndID = 'n3550';
72850 var twelfthAvenueEndID = 'n5';
72851 var _washingtonSegmentID = null;
72852 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
72853 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
72854 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
72855 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
72857 title: 'intro.lines.title'
72860 function timeout(f, t) {
72861 timeouts.push(window.setTimeout(f, t));
72864 function eventCancel(d3_event) {
72865 d3_event.stopPropagation();
72866 d3_event.preventDefault();
72869 function addLine() {
72870 context.enter(modeBrowse(context));
72871 context.history().reset('initial');
72872 var msec = transitionTime(tulipRoadStart, context.map().center());
72875 reveal(null, null, {
72880 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
72881 timeout(function () {
72882 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
72883 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
72884 context.on('enter.intro', function (mode) {
72885 if (mode.id !== 'add-line') return;
72886 continueTo(startLine);
72890 function continueTo(nextStep) {
72891 context.on('enter.intro', null);
72896 function startLine() {
72897 if (context.mode().id !== 'add-line') return chapter.restart();
72898 _tulipRoadID = null;
72899 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
72900 var box = pad(tulipRoadStart, padding, context);
72901 box.height = box.height + 100;
72902 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
72903 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
72904 reveal(box, startLineString);
72905 context.map().on('move.intro drawn.intro', function () {
72906 padding = 70 * Math.pow(2, context.map().zoom() - 18);
72907 box = pad(tulipRoadStart, padding, context);
72908 box.height = box.height + 100;
72909 reveal(box, startLineString, {
72913 context.on('enter.intro', function (mode) {
72914 if (mode.id !== 'draw-line') return chapter.restart();
72915 continueTo(drawLine);
72918 function continueTo(nextStep) {
72919 context.map().on('move.intro drawn.intro', null);
72920 context.on('enter.intro', null);
72925 function drawLine() {
72926 if (context.mode().id !== 'draw-line') return chapter.restart();
72927 _tulipRoadID = context.mode().selectedIDs()[0];
72928 context.map().centerEase(tulipRoadMidpoint, 500);
72929 timeout(function () {
72930 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
72931 var box = pad(tulipRoadMidpoint, padding, context);
72932 box.height = box.height * 2;
72933 reveal(box, helpHtml('intro.lines.intersect', {
72934 name: _t('intro.graph.name.flower-street')
72936 context.map().on('move.intro drawn.intro', function () {
72937 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
72938 box = pad(tulipRoadMidpoint, padding, context);
72939 box.height = box.height * 2;
72940 reveal(box, helpHtml('intro.lines.intersect', {
72941 name: _t('intro.graph.name.flower-street')
72946 }, 550); // after easing..
72948 context.history().on('change.intro', function () {
72949 if (isLineConnected()) {
72950 continueTo(continueLine);
72953 context.on('enter.intro', function (mode) {
72954 if (mode.id === 'draw-line') {
72956 } else if (mode.id === 'select') {
72957 continueTo(retryIntersect);
72960 return chapter.restart();
72964 function continueTo(nextStep) {
72965 context.map().on('move.intro drawn.intro', null);
72966 context.history().on('change.intro', null);
72967 context.on('enter.intro', null);
72972 function isLineConnected() {
72973 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
72975 if (!entity) return false;
72976 var drawNodes = context.graph().childNodes(entity);
72977 return drawNodes.some(function (node) {
72978 return context.graph().parentWays(node).some(function (parent) {
72979 return parent.id === flowerRoadID;
72984 function retryIntersect() {
72985 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
72986 var box = pad(tulipRoadIntersection, 80, context);
72987 reveal(box, helpHtml('intro.lines.retry_intersect', {
72988 name: _t('intro.graph.name.flower-street')
72990 timeout(chapter.restart, 3000);
72993 function continueLine() {
72994 if (context.mode().id !== 'draw-line') return chapter.restart();
72996 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
72998 if (!entity) return chapter.restart();
72999 context.map().centerEase(tulipRoadIntersection, 500);
73000 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
73001 reveal('.surface', continueLineText);
73002 context.on('enter.intro', function (mode) {
73003 if (mode.id === 'draw-line') {
73005 } else if (mode.id === 'select') {
73006 return continueTo(chooseCategoryRoad);
73008 return chapter.restart();
73012 function continueTo(nextStep) {
73013 context.on('enter.intro', null);
73018 function chooseCategoryRoad() {
73019 if (context.mode().id !== 'select') return chapter.restart();
73020 context.on('exit.intro', function () {
73021 return chapter.restart();
73023 var button = context.container().select('.preset-category-road_minor .preset-list-button');
73024 if (button.empty()) return chapter.restart(); // disallow scrolling
73026 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
73027 timeout(function () {
73028 // reset pane, in case user somehow happened to change it..
73029 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
73030 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
73031 category: roadCategory.name()
73033 button.on('click.intro', function () {
73034 continueTo(choosePresetResidential);
73036 }, 400); // after editor pane visible
73038 function continueTo(nextStep) {
73039 context.container().select('.inspector-wrap').on('wheel.intro', null);
73040 context.container().select('.preset-list-button').on('click.intro', null);
73041 context.on('exit.intro', null);
73046 function choosePresetResidential() {
73047 if (context.mode().id !== 'select') return chapter.restart();
73048 context.on('exit.intro', function () {
73049 return chapter.restart();
73051 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
73052 if (subgrid.empty()) return chapter.restart();
73053 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
73054 continueTo(retryPresetResidential);
73056 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
73057 continueTo(nameRoad);
73059 timeout(function () {
73060 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
73061 preset: residentialPreset.name()
73063 tooltipBox: '.preset-highway-residential .preset-list-button',
73068 function continueTo(nextStep) {
73069 context.container().select('.preset-list-button').on('click.intro', null);
73070 context.on('exit.intro', null);
73073 } // selected wrong road type
73076 function retryPresetResidential() {
73077 if (context.mode().id !== 'select') return chapter.restart();
73078 context.on('exit.intro', function () {
73079 return chapter.restart();
73080 }); // disallow scrolling
73082 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
73083 timeout(function () {
73084 var button = context.container().select('.entity-editor-pane .preset-list-button');
73085 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
73086 preset: residentialPreset.name()
73088 button.on('click.intro', function () {
73089 continueTo(chooseCategoryRoad);
73093 function continueTo(nextStep) {
73094 context.container().select('.inspector-wrap').on('wheel.intro', null);
73095 context.container().select('.preset-list-button').on('click.intro', null);
73096 context.on('exit.intro', null);
73101 function nameRoad() {
73102 context.on('exit.intro', function () {
73103 continueTo(didNameRoad);
73105 timeout(function () {
73106 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
73107 button: icon('#iD-icon-close', 'inline')
73109 tooltipClass: 'intro-lines-name_road'
73113 function continueTo(nextStep) {
73114 context.on('exit.intro', null);
73119 function didNameRoad() {
73120 context.history().checkpoint('doneAddLine');
73121 timeout(function () {
73122 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
73123 buttonText: _t.html('intro.ok'),
73124 buttonCallback: function buttonCallback() {
73125 continueTo(updateLine);
73130 function continueTo(nextStep) {
73135 function updateLine() {
73136 context.history().reset('doneAddLine');
73138 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73139 return chapter.restart();
73142 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
73145 reveal(null, null, {
73150 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
73151 timeout(function () {
73152 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
73153 var box = pad(woodRoadDragMidpoint, padding, context);
73155 var advance = function advance() {
73156 continueTo(addNode);
73159 reveal(box, helpHtml('intro.lines.update_line'), {
73160 buttonText: _t.html('intro.ok'),
73161 buttonCallback: advance
73163 context.map().on('move.intro drawn.intro', function () {
73164 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
73165 var box = pad(woodRoadDragMidpoint, padding, context);
73166 reveal(box, helpHtml('intro.lines.update_line'), {
73168 buttonText: _t.html('intro.ok'),
73169 buttonCallback: advance
73174 function continueTo(nextStep) {
73175 context.map().on('move.intro drawn.intro', null);
73180 function addNode() {
73181 context.history().reset('doneAddLine');
73183 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73184 return chapter.restart();
73187 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
73188 var box = pad(woodRoadAddNode, padding, context);
73189 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
73190 reveal(box, addNodeString);
73191 context.map().on('move.intro drawn.intro', function () {
73192 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
73193 var box = pad(woodRoadAddNode, padding, context);
73194 reveal(box, addNodeString, {
73198 context.history().on('change.intro', function (changed) {
73199 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73200 return continueTo(updateLine);
73203 if (changed.created().length === 1) {
73204 timeout(function () {
73205 continueTo(startDragEndpoint);
73209 context.on('enter.intro', function (mode) {
73210 if (mode.id !== 'select') {
73211 continueTo(updateLine);
73215 function continueTo(nextStep) {
73216 context.map().on('move.intro drawn.intro', null);
73217 context.history().on('change.intro', null);
73218 context.on('enter.intro', null);
73223 function startDragEndpoint() {
73224 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73225 return continueTo(updateLine);
73228 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73229 var box = pad(woodRoadDragEndpoint, padding, context);
73230 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
73231 reveal(box, startDragString);
73232 context.map().on('move.intro drawn.intro', function () {
73233 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73234 return continueTo(updateLine);
73237 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73238 var box = pad(woodRoadDragEndpoint, padding, context);
73239 reveal(box, startDragString, {
73242 var entity = context.entity(woodRoadEndID);
73244 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
73245 continueTo(finishDragEndpoint);
73249 function continueTo(nextStep) {
73250 context.map().on('move.intro drawn.intro', null);
73255 function finishDragEndpoint() {
73256 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73257 return continueTo(updateLine);
73260 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73261 var box = pad(woodRoadDragEndpoint, padding, context);
73262 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
73263 reveal(box, finishDragString);
73264 context.map().on('move.intro drawn.intro', function () {
73265 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73266 return continueTo(updateLine);
73269 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73270 var box = pad(woodRoadDragEndpoint, padding, context);
73271 reveal(box, finishDragString, {
73274 var entity = context.entity(woodRoadEndID);
73276 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
73277 continueTo(startDragEndpoint);
73280 context.on('enter.intro', function () {
73281 continueTo(startDragMidpoint);
73284 function continueTo(nextStep) {
73285 context.map().on('move.intro drawn.intro', null);
73286 context.on('enter.intro', null);
73291 function startDragMidpoint() {
73292 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73293 return continueTo(updateLine);
73296 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
73297 context.enter(modeSelect(context, [woodRoadID]));
73300 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
73301 var box = pad(woodRoadDragMidpoint, padding, context);
73302 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
73303 context.map().on('move.intro drawn.intro', function () {
73304 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73305 return continueTo(updateLine);
73308 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
73309 var box = pad(woodRoadDragMidpoint, padding, context);
73310 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
73314 context.history().on('change.intro', function (changed) {
73315 if (changed.created().length === 1) {
73316 continueTo(continueDragMidpoint);
73319 context.on('enter.intro', function (mode) {
73320 if (mode.id !== 'select') {
73321 // keep Wood Road selected so midpoint triangles are drawn..
73322 context.enter(modeSelect(context, [woodRoadID]));
73326 function continueTo(nextStep) {
73327 context.map().on('move.intro drawn.intro', null);
73328 context.history().on('change.intro', null);
73329 context.on('enter.intro', null);
73334 function continueDragMidpoint() {
73335 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73336 return continueTo(updateLine);
73339 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73340 var box = pad(woodRoadDragEndpoint, padding, context);
73343 var advance = function advance() {
73344 context.history().checkpoint('doneUpdateLine');
73345 continueTo(deleteLines);
73348 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
73349 buttonText: _t.html('intro.ok'),
73350 buttonCallback: advance
73352 context.map().on('move.intro drawn.intro', function () {
73353 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
73354 return continueTo(updateLine);
73357 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
73358 var box = pad(woodRoadDragEndpoint, padding, context);
73360 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
73362 buttonText: _t.html('intro.ok'),
73363 buttonCallback: advance
73367 function continueTo(nextStep) {
73368 context.map().on('move.intro drawn.intro', null);
73373 function deleteLines() {
73374 context.history().reset('doneUpdateLine');
73375 context.enter(modeBrowse(context));
73377 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73378 return chapter.restart();
73381 var msec = transitionTime(deleteLinesLoc, context.map().center());
73384 reveal(null, null, {
73389 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
73390 timeout(function () {
73391 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73392 var box = pad(deleteLinesLoc, padding, context);
73396 var advance = function advance() {
73397 continueTo(rightClickIntersection);
73400 reveal(box, helpHtml('intro.lines.delete_lines', {
73401 street: _t('intro.graph.name.12th-avenue')
73403 buttonText: _t.html('intro.ok'),
73404 buttonCallback: advance
73406 context.map().on('move.intro drawn.intro', function () {
73407 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73408 var box = pad(deleteLinesLoc, padding, context);
73411 reveal(box, helpHtml('intro.lines.delete_lines', {
73412 street: _t('intro.graph.name.12th-avenue')
73415 buttonText: _t.html('intro.ok'),
73416 buttonCallback: advance
73419 context.history().on('change.intro', function () {
73420 timeout(function () {
73421 continueTo(deleteLines);
73422 }, 500); // after any transition (e.g. if user deleted intersection)
73426 function continueTo(nextStep) {
73427 context.map().on('move.intro drawn.intro', null);
73428 context.history().on('change.intro', null);
73433 function rightClickIntersection() {
73434 context.history().reset('doneUpdateLine');
73435 context.enter(modeBrowse(context));
73436 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
73437 var rightClickString = helpHtml('intro.lines.split_street', {
73438 street1: _t('intro.graph.name.11th-avenue'),
73439 street2: _t('intro.graph.name.washington-street')
73440 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
73441 timeout(function () {
73442 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73443 var box = pad(eleventhAvenueEnd, padding, context);
73444 reveal(box, rightClickString);
73445 context.map().on('move.intro drawn.intro', function () {
73446 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73447 var box = pad(eleventhAvenueEnd, padding, context);
73448 reveal(box, rightClickString, {
73452 context.on('enter.intro', function (mode) {
73453 if (mode.id !== 'select') return;
73454 var ids = context.selectedIDs();
73455 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
73456 timeout(function () {
73457 var node = selectMenuItem(context, 'split').node();
73459 continueTo(splitIntersection);
73460 }, 50); // after menu visible
73462 context.history().on('change.intro', function () {
73463 timeout(function () {
73464 continueTo(deleteLines);
73465 }, 300); // after any transition (e.g. if user deleted intersection)
73469 function continueTo(nextStep) {
73470 context.map().on('move.intro drawn.intro', null);
73471 context.on('enter.intro', null);
73472 context.history().on('change.intro', null);
73477 function splitIntersection() {
73478 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73479 return continueTo(deleteLines);
73482 var node = selectMenuItem(context, 'split').node();
73485 return continueTo(rightClickIntersection);
73488 var wasChanged = false;
73489 _washingtonSegmentID = null;
73490 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
73491 street: _t('intro.graph.name.washington-street')
73495 context.map().on('move.intro drawn.intro', function () {
73496 var node = selectMenuItem(context, 'split').node();
73498 if (!wasChanged && !node) {
73499 return continueTo(rightClickIntersection);
73502 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
73503 street: _t('intro.graph.name.washington-street')
73509 context.history().on('change.intro', function (changed) {
73511 timeout(function () {
73512 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
73515 _washingtonSegmentID = changed.created()[0].id;
73516 continueTo(didSplit);
73518 _washingtonSegmentID = null;
73519 continueTo(retrySplit);
73521 }, 300); // after any transition (e.g. if user deleted intersection)
73524 function continueTo(nextStep) {
73525 context.map().on('move.intro drawn.intro', null);
73526 context.history().on('change.intro', null);
73531 function retrySplit() {
73532 context.enter(modeBrowse(context));
73533 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
73535 var advance = function advance() {
73536 continueTo(rightClickIntersection);
73539 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73540 var box = pad(eleventhAvenueEnd, padding, context);
73541 reveal(box, helpHtml('intro.lines.retry_split'), {
73542 buttonText: _t.html('intro.ok'),
73543 buttonCallback: advance
73545 context.map().on('move.intro drawn.intro', function () {
73546 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
73547 var box = pad(eleventhAvenueEnd, padding, context);
73548 reveal(box, helpHtml('intro.lines.retry_split'), {
73550 buttonText: _t.html('intro.ok'),
73551 buttonCallback: advance
73555 function continueTo(nextStep) {
73556 context.map().on('move.intro drawn.intro', null);
73561 function didSplit() {
73562 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73563 return continueTo(rightClickIntersection);
73566 var ids = context.selectedIDs();
73567 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
73568 var street = _t('intro.graph.name.washington-street');
73569 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73570 var box = pad(twelfthAvenue, padding, context);
73571 box.width = box.width / 2;
73572 reveal(box, helpHtml(string, {
73578 timeout(function () {
73579 context.map().centerZoomEase(twelfthAvenue, 18, 500);
73580 context.map().on('move.intro drawn.intro', function () {
73581 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73582 var box = pad(twelfthAvenue, padding, context);
73583 box.width = box.width / 2;
73584 reveal(box, helpHtml(string, {
73591 }, 600); // after initial reveal and curtain cut
73593 context.on('enter.intro', function () {
73594 var ids = context.selectedIDs();
73596 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
73597 continueTo(multiSelect);
73600 context.history().on('change.intro', function () {
73601 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73602 return continueTo(rightClickIntersection);
73606 function continueTo(nextStep) {
73607 context.map().on('move.intro drawn.intro', null);
73608 context.on('enter.intro', null);
73609 context.history().on('change.intro', null);
73614 function multiSelect() {
73615 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73616 return continueTo(rightClickIntersection);
73619 var ids = context.selectedIDs();
73620 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
73621 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
73623 if (hasWashington && hasTwelfth) {
73624 return continueTo(multiRightClick);
73625 } else if (!hasWashington && !hasTwelfth) {
73626 return continueTo(didSplit);
73629 context.map().centerZoomEase(twelfthAvenue, 18, 500);
73630 timeout(function () {
73631 var selected, other, padding, box;
73633 if (hasWashington) {
73634 selected = _t('intro.graph.name.washington-street');
73635 other = _t('intro.graph.name.12th-avenue');
73636 padding = 60 * Math.pow(2, context.map().zoom() - 18);
73637 box = pad(twelfthAvenueEnd, padding, context);
73640 selected = _t('intro.graph.name.12th-avenue');
73641 other = _t('intro.graph.name.washington-street');
73642 padding = 200 * Math.pow(2, context.map().zoom() - 18);
73643 box = pad(twelfthAvenue, padding, context);
73647 reveal(box, helpHtml('intro.lines.multi_select', {
73648 selected: selected,
73650 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
73651 selected: selected,
73654 context.map().on('move.intro drawn.intro', function () {
73655 if (hasWashington) {
73656 selected = _t('intro.graph.name.washington-street');
73657 other = _t('intro.graph.name.12th-avenue');
73658 padding = 60 * Math.pow(2, context.map().zoom() - 18);
73659 box = pad(twelfthAvenueEnd, padding, context);
73662 selected = _t('intro.graph.name.12th-avenue');
73663 other = _t('intro.graph.name.washington-street');
73664 padding = 200 * Math.pow(2, context.map().zoom() - 18);
73665 box = pad(twelfthAvenue, padding, context);
73669 reveal(box, helpHtml('intro.lines.multi_select', {
73670 selected: selected,
73672 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
73673 selected: selected,
73679 context.on('enter.intro', function () {
73680 continueTo(multiSelect);
73682 context.history().on('change.intro', function () {
73683 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73684 return continueTo(rightClickIntersection);
73689 function continueTo(nextStep) {
73690 context.map().on('move.intro drawn.intro', null);
73691 context.on('enter.intro', null);
73692 context.history().on('change.intro', null);
73697 function multiRightClick() {
73698 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73699 return continueTo(rightClickIntersection);
73702 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73703 var box = pad(twelfthAvenue, padding, context);
73704 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
73705 reveal(box, rightClickString);
73706 context.map().on('move.intro drawn.intro', function () {
73707 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73708 var box = pad(twelfthAvenue, padding, context);
73709 reveal(box, rightClickString, {
73713 context.ui().editMenu().on('toggled.intro', function (open) {
73715 timeout(function () {
73716 var ids = context.selectedIDs();
73718 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
73719 var node = selectMenuItem(context, 'delete').node();
73721 continueTo(multiDelete);
73722 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
73723 return continueTo(multiSelect);
73725 return continueTo(didSplit);
73727 }, 300); // after edit menu visible
73729 context.history().on('change.intro', function () {
73730 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73731 return continueTo(rightClickIntersection);
73735 function continueTo(nextStep) {
73736 context.map().on('move.intro drawn.intro', null);
73737 context.ui().editMenu().on('toggled.intro', null);
73738 context.history().on('change.intro', null);
73743 function multiDelete() {
73744 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
73745 return continueTo(rightClickIntersection);
73748 var node = selectMenuItem(context, 'delete').node();
73749 if (!node) return continueTo(multiRightClick);
73750 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
73753 context.map().on('move.intro drawn.intro', function () {
73754 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
73759 context.on('exit.intro', function () {
73760 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
73761 return continueTo(multiSelect); // left select mode but roads still exist
73764 context.history().on('change.intro', function () {
73765 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
73766 continueTo(retryDelete); // changed something but roads still exist
73772 function continueTo(nextStep) {
73773 context.map().on('move.intro drawn.intro', null);
73774 context.on('exit.intro', null);
73775 context.history().on('change.intro', null);
73780 function retryDelete() {
73781 context.enter(modeBrowse(context));
73782 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
73783 var box = pad(twelfthAvenue, padding, context);
73784 reveal(box, helpHtml('intro.lines.retry_delete'), {
73785 buttonText: _t.html('intro.ok'),
73786 buttonCallback: function buttonCallback() {
73787 continueTo(multiSelect);
73791 function continueTo(nextStep) {
73797 dispatch.call('done');
73798 reveal('.ideditor', helpHtml('intro.lines.play', {
73799 next: _t('intro.buildings.title')
73801 tooltipBox: '.intro-nav-wrap .chapter-building',
73802 buttonText: _t.html('intro.ok'),
73803 buttonCallback: function buttonCallback() {
73804 reveal('.ideditor');
73809 chapter.enter = function () {
73813 chapter.exit = function () {
73814 timeouts.forEach(window.clearTimeout);
73815 select(window).on('pointerdown.intro mousedown.intro', null, true);
73816 context.on('enter.intro exit.intro', null);
73817 context.map().on('move.intro drawn.intro', null);
73818 context.history().on('change.intro', null);
73819 context.container().select('.inspector-wrap').on('wheel.intro', null);
73820 context.container().select('.preset-list-button').on('click.intro', null);
73823 chapter.restart = function () {
73828 return utilRebind(chapter, dispatch, 'on');
73831 function uiIntroBuilding(context, reveal) {
73832 var dispatch = dispatch$8('done');
73833 var house = [-85.62815, 41.95638];
73834 var tank = [-85.62732, 41.95347];
73835 var buildingCatetory = _mainPresetIndex.item('category-building');
73836 var housePreset = _mainPresetIndex.item('building/house');
73837 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
73839 var _houseID = null;
73840 var _tankID = null;
73842 title: 'intro.buildings.title'
73845 function timeout(f, t) {
73846 timeouts.push(window.setTimeout(f, t));
73849 function eventCancel(d3_event) {
73850 d3_event.stopPropagation();
73851 d3_event.preventDefault();
73854 function revealHouse(center, text, options) {
73855 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
73856 var box = pad(center, padding, context);
73857 reveal(box, text, options);
73860 function revealTank(center, text, options) {
73861 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
73862 var box = pad(center, padding, context);
73863 reveal(box, text, options);
73866 function addHouse() {
73867 context.enter(modeBrowse(context));
73868 context.history().reset('initial');
73870 var msec = transitionTime(house, context.map().center());
73873 reveal(null, null, {
73878 context.map().centerZoomEase(house, 19, msec);
73879 timeout(function () {
73880 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
73881 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
73882 context.on('enter.intro', function (mode) {
73883 if (mode.id !== 'add-area') return;
73884 continueTo(startHouse);
73888 function continueTo(nextStep) {
73889 context.on('enter.intro', null);
73894 function startHouse() {
73895 if (context.mode().id !== 'add-area') {
73896 return continueTo(addHouse);
73900 context.map().zoomEase(20, 500);
73901 timeout(function () {
73902 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
73903 revealHouse(house, startString);
73904 context.map().on('move.intro drawn.intro', function () {
73905 revealHouse(house, startString, {
73909 context.on('enter.intro', function (mode) {
73910 if (mode.id !== 'draw-area') return chapter.restart();
73911 continueTo(continueHouse);
73913 }, 550); // after easing
73915 function continueTo(nextStep) {
73916 context.map().on('move.intro drawn.intro', null);
73917 context.on('enter.intro', null);
73922 function continueHouse() {
73923 if (context.mode().id !== 'draw-area') {
73924 return continueTo(addHouse);
73928 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
73929 revealHouse(house, continueString);
73930 context.map().on('move.intro drawn.intro', function () {
73931 revealHouse(house, continueString, {
73935 context.on('enter.intro', function (mode) {
73936 if (mode.id === 'draw-area') {
73938 } else if (mode.id === 'select') {
73939 var graph = context.graph();
73940 var way = context.entity(context.selectedIDs()[0]);
73941 var nodes = graph.childNodes(way);
73942 var points = utilArrayUniq(nodes).map(function (n) {
73943 return context.projection(n.loc);
73946 if (isMostlySquare(points)) {
73948 return continueTo(chooseCategoryBuilding);
73950 return continueTo(retryHouse);
73953 return chapter.restart();
73957 function continueTo(nextStep) {
73958 context.map().on('move.intro drawn.intro', null);
73959 context.on('enter.intro', null);
73964 function retryHouse() {
73965 var onClick = function onClick() {
73966 continueTo(addHouse);
73969 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
73970 buttonText: _t.html('intro.ok'),
73971 buttonCallback: onClick
73973 context.map().on('move.intro drawn.intro', function () {
73974 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
73976 buttonText: _t.html('intro.ok'),
73977 buttonCallback: onClick
73981 function continueTo(nextStep) {
73982 context.map().on('move.intro drawn.intro', null);
73987 function chooseCategoryBuilding() {
73988 if (!_houseID || !context.hasEntity(_houseID)) {
73992 var ids = context.selectedIDs();
73994 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
73995 context.enter(modeSelect(context, [_houseID]));
73996 } // disallow scrolling
73999 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74000 timeout(function () {
74001 // reset pane, in case user somehow happened to change it..
74002 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74003 var button = context.container().select('.preset-category-building .preset-list-button');
74004 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
74005 category: buildingCatetory.name()
74007 button.on('click.intro', function () {
74008 button.on('click.intro', null);
74009 continueTo(choosePresetHouse);
74011 }, 400); // after preset list pane visible..
74013 context.on('enter.intro', function (mode) {
74014 if (!_houseID || !context.hasEntity(_houseID)) {
74015 return continueTo(addHouse);
74018 var ids = context.selectedIDs();
74020 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
74021 return continueTo(chooseCategoryBuilding);
74025 function continueTo(nextStep) {
74026 context.container().select('.inspector-wrap').on('wheel.intro', null);
74027 context.container().select('.preset-list-button').on('click.intro', null);
74028 context.on('enter.intro', null);
74033 function choosePresetHouse() {
74034 if (!_houseID || !context.hasEntity(_houseID)) {
74038 var ids = context.selectedIDs();
74040 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
74041 context.enter(modeSelect(context, [_houseID]));
74042 } // disallow scrolling
74045 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74046 timeout(function () {
74047 // reset pane, in case user somehow happened to change it..
74048 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74049 var button = context.container().select('.preset-building-house .preset-list-button');
74050 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
74051 preset: housePreset.name()
74055 button.on('click.intro', function () {
74056 button.on('click.intro', null);
74057 continueTo(closeEditorHouse);
74059 }, 400); // after preset list pane visible..
74061 context.on('enter.intro', function (mode) {
74062 if (!_houseID || !context.hasEntity(_houseID)) {
74063 return continueTo(addHouse);
74066 var ids = context.selectedIDs();
74068 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
74069 return continueTo(chooseCategoryBuilding);
74073 function continueTo(nextStep) {
74074 context.container().select('.inspector-wrap').on('wheel.intro', null);
74075 context.container().select('.preset-list-button').on('click.intro', null);
74076 context.on('enter.intro', null);
74081 function closeEditorHouse() {
74082 if (!_houseID || !context.hasEntity(_houseID)) {
74086 var ids = context.selectedIDs();
74088 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
74089 context.enter(modeSelect(context, [_houseID]));
74092 context.history().checkpoint('hasHouse');
74093 context.on('exit.intro', function () {
74094 continueTo(rightClickHouse);
74096 timeout(function () {
74097 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
74098 button: icon('#iD-icon-close', 'inline')
74102 function continueTo(nextStep) {
74103 context.on('exit.intro', null);
74108 function rightClickHouse() {
74109 if (!_houseID) return chapter.restart();
74110 context.enter(modeBrowse(context));
74111 context.history().reset('hasHouse');
74112 var zoom = context.map().zoom();
74118 context.map().centerZoomEase(house, zoom, 500);
74119 context.on('enter.intro', function (mode) {
74120 if (mode.id !== 'select') return;
74121 var ids = context.selectedIDs();
74122 if (ids.length !== 1 || ids[0] !== _houseID) return;
74123 timeout(function () {
74124 var node = selectMenuItem(context, 'orthogonalize').node();
74126 continueTo(clickSquare);
74127 }, 50); // after menu visible
74129 context.map().on('move.intro drawn.intro', function () {
74130 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
74131 revealHouse(house, rightclickString, {
74135 context.history().on('change.intro', function () {
74136 continueTo(rightClickHouse);
74139 function continueTo(nextStep) {
74140 context.on('enter.intro', null);
74141 context.map().on('move.intro drawn.intro', null);
74142 context.history().on('change.intro', null);
74147 function clickSquare() {
74148 if (!_houseID) return chapter.restart();
74149 var entity = context.hasEntity(_houseID);
74150 if (!entity) return continueTo(rightClickHouse);
74151 var node = selectMenuItem(context, 'orthogonalize').node();
74154 return continueTo(rightClickHouse);
74157 var wasChanged = false;
74158 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
74161 context.on('enter.intro', function (mode) {
74162 if (mode.id === 'browse') {
74163 continueTo(rightClickHouse);
74164 } else if (mode.id === 'move' || mode.id === 'rotate') {
74165 continueTo(retryClickSquare);
74168 context.map().on('move.intro', function () {
74169 var node = selectMenuItem(context, 'orthogonalize').node();
74171 if (!wasChanged && !node) {
74172 return continueTo(rightClickHouse);
74175 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
74180 context.history().on('change.intro', function () {
74182 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
74184 timeout(function () {
74185 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
74188 continueTo(doneSquare);
74190 continueTo(retryClickSquare);
74192 }, 500); // after transitioned actions
74195 function continueTo(nextStep) {
74196 context.on('enter.intro', null);
74197 context.map().on('move.intro', null);
74198 context.history().on('change.intro', null);
74203 function retryClickSquare() {
74204 context.enter(modeBrowse(context));
74205 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
74206 buttonText: _t.html('intro.ok'),
74207 buttonCallback: function buttonCallback() {
74208 continueTo(rightClickHouse);
74212 function continueTo(nextStep) {
74217 function doneSquare() {
74218 context.history().checkpoint('doneSquare');
74219 revealHouse(house, helpHtml('intro.buildings.done_square'), {
74220 buttonText: _t.html('intro.ok'),
74221 buttonCallback: function buttonCallback() {
74222 continueTo(addTank);
74226 function continueTo(nextStep) {
74231 function addTank() {
74232 context.enter(modeBrowse(context));
74233 context.history().reset('doneSquare');
74235 var msec = transitionTime(tank, context.map().center());
74238 reveal(null, null, {
74243 context.map().centerZoomEase(tank, 19.5, msec);
74244 timeout(function () {
74245 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
74246 context.on('enter.intro', function (mode) {
74247 if (mode.id !== 'add-area') return;
74248 continueTo(startTank);
74252 function continueTo(nextStep) {
74253 context.on('enter.intro', null);
74258 function startTank() {
74259 if (context.mode().id !== 'add-area') {
74260 return continueTo(addTank);
74264 timeout(function () {
74265 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
74266 revealTank(tank, startString);
74267 context.map().on('move.intro drawn.intro', function () {
74268 revealTank(tank, startString, {
74272 context.on('enter.intro', function (mode) {
74273 if (mode.id !== 'draw-area') return chapter.restart();
74274 continueTo(continueTank);
74276 }, 550); // after easing
74278 function continueTo(nextStep) {
74279 context.map().on('move.intro drawn.intro', null);
74280 context.on('enter.intro', null);
74285 function continueTank() {
74286 if (context.mode().id !== 'draw-area') {
74287 return continueTo(addTank);
74291 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
74292 revealTank(tank, continueString);
74293 context.map().on('move.intro drawn.intro', function () {
74294 revealTank(tank, continueString, {
74298 context.on('enter.intro', function (mode) {
74299 if (mode.id === 'draw-area') {
74301 } else if (mode.id === 'select') {
74302 _tankID = context.selectedIDs()[0];
74303 return continueTo(searchPresetTank);
74305 return continueTo(addTank);
74309 function continueTo(nextStep) {
74310 context.map().on('move.intro drawn.intro', null);
74311 context.on('enter.intro', null);
74316 function searchPresetTank() {
74317 if (!_tankID || !context.hasEntity(_tankID)) {
74321 var ids = context.selectedIDs();
74323 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
74324 context.enter(modeSelect(context, [_tankID]));
74325 } // disallow scrolling
74328 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74329 timeout(function () {
74330 // reset pane, in case user somehow happened to change it..
74331 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
74332 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74333 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
74334 preset: tankPreset.name()
74336 }, 400); // after preset list pane visible..
74338 context.on('enter.intro', function (mode) {
74339 if (!_tankID || !context.hasEntity(_tankID)) {
74340 return continueTo(addTank);
74343 var ids = context.selectedIDs();
74345 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
74346 // keep the user's area selected..
74347 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
74349 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
74351 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
74352 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
74353 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
74354 preset: tankPreset.name()
74356 context.history().on('change.intro', null);
74360 function checkPresetSearch() {
74361 var first = context.container().select('.preset-list-item:first-child');
74363 if (first.classed('preset-man_made-storage_tank')) {
74364 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
74365 preset: tankPreset.name()
74369 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
74370 context.history().on('change.intro', function () {
74371 continueTo(closeEditorTank);
74376 function continueTo(nextStep) {
74377 context.container().select('.inspector-wrap').on('wheel.intro', null);
74378 context.on('enter.intro', null);
74379 context.history().on('change.intro', null);
74380 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74385 function closeEditorTank() {
74386 if (!_tankID || !context.hasEntity(_tankID)) {
74390 var ids = context.selectedIDs();
74392 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
74393 context.enter(modeSelect(context, [_tankID]));
74396 context.history().checkpoint('hasTank');
74397 context.on('exit.intro', function () {
74398 continueTo(rightClickTank);
74400 timeout(function () {
74401 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
74402 button: icon('#iD-icon-close', 'inline')
74406 function continueTo(nextStep) {
74407 context.on('exit.intro', null);
74412 function rightClickTank() {
74413 if (!_tankID) return continueTo(addTank);
74414 context.enter(modeBrowse(context));
74415 context.history().reset('hasTank');
74416 context.map().centerEase(tank, 500);
74417 timeout(function () {
74418 context.on('enter.intro', function (mode) {
74419 if (mode.id !== 'select') return;
74420 var ids = context.selectedIDs();
74421 if (ids.length !== 1 || ids[0] !== _tankID) return;
74422 timeout(function () {
74423 var node = selectMenuItem(context, 'circularize').node();
74425 continueTo(clickCircle);
74426 }, 50); // after menu visible
74428 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
74429 revealTank(tank, rightclickString);
74430 context.map().on('move.intro drawn.intro', function () {
74431 revealTank(tank, rightclickString, {
74435 context.history().on('change.intro', function () {
74436 continueTo(rightClickTank);
74440 function continueTo(nextStep) {
74441 context.on('enter.intro', null);
74442 context.map().on('move.intro drawn.intro', null);
74443 context.history().on('change.intro', null);
74448 function clickCircle() {
74449 if (!_tankID) return chapter.restart();
74450 var entity = context.hasEntity(_tankID);
74451 if (!entity) return continueTo(rightClickTank);
74452 var node = selectMenuItem(context, 'circularize').node();
74455 return continueTo(rightClickTank);
74458 var wasChanged = false;
74459 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
74462 context.on('enter.intro', function (mode) {
74463 if (mode.id === 'browse') {
74464 continueTo(rightClickTank);
74465 } else if (mode.id === 'move' || mode.id === 'rotate') {
74466 continueTo(retryClickCircle);
74469 context.map().on('move.intro', function () {
74470 var node = selectMenuItem(context, 'circularize').node();
74472 if (!wasChanged && !node) {
74473 return continueTo(rightClickTank);
74476 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
74481 context.history().on('change.intro', function () {
74483 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
74485 timeout(function () {
74486 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
74491 continueTo(retryClickCircle);
74493 }, 500); // after transitioned actions
74496 function continueTo(nextStep) {
74497 context.on('enter.intro', null);
74498 context.map().on('move.intro', null);
74499 context.history().on('change.intro', null);
74504 function retryClickCircle() {
74505 context.enter(modeBrowse(context));
74506 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
74507 buttonText: _t.html('intro.ok'),
74508 buttonCallback: function buttonCallback() {
74509 continueTo(rightClickTank);
74513 function continueTo(nextStep) {
74519 dispatch.call('done');
74520 reveal('.ideditor', helpHtml('intro.buildings.play', {
74521 next: _t('intro.startediting.title')
74523 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
74524 buttonText: _t.html('intro.ok'),
74525 buttonCallback: function buttonCallback() {
74526 reveal('.ideditor');
74531 chapter.enter = function () {
74535 chapter.exit = function () {
74536 timeouts.forEach(window.clearTimeout);
74537 context.on('enter.intro exit.intro', null);
74538 context.map().on('move.intro drawn.intro', null);
74539 context.history().on('change.intro', null);
74540 context.container().select('.inspector-wrap').on('wheel.intro', null);
74541 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
74542 context.container().select('.more-fields .combobox-input').on('click.intro', null);
74545 chapter.restart = function () {
74550 return utilRebind(chapter, dispatch, 'on');
74553 function uiIntroStartEditing(context, reveal) {
74554 var dispatch = dispatch$8('done', 'startEditing');
74555 var modalSelection = select(null);
74557 title: 'intro.startediting.title'
74560 function showHelp() {
74561 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
74562 buttonText: _t.html('intro.ok'),
74563 buttonCallback: function buttonCallback() {
74569 function shortcuts() {
74570 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
74571 buttonText: _t.html('intro.ok'),
74572 buttonCallback: function buttonCallback() {
74578 function showSave() {
74579 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74581 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
74582 buttonText: _t.html('intro.ok'),
74583 buttonCallback: function buttonCallback() {
74589 function showStart() {
74590 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74592 modalSelection = uiModal(context.container());
74593 modalSelection.select('.modal').attr('class', 'modal-splash modal');
74594 modalSelection.selectAll('.close').remove();
74595 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
74596 modalSelection.remove();
74598 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
74599 startbutton.append('h2').html(_t.html('intro.startediting.start'));
74600 dispatch.call('startEditing');
74603 chapter.enter = function () {
74607 chapter.exit = function () {
74608 modalSelection.remove();
74609 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
74612 return utilRebind(chapter, dispatch, 'on');
74616 welcome: uiIntroWelcome,
74617 navigation: uiIntroNavigation,
74618 point: uiIntroPoint,
74621 building: uiIntroBuilding,
74622 startEditing: uiIntroStartEditing
74624 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
74625 function uiIntro(context) {
74626 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
74627 var _introGraph = {};
74631 function intro(selection) {
74632 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
74633 // create entities for intro graph and localize names
74634 for (var id in dataIntroGraph) {
74635 if (!_introGraph[id]) {
74636 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
74640 selection.call(startIntro);
74641 })["catch"](function () {
74646 function startIntro(selection) {
74647 context.enter(modeBrowse(context)); // Save current map state
74649 var osm = context.connection();
74650 var history = context.history().toJSON();
74651 var hash = window.location.hash;
74652 var center = context.map().center();
74653 var zoom = context.map().zoom();
74654 var background = context.background().baseLayerSource();
74655 var overlays = context.background().overlayLayerSources();
74656 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
74657 var caches = osm && osm.caches();
74658 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
74659 // (this needs to be before `context.inIntro(true)`)
74661 context.ui().sidebar.expand();
74662 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
74664 context.inIntro(true); // Load semi-real data used in intro
74667 osm.toggle(false).reset();
74670 context.history().reset();
74671 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
74672 context.history().checkpoint('initial'); // Setup imagery
74674 var imagery = context.background().findSource(INTRO_IMAGERY);
74677 context.background().baseLayerSource(imagery);
74679 context.background().bing();
74682 overlays.forEach(function (d) {
74683 return context.background().toggleOverlayLayer(d);
74684 }); // Setup data layers (only OSM)
74686 var layers = context.layers();
74687 layers.all().forEach(function (item) {
74688 // if the layer has the function `enabled`
74689 if (typeof item.layer.enabled === 'function') {
74690 item.layer.enabled(item.id === 'osm');
74693 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
74694 var curtain = uiCurtain(context.container().node());
74695 selection.call(curtain); // Store that the user started the walkthrough..
74697 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
74699 var storedProgress = corePreferences('walkthrough_progress') || '';
74700 var progress = storedProgress.split(';').filter(Boolean);
74701 var chapters = chapterFlow.map(function (chapter, i) {
74702 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
74703 buttons.filter(function (d) {
74704 return d.title === s.title;
74705 }).classed('finished', true);
74707 if (i < chapterFlow.length - 1) {
74708 var next = chapterFlow[i + 1];
74709 context.container().select("button.chapter-".concat(next)).classed('next', true);
74710 } // Store walkthrough progress..
74713 progress.push(chapter);
74714 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
74718 chapters[chapters.length - 1].on('startEditing', function () {
74719 // Store walkthrough progress..
74720 progress.push('startEditing');
74721 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
74723 var incomplete = utilArrayDifference(chapterFlow, progress);
74725 if (!incomplete.length) {
74726 corePreferences('walkthrough_completed', 'yes');
74731 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
74732 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
74735 osm.toggle(true).reset().caches(caches);
74738 context.history().reset().merge(Object.values(baseEntities));
74739 context.background().baseLayerSource(background);
74740 overlays.forEach(function (d) {
74741 return context.background().toggleOverlayLayer(d);
74745 context.history().fromJSON(history, false);
74748 context.map().centerZoom(center, zoom);
74749 window.location.replace(hash);
74750 context.inIntro(false);
74752 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
74753 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
74754 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
74755 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
74756 return "chapter chapter-".concat(chapterFlow[i]);
74757 }).on('click', enterChapter);
74758 buttons.append('span').html(function (d) {
74759 return _t.html(d.title);
74761 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
74762 enterChapter(null, chapters[0]);
74764 function enterChapter(d3_event, newChapter) {
74765 if (_currChapter) {
74766 _currChapter.exit();
74769 context.enter(modeBrowse(context));
74770 _currChapter = newChapter;
74772 _currChapter.enter();
74774 buttons.classed('next', false).classed('active', function (d) {
74775 return d.title === _currChapter.title;
74783 function uiIssuesInfo(context) {
74784 var warningsItem = {
74787 iconID: 'iD-icon-alert',
74788 descriptionID: 'issues.warnings_and_errors'
74790 var resolvedItem = {
74793 iconID: 'iD-icon-apply',
74794 descriptionID: 'issues.user_resolved_issues'
74797 function update(selection) {
74798 var shownItems = [];
74799 var liveIssues = context.validator().getIssues({
74800 what: corePreferences('validate-what') || 'edited',
74801 where: corePreferences('validate-where') || 'all'
74804 if (liveIssues.length) {
74805 warningsItem.count = liveIssues.length;
74806 shownItems.push(warningsItem);
74809 if (corePreferences('validate-what') === 'all') {
74810 var resolvedIssues = context.validator().getResolvedIssues();
74812 if (resolvedIssues.length) {
74813 resolvedItem.count = resolvedIssues.length;
74814 shownItems.push(resolvedItem);
74818 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
74821 chips.exit().remove();
74822 var enter = chips.enter().append('a').attr('class', function (d) {
74823 return 'chip ' + d.id + '-count';
74824 }).attr('href', '#').each(function (d) {
74825 var chipSelection = select(this);
74826 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
74827 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
74828 d3_event.preventDefault();
74829 tooltipBehavior.hide(select(this)); // open the Issues pane
74831 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
74833 chipSelection.call(svgIcon('#' + d.iconID));
74835 enter.append('span').attr('class', 'count');
74836 enter.merge(chips).selectAll('span.count').html(function (d) {
74837 return d.count.toString();
74841 return function (selection) {
74843 context.validator().on('validated.infobox', function () {
74849 function uiMapInMap(context) {
74850 function mapInMap(selection) {
74851 var backgroundLayer = rendererTileLayer(context);
74852 var overlayLayers = {};
74853 var projection = geoRawMercator();
74854 var dataLayer = svgData(projection, context).showLabels(false);
74855 var debugLayer = svgDebug(projection, context);
74856 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
74857 var wrap = select(null);
74858 var tiles = select(null);
74859 var viewport = select(null);
74860 var _isTransformed = false;
74861 var _isHidden = true;
74862 var _skipEvents = false;
74863 var _gesture = null;
74864 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
74866 var _dMini; // dimensions of minimap
74869 var _cMini; // center pixel of minimap
74872 var _tStart; // transform at start of gesture
74875 var _tCurr; // transform at most recent event
74880 function zoomStarted() {
74881 if (_skipEvents) return;
74882 _tStart = _tCurr = projection.transform();
74886 function zoomed(d3_event) {
74887 if (_skipEvents) return;
74888 var x = d3_event.transform.x;
74889 var y = d3_event.transform.y;
74890 var k = d3_event.transform.k;
74891 var isZooming = k !== _tStart.k;
74892 var isPanning = x !== _tStart.x || y !== _tStart.y;
74894 if (!isZooming && !isPanning) {
74895 return; // no change
74896 } // lock in either zooming or panning, don't allow both in minimap.
74900 _gesture = isZooming ? 'zoom' : 'pan';
74903 var tMini = projection.transform();
74906 if (_gesture === 'zoom') {
74907 scale = k / tMini.k;
74908 tX = (_cMini[0] / scale - _cMini[0]) * scale;
74909 tY = (_cMini[1] / scale - _cMini[1]) * scale;
74917 utilSetTransform(tiles, tX, tY, scale);
74918 utilSetTransform(viewport, 0, 0, scale);
74919 _isTransformed = true;
74920 _tCurr = identity$2.translate(x, y).scale(k);
74921 var zMain = geoScaleToZoom(context.projection.scale());
74922 var zMini = geoScaleToZoom(k);
74923 _zDiff = zMain - zMini;
74927 function zoomEnded() {
74928 if (_skipEvents) return;
74929 if (_gesture !== 'pan') return;
74930 updateProjection();
74932 context.map().center(projection.invert(_cMini)); // recenter main map..
74935 function updateProjection() {
74936 var loc = context.map().center();
74937 var tMain = context.projection.transform();
74938 var zMain = geoScaleToZoom(tMain.k);
74939 var zMini = Math.max(zMain - _zDiff, 0.5);
74940 var kMini = geoZoomToScale(zMini);
74941 projection.translate([tMain.x, tMain.y]).scale(kMini);
74942 var point = projection(loc);
74943 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
74944 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
74945 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
74946 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
74947 _tCurr = projection.transform();
74949 if (_isTransformed) {
74950 utilSetTransform(tiles, 0, 0);
74951 utilSetTransform(viewport, 0, 0);
74952 _isTransformed = false;
74955 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
74956 _skipEvents = true;
74957 wrap.call(zoom.transform, _tCurr);
74958 _skipEvents = false;
74961 function redraw() {
74962 clearTimeout(_timeoutID);
74963 if (_isHidden) return;
74964 updateProjection();
74965 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
74967 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
74968 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
74970 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
74971 var background = tiles.selectAll('.map-in-map-background').data([0]);
74972 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
74974 var overlaySources = context.background().overlayLayerSources();
74975 var activeOverlayLayers = [];
74977 for (var i = 0; i < overlaySources.length; i++) {
74978 if (overlaySources[i].validZoom(zMini)) {
74979 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
74980 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
74984 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
74985 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
74986 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
74987 return d.source().name();
74989 overlays.exit().remove();
74990 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
74991 select(this).call(layer);
74993 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
74994 dataLayers.exit().remove();
74995 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
74997 if (_gesture !== 'pan') {
74998 var getPath = d3_geoPath(projection);
75001 coordinates: [context.map().extent().polygon()]
75003 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
75004 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
75005 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
75006 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
75007 return getPath.area(d) < 30;
75012 function queueRedraw() {
75013 clearTimeout(_timeoutID);
75014 _timeoutID = setTimeout(function () {
75019 function toggle(d3_event) {
75020 if (d3_event) d3_event.preventDefault();
75021 _isHidden = !_isHidden;
75022 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
75025 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
75026 selection.selectAll('.map-in-map').style('display', 'none');
75029 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
75035 uiMapInMap.toggle = toggle;
75036 wrap = selection.selectAll('.map-in-map').data([0]);
75037 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..
75039 _dMini = [200, 150]; //utilGetDimensions(wrap);
75041 _cMini = geoVecScale(_dMini, 0.5);
75042 context.map().on('drawn.map-in-map', function (drawn) {
75043 if (drawn.full === true) {
75048 context.keybinding().on(_t('background.minimap.key'), toggle);
75054 function uiNotice(context) {
75055 return function (selection) {
75056 var div = selection.append('div').attr('class', 'notice');
75057 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
75058 context.map().zoomEase(context.minEditableZoom());
75059 }).on('wheel', function (d3_event) {
75060 // let wheel events pass through #4482
75061 var e2 = new WheelEvent(d3_event.type, d3_event);
75062 context.surface().node().dispatchEvent(e2);
75064 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
75066 function disableTooHigh() {
75067 var canEdit = context.map().zoom() >= context.minEditableZoom();
75068 div.style('display', canEdit ? 'none' : 'block');
75071 context.map().on('move.notice', debounce(disableTooHigh, 500));
75076 function uiPhotoviewer(context) {
75077 var dispatch = dispatch$8('resize');
75079 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
75081 function photoviewer(selection) {
75082 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
75083 if (services.streetside) {
75084 services.streetside.hideViewer(context);
75087 if (services.mapillary) {
75088 services.mapillary.hideViewer(context);
75091 if (services.openstreetcam) {
75092 services.openstreetcam.hideViewer(context);
75094 }).append('div').call(svgIcon('#iD-icon-close'));
75096 function preventDefault(d3_event) {
75097 d3_event.preventDefault();
75100 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75104 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75107 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch, {
75111 function buildResizeListener(target, eventName, dispatch, options) {
75112 var resizeOnX = !!options.resizeOnX;
75113 var resizeOnY = !!options.resizeOnY;
75114 var minHeight = options.minHeight || 240;
75115 var minWidth = options.minWidth || 320;
75122 function startResize(d3_event) {
75123 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
75124 d3_event.preventDefault();
75125 d3_event.stopPropagation();
75126 var mapSize = context.map().dimensions();
75129 var maxWidth = mapSize[0];
75130 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
75131 target.style('width', newWidth + 'px');
75135 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
75137 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
75138 target.style('height', newHeight + 'px');
75141 dispatch.call(eventName, target, utilGetDimensions(target, true));
75144 function clamp(num, min, max) {
75145 return Math.max(min, Math.min(num, max));
75148 function stopResize(d3_event) {
75149 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
75150 d3_event.preventDefault();
75151 d3_event.stopPropagation(); // remove all the listeners we added
75153 select(window).on('.' + eventName, null);
75156 return function initResize(d3_event) {
75157 d3_event.preventDefault();
75158 d3_event.stopPropagation();
75159 pointerId = d3_event.pointerId || 'mouse';
75160 startX = d3_event.clientX;
75161 startY = d3_event.clientY;
75162 var targetRect = target.node().getBoundingClientRect();
75163 startWidth = targetRect.width;
75164 startHeight = targetRect.height;
75165 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
75167 if (_pointerPrefix === 'pointer') {
75168 select(window).on('pointercancel.' + eventName, stopResize, false);
75174 photoviewer.onMapResize = function () {
75175 var photoviewer = context.container().select('.photoviewer');
75176 var content = context.container().select('.main-content');
75177 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
75178 // (-90 preserves space at top and bottom of map used by menus)
75180 var photoDimensions = utilGetDimensions(photoviewer, true);
75182 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
75183 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
75184 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
75185 dispatch.call('resize', photoviewer, setPhotoDimensions);
75189 return utilRebind(photoviewer, dispatch, 'on');
75192 function uiRestore(context) {
75193 return function (selection) {
75194 if (!context.history().hasRestorableChanges()) return;
75195 var modalSelection = uiModal(selection, true);
75196 modalSelection.select('.modal').attr('class', 'modal fillL');
75197 var introModal = modalSelection.select('.content');
75198 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
75199 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
75200 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
75201 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
75202 context.history().restore();
75203 modalSelection.remove();
75205 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
75206 restore.append('div').html(_t.html('restore.restore'));
75207 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
75208 context.history().clearSaved();
75209 modalSelection.remove();
75211 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
75212 reset.append('div').html(_t.html('restore.reset'));
75213 restore.node().focus();
75217 function uiScale(context) {
75218 var projection = context.projection,
75219 isImperial = !_mainLocalizer.usesMetric(),
75223 function scaleDefs(loc1, loc2) {
75224 var lat = (loc2[1] + loc1[1]) / 2,
75225 conversion = isImperial ? 3.28084 : 1,
75226 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
75238 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
75240 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
75241 } // determine a user-friendly endpoint for the scale
75244 for (i = 0; i < buckets.length; i++) {
75248 scale.dist = Math.floor(dist / val) * val;
75251 scale.dist = +dist.toFixed(2);
75255 dLon = geoMetersToLon(scale.dist / conversion, lat);
75256 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
75257 scale.text = displayLength(scale.dist / conversion, isImperial);
75261 function update(selection) {
75262 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
75263 var dims = context.map().dimensions(),
75264 loc1 = projection.invert([0, dims[1]]),
75265 loc2 = projection.invert([maxLength, dims[1]]),
75266 scale = scaleDefs(loc1, loc2);
75267 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
75268 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
75271 return function (selection) {
75272 function switchUnits() {
75273 isImperial = !isImperial;
75274 selection.call(update);
75277 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
75278 scalegroup.append('path').attr('class', 'scale-path');
75279 selection.append('div').attr('class', 'scale-text');
75280 selection.call(update);
75281 context.map().on('move.scale', function () {
75287 function uiShortcuts(context) {
75288 var detected = utilDetect();
75289 var _activeTab = 0;
75291 var _modalSelection;
75293 var _selection = select(null);
75295 var _dataShortcuts;
75297 function shortcutsModal(_modalSelection) {
75298 _modalSelection.select('.modal').classed('modal-shortcuts', true);
75300 var content = _modalSelection.select('.content');
75302 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
75303 _mainFileFetcher.get('shortcuts').then(function (data) {
75304 _dataShortcuts = data;
75305 content.call(render);
75306 })["catch"](function () {
75311 function render(selection) {
75312 if (!_dataShortcuts) return;
75313 var wrapper = selection.selectAll('.wrapper').data([0]);
75314 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
75315 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
75316 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
75317 wrapper = wrapper.merge(wrapperEnter);
75318 var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
75319 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
75320 d3_event.preventDefault();
75322 var i = _dataShortcuts.indexOf(d);
75327 tabsEnter.append('span').html(function (d) {
75328 return _t.html(d.text);
75331 wrapper.selectAll('.tab').classed('active', function (d, i) {
75332 return i === _activeTab;
75334 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
75335 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
75336 return 'shortcut-tab shortcut-tab-' + d.tab;
75338 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
75340 }).enter().append('table').attr('class', 'shortcut-column');
75341 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
75343 }).enter().append('tr').attr('class', 'shortcut-row');
75344 var sectionRows = rowsEnter.filter(function (d) {
75345 return !d.shortcuts;
75347 sectionRows.append('td');
75348 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
75349 return _t.html(d.text);
75351 var shortcutRows = rowsEnter.filter(function (d) {
75352 return d.shortcuts;
75354 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
75355 var modifierKeys = shortcutKeys.filter(function (d) {
75356 return d.modifiers;
75358 modifierKeys.selectAll('kbd.modifier').data(function (d) {
75359 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
75361 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
75364 return d.modifiers;
75366 }).enter().each(function () {
75367 var selection = select(this);
75368 selection.append('kbd').attr('class', 'modifier').html(function (d) {
75369 return uiCmd.display(d);
75371 selection.append('span').html('+');
75373 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
75374 var arr = d.shortcuts;
75376 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
75378 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
75380 } // replace translations
75383 arr = arr.map(function (s) {
75384 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
75386 return utilArrayUniq(arr).map(function (s) {
75389 separator: d.separator,
75393 }).enter().each(function (d, i, nodes) {
75394 var selection = select(this);
75395 var click = d.shortcut.toLowerCase().match(/(.*).click/);
75397 if (click && click[1]) {
75398 // replace "left_click", "right_click" with mouse icon
75399 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
75400 } else if (d.shortcut.toLowerCase() === 'long-press') {
75401 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
75402 } else if (d.shortcut.toLowerCase() === 'tap') {
75403 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
75405 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
75410 if (i < nodes.length - 1) {
75411 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
75412 } else if (i === nodes.length - 1 && d.suffix) {
75413 selection.append('span').html(d.suffix);
75416 shortcutKeys.filter(function (d) {
75418 }).each(function () {
75419 var selection = select(this);
75420 selection.append('span').html('+');
75421 selection.append('span').attr('class', 'gesture').html(function (d) {
75422 return _t.html(d.gesture);
75425 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
75426 return d.text ? _t.html(d.text) : "\xA0";
75429 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
75430 return i === _activeTab ? 'flex' : 'none';
75434 return function (selection, show) {
75435 _selection = selection;
75438 _modalSelection = uiModal(selection);
75440 _modalSelection.call(shortcutsModal);
75442 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
75443 if (context.container().selectAll('.modal-shortcuts').size()) {
75445 if (_modalSelection) {
75446 _modalSelection.close();
75448 _modalSelection = null;
75451 _modalSelection = uiModal(_selection);
75453 _modalSelection.call(shortcutsModal);
75460 function uiDataHeader() {
75463 function dataHeader(selection) {
75464 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
75465 return d.__featurehash__;
75467 header.exit().remove();
75468 var headerEnter = header.enter().append('div').attr('class', 'data-header');
75469 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
75470 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
75471 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
75474 dataHeader.datum = function (val) {
75475 if (!arguments.length) return _datum;
75483 // It is keyed on the `value` of the entry. Data should be an array of objects like:
75485 // value: 'string value', // required
75486 // display: 'label html' // optional
75487 // title: 'hover text' // optional
75488 // terms: ['search terms'] // optional
75491 var _comboHideTimerID;
75493 function uiCombobox(context, klass) {
75494 var dispatch = dispatch$8('accept', 'cancel');
75495 var container = context.container();
75496 var _suggestions = [];
75499 var _selected = null;
75500 var _canAutocomplete = true;
75501 var _caseSensitive = false;
75502 var _cancelFetch = false;
75506 var _mouseEnterHandler, _mouseLeaveHandler;
75508 var _fetcher = function _fetcher(val, cb) {
75509 cb(_data.filter(function (d) {
75510 var terms = d.terms || [];
75511 terms.push(d.value);
75512 return terms.some(function (term) {
75513 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
75518 var combobox = function combobox(input, attachTo) {
75519 if (!input || input.empty()) return;
75520 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 () {
75521 var parent = this.parentNode;
75522 var sibling = this.nextSibling;
75523 select(parent).selectAll('.combobox-caret').filter(function (d) {
75524 return d === input.node();
75525 }).data([input.node()]).enter().insert('div', function () {
75527 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
75528 d3_event.preventDefault(); // don't steal focus from input
75530 input.node().focus(); // focus the input as if it was clicked
75532 mousedown(d3_event);
75533 }).on('mouseup.combo-caret', function (d3_event) {
75534 d3_event.preventDefault(); // don't steal focus from input
75540 function mousedown(d3_event) {
75541 if (d3_event.button !== 0) return; // left click only
75543 _tDown = +new Date(); // clear selection
75545 var start = input.property('selectionStart');
75546 var end = input.property('selectionEnd');
75548 if (start !== end) {
75549 var val = utilGetSetValue(input);
75550 input.node().setSelectionRange(val.length, val.length);
75554 input.on('mouseup.combo-input', mouseup);
75557 function mouseup(d3_event) {
75558 input.on('mouseup.combo-input', null);
75559 if (d3_event.button !== 0) return; // left click only
75561 if (input.node() !== document.activeElement) return; // exit if this input is not focused
75563 var start = input.property('selectionStart');
75564 var end = input.property('selectionEnd');
75565 if (start !== end) return; // exit if user is selecting
75566 // not showing or showing for a different field - try to show it.
75568 var combo = container.selectAll('.combobox');
75570 if (combo.empty() || combo.datum() !== input.node()) {
75571 var tOrig = _tDown;
75572 window.setTimeout(function () {
75573 if (tOrig !== _tDown) return; // exit if user double clicked
75575 fetchComboData('', function () {
75586 fetchComboData(''); // prefetch values (may warm taginfo cache)
75590 _comboHideTimerID = window.setTimeout(hide, 75);
75594 hide(); // remove any existing
75596 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 (d3_event) {
75597 // prevent moving focus out of the input field
75598 d3_event.preventDefault();
75600 container.on('scroll.combo-scroll', render, true);
75604 if (_comboHideTimerID) {
75605 window.clearTimeout(_comboHideTimerID);
75606 _comboHideTimerID = undefined;
75609 container.selectAll('.combobox').remove();
75610 container.on('scroll.combo-scroll', null);
75613 function keydown(d3_event) {
75614 var shown = !container.selectAll('.combobox').empty();
75615 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
75617 switch (d3_event.keyCode) {
75618 case 8: // ⌫ Backspace
75622 d3_event.stopPropagation();
75625 input.on('input.combo-input', function () {
75626 var start = input.property('selectionStart');
75627 input.node().setSelectionRange(start, start);
75628 input.on('input.combo-input', change);
75639 d3_event.preventDefault();
75640 d3_event.stopPropagation();
75645 if (tagName === 'textarea' && !shown) return;
75646 d3_event.preventDefault();
75648 if (tagName === 'input' && !shown) {
75657 if (tagName === 'textarea' && !shown) return;
75658 d3_event.preventDefault();
75660 if (tagName === 'input' && !shown) {
75669 function keyup(d3_event) {
75670 switch (d3_event.keyCode) {
75681 } // Called whenever the input value is changed (e.g. on typing)
75684 function change() {
75685 fetchComboData(value(), function () {
75687 var val = input.property('value');
75689 if (_suggestions.length) {
75690 if (input.property('selectionEnd') === val.length) {
75691 _selected = tryAutocomplete();
75700 var combo = container.selectAll('.combobox');
75702 if (combo.empty()) {
75711 } // Called when the user presses up/down arrows to navigate the list
75714 function nav(dir) {
75715 if (_suggestions.length) {
75716 // try to determine previously selected index..
75719 for (var i = 0; i < _suggestions.length; i++) {
75720 if (_selected && _suggestions[i].value === _selected) {
75724 } // pick new _selected
75727 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
75728 _selected = _suggestions[index].value;
75729 input.property('value', _selected);
75736 function ensureVisible() {
75737 var combo = container.selectAll('.combobox');
75738 if (combo.empty()) return;
75739 var containerRect = container.node().getBoundingClientRect();
75740 var comboRect = combo.node().getBoundingClientRect();
75742 if (comboRect.bottom > containerRect.bottom) {
75743 var node = attachTo ? attachTo.node() : input.node();
75744 node.scrollIntoView({
75745 behavior: 'instant',
75749 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
75752 var selected = combo.selectAll('.combobox-option.selected').node();
75755 selected.scrollIntoView({
75756 behavior: 'smooth',
75763 var value = input.property('value');
75764 var start = input.property('selectionStart');
75765 var end = input.property('selectionEnd');
75767 if (start && end) {
75768 value = value.substring(0, start);
75774 function fetchComboData(v, cb) {
75775 _cancelFetch = false;
75777 _fetcher.call(input, v, function (results) {
75778 // already chose a value, don't overwrite or autocomplete it
75779 if (_cancelFetch) return;
75780 _suggestions = results;
75781 results.forEach(function (d) {
75782 _fetched[d.value] = d;
75791 function tryAutocomplete() {
75792 if (!_canAutocomplete) return;
75793 var val = _caseSensitive ? value() : value().toLowerCase();
75794 if (!val) return; // Don't autocomplete if user is typing a number - #4935
75796 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
75797 var bestIndex = -1;
75799 for (var i = 0; i < _suggestions.length; i++) {
75800 var suggestion = _suggestions[i].value;
75801 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
75803 if (compare === val) {
75805 break; // otherwise lock in the first result that starts with the search string..
75806 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
75811 if (bestIndex !== -1) {
75812 var bestVal = _suggestions[bestIndex].value;
75813 input.property('value', bestVal);
75814 input.node().setSelectionRange(val.length, bestVal.length);
75819 function render() {
75820 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
75825 var shown = !container.selectAll('.combobox').empty();
75826 if (!shown) return;
75827 var combo = container.selectAll('.combobox');
75828 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
75831 options.exit().remove(); // enter/update
75833 options.enter().append('a').attr('class', function (d) {
75834 return 'combobox-option ' + (d.klass || '');
75835 }).attr('title', function (d) {
75837 }).html(function (d) {
75838 return d.display || d.value;
75839 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
75840 return d.value === _selected;
75841 }).on('click.combo-option', accept).order();
75842 var node = attachTo ? attachTo.node() : input.node();
75843 var containerRect = container.node().getBoundingClientRect();
75844 var rect = node.getBoundingClientRect();
75845 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
75846 } // Dispatches an 'accept' event
75847 // Then hides the combobox.
75850 function accept(d3_event, d) {
75851 _cancelFetch = true;
75852 var thiz = input.node();
75855 // user clicked on a suggestion
75856 utilGetSetValue(input, d.value); // replace field contents
75858 utilTriggerEvent(input, 'change');
75859 } // clear (and keep) selection
75862 var val = utilGetSetValue(input);
75863 thiz.setSelectionRange(val.length, val.length);
75865 dispatch.call('accept', thiz, d, val);
75867 } // Dispatches an 'cancel' event
75868 // Then hides the combobox.
75871 function cancel() {
75872 _cancelFetch = true;
75873 var thiz = input.node(); // clear (and remove) selection, and replace field contents
75875 var val = utilGetSetValue(input);
75876 var start = input.property('selectionStart');
75877 var end = input.property('selectionEnd');
75878 val = val.slice(0, start) + val.slice(end);
75879 utilGetSetValue(input, val);
75880 thiz.setSelectionRange(val.length, val.length);
75881 dispatch.call('cancel', thiz);
75886 combobox.canAutocomplete = function (val) {
75887 if (!arguments.length) return _canAutocomplete;
75888 _canAutocomplete = val;
75892 combobox.caseSensitive = function (val) {
75893 if (!arguments.length) return _caseSensitive;
75894 _caseSensitive = val;
75898 combobox.data = function (val) {
75899 if (!arguments.length) return _data;
75904 combobox.fetcher = function (val) {
75905 if (!arguments.length) return _fetcher;
75910 combobox.minItems = function (val) {
75911 if (!arguments.length) return _minItems;
75916 combobox.itemsMouseEnter = function (val) {
75917 if (!arguments.length) return _mouseEnterHandler;
75918 _mouseEnterHandler = val;
75922 combobox.itemsMouseLeave = function (val) {
75923 if (!arguments.length) return _mouseLeaveHandler;
75924 _mouseLeaveHandler = val;
75928 return utilRebind(combobox, dispatch, 'on');
75931 uiCombobox.off = function (input, context) {
75932 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);
75933 context.container().on('scroll.combo-scroll', null);
75936 function uiDisclosure(context, key, expandedDefault) {
75937 var dispatch = dispatch$8('toggled');
75941 var _label = utilFunctor('');
75943 var _updatePreference = true;
75945 var _content = function _content() {};
75947 var disclosure = function disclosure(selection) {
75948 if (_expanded === undefined || _expanded === null) {
75949 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
75950 var preference = corePreferences('disclosure.' + key + '.expanded');
75951 _expanded = preference === null ? !!expandedDefault : preference === 'true';
75954 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
75956 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
75957 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
75959 hideToggle = hideToggleEnter.merge(hideToggle);
75960 hideToggle.on('click', toggle).classed('expanded', _expanded);
75961 hideToggle.selectAll('.hide-toggle-text').html(_label());
75962 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
75963 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
75965 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
75968 wrap.call(_content);
75971 function toggle(d3_event) {
75972 d3_event.preventDefault();
75973 _expanded = !_expanded;
75975 if (_updatePreference) {
75976 corePreferences('disclosure.' + key + '.expanded', _expanded);
75979 hideToggle.classed('expanded', _expanded);
75980 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
75981 wrap.call(uiToggle(_expanded));
75984 wrap.call(_content);
75987 dispatch.call('toggled', this, _expanded);
75991 disclosure.label = function (val) {
75992 if (!arguments.length) return _label;
75993 _label = utilFunctor(val);
75997 disclosure.expanded = function (val) {
75998 if (!arguments.length) return _expanded;
76003 disclosure.updatePreference = function (val) {
76004 if (!arguments.length) return _updatePreference;
76005 _updatePreference = val;
76009 disclosure.content = function (val) {
76010 if (!arguments.length) return _content;
76015 return utilRebind(disclosure, dispatch, 'on');
76018 // Can be labeled and collapsible.
76020 function uiSection(id, context) {
76021 var _classes = utilFunctor('');
76023 var _shouldDisplay;
76031 var _expandedByDefault = utilFunctor(true);
76033 var _disclosureContent;
76035 var _disclosureExpanded;
76037 var _containerSelection = select(null);
76043 section.classes = function (val) {
76044 if (!arguments.length) return _classes;
76045 _classes = utilFunctor(val);
76049 section.label = function (val) {
76050 if (!arguments.length) return _label;
76051 _label = utilFunctor(val);
76055 section.expandedByDefault = function (val) {
76056 if (!arguments.length) return _expandedByDefault;
76057 _expandedByDefault = utilFunctor(val);
76061 section.shouldDisplay = function (val) {
76062 if (!arguments.length) return _shouldDisplay;
76063 _shouldDisplay = utilFunctor(val);
76067 section.content = function (val) {
76068 if (!arguments.length) return _content;
76073 section.disclosureContent = function (val) {
76074 if (!arguments.length) return _disclosureContent;
76075 _disclosureContent = val;
76079 section.disclosureExpanded = function (val) {
76080 if (!arguments.length) return _disclosureExpanded;
76081 _disclosureExpanded = val;
76083 }; // may be called multiple times
76086 section.render = function (selection) {
76087 _containerSelection = selection.selectAll('.section-' + id).data([0]);
76089 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
76091 _containerSelection = sectionEnter.merge(_containerSelection);
76093 _containerSelection.call(renderContent);
76096 section.reRender = function () {
76097 _containerSelection.call(renderContent);
76100 section.selection = function () {
76101 return _containerSelection;
76104 section.disclosure = function () {
76105 return _disclosure;
76106 }; // may be called multiple times
76109 function renderContent(selection) {
76110 if (_shouldDisplay) {
76111 var shouldDisplay = _shouldDisplay();
76113 selection.classed('hide', !shouldDisplay);
76115 if (!shouldDisplay) {
76116 selection.html('');
76121 if (_disclosureContent) {
76122 if (!_disclosure) {
76123 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
76124 /*.on('toggled', function(expanded) {
76125 if (expanded) { selection.node().parentNode.scrollTop += 200; }
76127 .content(_disclosureContent);
76130 if (_disclosureExpanded !== undefined) {
76131 _disclosure.expanded(_disclosureExpanded);
76133 _disclosureExpanded = undefined;
76136 selection.call(_disclosure);
76141 selection.call(_content);
76149 // key: 'string', // required
76150 // value: 'string' // optional
76154 // qid: 'string' // brand wikidata (e.g. 'Q37158')
76158 function uiTagReference(what) {
76159 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
76160 var tagReference = {};
76162 var _button = select(null);
76164 var _body = select(null);
76171 if (!wikibase) return;
76173 _button.classed('tag-reference-loading', true);
76175 wikibase.getDocs(what, gotDocs);
76178 function gotDocs(err, docs) {
76181 if (!docs || !docs.title) {
76182 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
76188 if (docs.imageURL) {
76189 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
76191 }).on('error', function () {
76192 select(this).remove();
76199 _body.append('p').attr('class', 'tag-reference-description').html(docs.description ? _mainLocalizer.htmlForLocalizedText(docs.description, docs.descriptionLocaleCode) : _t.html('inspector.no_documentation_key')).append('a').attr('class', 'tag-reference-edit').attr('target', '_blank').attr('title', _t('inspector.edit_reference')).attr('href', docs.editURL).call(svgIcon('#iD-icon-edit', 'inline'));
76202 _body.append('a').attr('class', 'tag-reference-link').attr('target', '_blank').attr('href', docs.wiki.url).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html(docs.wiki.text));
76203 } // Add link to info about "good changeset comments" - #2923
76206 if (what.key === 'comment') {
76207 _body.append('a').attr('class', 'tag-reference-comment-link').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', _t('commit.about_changeset_comments_link')).append('span').html(_t.html('commit.about_changeset_comments'));
76214 _button.classed('tag-reference-loading', false);
76216 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
76220 _button.selectAll('svg.icon use').each(function () {
76221 var iconUse = select(this);
76223 if (iconUse.attr('href') === '#iD-icon-info') {
76224 iconUse.attr('href', '#iD-icon-info-filled');
76230 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
76231 _body.classed('expanded', false);
76236 _button.selectAll('svg.icon use').each(function () {
76237 var iconUse = select(this);
76239 if (iconUse.attr('href') === '#iD-icon-info-filled') {
76240 iconUse.attr('href', '#iD-icon-info');
76245 tagReference.button = function (selection, klass, iconName) {
76246 _button = selection.selectAll('.tag-reference-button').data([0]);
76247 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
76249 _button.on('click', function (d3_event) {
76250 d3_event.stopPropagation();
76251 d3_event.preventDefault();
76252 this.blur(); // avoid keeping focus on the button - #4641
76256 } else if (_loaded) {
76264 tagReference.body = function (selection) {
76265 var itemID = what.qid || what.key + '-' + (what.value || '');
76266 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
76270 _body.exit().remove();
76272 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
76274 if (_showing === false) {
76279 tagReference.showing = function (val) {
76280 if (!arguments.length) return _showing;
76282 return tagReference;
76285 return tagReference;
76288 function uiSectionRawTagEditor(id, context) {
76289 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
76290 var count = Object.keys(_tags).filter(function (d) {
76293 return _t('inspector.title_count', {
76294 title: _t.html('inspector.tags'),
76297 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
76298 var taginfo = services.taginfo;
76299 var dispatch = dispatch$8('change');
76300 var availableViews = [{
76302 icon: '#fas-th-list'
76305 icon: '#fas-i-cursor'
76308 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
76311 var _readOnlyTags = []; // the keys in the order we want them to display
76313 var _orderedKeys = [];
76314 var _showBlank = false;
76315 var _pendingChange = null;
76325 var _didInteract = false;
76327 function interacted() {
76328 _didInteract = true;
76331 function renderDisclosureContent(wrap) {
76332 // remove deleted keys
76333 _orderedKeys = _orderedKeys.filter(function (key) {
76334 return _tags[key] !== undefined;
76335 }); // When switching to a different entity or changing the state (hover/select)
76336 // reorder the keys alphabetically.
76337 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
76338 // Otherwise leave their order alone - #5857, #5927
76340 var all = Object.keys(_tags).sort();
76341 var missingKeys = utilArrayDifference(all, _orderedKeys);
76343 for (var i in missingKeys) {
76344 _orderedKeys.push(missingKeys[i]);
76345 } // assemble row data
76348 var rowData = _orderedKeys.map(function (key, i) {
76354 }); // append blank row last, if necessary
76357 if (!rowData.length || _showBlank) {
76358 _showBlank = false;
76360 index: rowData.length,
76367 var options = wrap.selectAll('.raw-tag-options').data([0]);
76368 options.exit().remove();
76369 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
76370 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
76373 optionEnter.append('button').attr('class', function (d) {
76374 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
76375 }).attr('title', function (d) {
76376 return _t('icons.' + d.id);
76377 }).on('click', function (d3_event, d) {
76379 corePreferences('raw-tag-editor-view', d.id);
76380 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
76381 return datum === d;
76383 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
76384 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
76385 }).each(function (d) {
76386 select(this).call(svgIcon(d.icon));
76387 }); // View as Text
76389 var textData = rowsToText(rowData);
76390 var textarea = wrap.selectAll('.tag-text').data([0]);
76391 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);
76392 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
76394 var list = wrap.selectAll('.tag-list').data([0]);
76395 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
76397 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
76398 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
76399 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
76401 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
76404 var items = list.selectAll('.tag-row').data(rowData, function (d) {
76407 items.exit().each(unbind).remove(); // Enter
76409 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
76410 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
76411 innerWrap.append('div').attr('class', 'key-wrap').append('input').property('type', 'text').attr('class', 'key').call(utilNoAuto).on('focus', interacted).on('blur', keyChange).on('change', keyChange);
76412 innerWrap.append('div').attr('class', 'value-wrap').append('input').property('type', 'text').attr('class', 'value').call(utilNoAuto).on('focus', interacted).on('blur', valueChange).on('change', valueChange).on('keydown.push-more', pushMore);
76413 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
76415 items = items.merge(itemsEnter).sort(function (a, b) {
76416 return a.index - b.index;
76418 items.each(function (d) {
76419 var row = select(this);
76420 var key = row.select('input.key'); // propagate bound data
76422 var value = row.select('input.value'); // propagate bound data
76424 if (_entityIDs && taginfo && _state !== 'hover') {
76425 bindTypeahead(key, value);
76428 var referenceOptions = {
76432 if (typeof d.value === 'string') {
76433 referenceOptions.value = d.value;
76436 var reference = uiTagReference(referenceOptions);
76438 if (_state === 'hover') {
76439 reference.showing(false);
76442 row.select('.inner-wrap') // propagate bound data
76443 .call(reference.button);
76444 row.call(reference.body);
76445 row.select('button.remove'); // propagate bound data
76447 items.selectAll('input.key').attr('title', function (d) {
76449 }).call(utilGetSetValue, function (d) {
76451 }).attr('readonly', function (d) {
76452 return isReadOnly(d) || typeof d.value !== 'string' || null;
76454 items.selectAll('input.value').attr('title', function (d) {
76455 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
76456 }).classed('mixed', function (d) {
76457 return Array.isArray(d.value);
76458 }).attr('placeholder', function (d) {
76459 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
76460 }).call(utilGetSetValue, function (d) {
76461 return typeof d.value === 'string' ? d.value : '';
76462 }).attr('readonly', function (d) {
76463 return isReadOnly(d) || null;
76465 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
76468 function isReadOnly(d) {
76469 for (var i = 0; i < _readOnlyTags.length; i++) {
76470 if (d.key.match(_readOnlyTags[i]) !== null) {
76478 function setTextareaHeight() {
76479 if (_tagView !== 'text') return;
76480 var selection = select(this);
76481 var matches = selection.node().value.match(/\n/g);
76482 var lineCount = 2 + Number(matches && matches.length);
76483 var lineHeight = 20;
76484 selection.style('height', lineCount * lineHeight + 'px');
76487 function stringify(s) {
76488 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
76491 function unstringify(s) {
76495 if (s.length < 1 || s.charAt(0) !== '"') {
76499 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
76503 return JSON.parse(leading + s + trailing);
76506 function rowsToText(rows) {
76507 var str = rows.filter(function (row) {
76508 return row.key && row.key.trim() !== '';
76509 }).map(function (row) {
76510 var rawVal = row.value;
76511 if (typeof rawVal !== 'string') rawVal = '*';
76512 var val = rawVal ? stringify(rawVal) : '';
76513 return stringify(row.key) + '=' + val;
76516 if (_state !== 'hover' && str.length) {
76523 function textChanged() {
76524 var newText = this.value.trim();
76526 newText.split('\n').forEach(function (row) {
76527 var m = row.match(/^\s*([^=]+)=(.*)$/);
76530 var k = context.cleanTagKey(unstringify(m[1].trim()));
76531 var v = context.cleanTagValue(unstringify(m[2].trim()));
76535 var tagDiff = utilTagDiff(_tags, newTags);
76536 if (!tagDiff.length) return;
76537 _pendingChange = _pendingChange || {};
76538 tagDiff.forEach(function (change) {
76541 })) return; // skip unchanged multiselection placeholders
76543 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
76545 if (change.type === '-') {
76546 _pendingChange[change.key] = undefined;
76547 } else if (change.type === '+') {
76548 _pendingChange[change.key] = change.newVal || '';
76552 if (Object.keys(_pendingChange).length === 0) {
76553 _pendingChange = null;
76560 function pushMore(d3_event) {
76561 // if pressing Tab on the last value field with content, add a blank row
76562 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
76567 function bindTypeahead(key, value) {
76568 if (isReadOnly(key.datum())) return;
76570 if (Array.isArray(value.datum().value)) {
76571 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
76572 var keyString = utilGetSetValue(key);
76573 if (!_tags[keyString]) return;
76575 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
76587 var geometry = context.graph().geometry(_entityIDs[0]);
76588 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
76591 geometry: geometry,
76593 }, function (err, data) {
76595 var filtered = data.filter(function (d) {
76596 return _tags[d.value] === undefined;
76598 callback(sort(value, filtered));
76602 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
76605 key: utilGetSetValue(key),
76606 geometry: geometry,
76608 }, function (err, data) {
76609 if (!err) callback(sort(value, data));
76613 function sort(value, data) {
76614 var sameletter = [];
76617 for (var i = 0; i < data.length; i++) {
76618 if (data[i].value.substring(0, value.length) === value) {
76619 sameletter.push(data[i]);
76621 other.push(data[i]);
76625 return sameletter.concat(other);
76629 function unbind() {
76630 var row = select(this);
76631 row.selectAll('input.key').call(uiCombobox.off, context);
76632 row.selectAll('input.value').call(uiCombobox.off, context);
76635 function keyChange(d3_event, d) {
76636 if (select(this).attr('readonly')) return;
76637 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
76639 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
76640 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
76649 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
76650 // new key is already in use, switch focus to the existing row
76651 this.value = kOld; // reset the key
76653 section.selection().selectAll('.tag-list input.value').each(function (d) {
76654 if (d.key === kNew) {
76655 // send focus to that other value combo instead
76656 var input = select(this).node();
76664 var row = this.parentNode.parentNode;
76665 var inputVal = select(row).selectAll('input.value');
76666 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
76667 _pendingChange = _pendingChange || {};
76670 _pendingChange[kOld] = undefined;
76673 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
76675 var existingKeyIndex = _orderedKeys.indexOf(kOld);
76677 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
76678 d.key = kNew; // update datum to avoid exit/enter on tag update
76682 utilGetSetValue(inputVal, vNew);
76686 function valueChange(d3_event, d) {
76687 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
76689 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
76691 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
76692 _pendingChange = _pendingChange || {};
76693 _pendingChange[d.key] = context.cleanTagValue(this.value);
76697 function removeTag(d3_event, d) {
76698 if (isReadOnly(d)) return;
76700 if (d.key === '') {
76701 // removing the blank row
76702 _showBlank = false;
76703 section.reRender();
76705 // remove the key from the ordered key index
76706 _orderedKeys = _orderedKeys.filter(function (key) {
76707 return key !== d.key;
76709 _pendingChange = _pendingChange || {};
76710 _pendingChange[d.key] = undefined;
76715 function addTag() {
76716 // Delay render in case this click is blurring an edited combo.
76717 // Without the setTimeout, the `content` render would wipe out the pending tag change.
76718 window.setTimeout(function () {
76720 section.reRender();
76721 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
76725 function scheduleChange() {
76726 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
76727 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
76729 window.setTimeout(function () {
76730 if (!_pendingChange) return;
76731 dispatch.call('change', this, entityIDs, _pendingChange);
76732 _pendingChange = null;
76736 section.state = function (val) {
76737 if (!arguments.length) return _state;
76739 if (_state !== val) {
76747 section.presets = function (val) {
76748 if (!arguments.length) return _presets;
76751 if (_presets && _presets.length && _presets[0].isFallback()) {
76752 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
76753 } else if (!_didInteract) {
76754 section.disclosureExpanded(null);
76760 section.tags = function (val) {
76761 if (!arguments.length) return _tags;
76766 section.entityIDs = function (val) {
76767 if (!arguments.length) return _entityIDs;
76769 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
76775 }; // pass an array of regular expressions to test against the tag key
76778 section.readOnlyTags = function (val) {
76779 if (!arguments.length) return _readOnlyTags;
76780 _readOnlyTags = val;
76784 return utilRebind(section, dispatch, 'on');
76787 function uiDataEditor(context) {
76788 var dataHeader = uiDataHeader();
76789 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
76793 function dataEditor(selection) {
76794 var header = selection.selectAll('.header').data([0]);
76795 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
76796 headerEnter.append('button').attr('class', 'close').on('click', function () {
76797 context.enter(modeBrowse(context));
76798 }).call(svgIcon('#iD-icon-close'));
76799 headerEnter.append('h3').html(_t.html('map_data.title'));
76800 var body = selection.selectAll('.body').data([0]);
76801 body = body.enter().append('div').attr('class', 'body').merge(body);
76802 var editor = body.selectAll('.data-editor').data([0]); // enter/update
76804 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
76805 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
76807 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);
76810 dataEditor.datum = function (val) {
76811 if (!arguments.length) return _datum;
76821 function search(input, dims) {
76822 if (!dims) dims = 'NSEW';
76823 if (typeof input !== 'string') return null;
76824 input = input.toUpperCase();
76825 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
76826 var m = input.match(regex);
76827 if (!m) return null; // no match
76829 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
76833 if (m[1] && m[5]) {
76834 // if matched both..
76835 dim = m[1]; // keep leading
76837 matched = matched.slice(0, -1); // remove trailing dimension from match
76839 dim = m[1] || m[5];
76840 } // if unrecognized dimension
76843 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
76845 var deg = m[2] ? parseFloat(m[2]) : 0;
76846 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
76847 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
76848 var sign = deg < 0 ? -1 : 1;
76849 if (dim === 'S' || dim === 'W') sign *= -1;
76851 val: (Math.abs(deg) + min + sec) * sign,
76854 remain: input.slice(matched.length)
76858 function pair(input, dims) {
76859 input = input.trim();
76860 var one = search(input, dims);
76861 if (!one) return null;
76862 input = one.remain.trim();
76863 var two = search(input, dims);
76864 if (!two || two.remain) return null;
76867 return swapdim(one.val, two.val, one.dim);
76869 return [one.val, two.val];
76873 function swapdim(a, b, dim) {
76874 if (dim === 'N' || dim === 'S') return [a, b];
76875 if (dim === 'W' || dim === 'E') return [b, a];
76878 function uiFeatureList(context) {
76879 var _geocodeResults;
76881 function featureList(selection) {
76882 var header = selection.append('div').attr('class', 'header fillL');
76883 header.append('h3').html(_t.html('inspector.feature_list'));
76884 var searchWrap = selection.append('div').attr('class', 'search-header');
76885 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
76886 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
76887 var listWrap = selection.append('div').attr('class', 'inspector-body');
76888 var list = listWrap.append('div').attr('class', 'feature-list');
76889 context.on('exit.feature-list', clearSearch);
76890 context.map().on('drawn.feature-list', mapDrawn);
76891 context.keybinding().on(uiCmd('⌘F'), focusSearch);
76893 function focusSearch(d3_event) {
76894 var mode = context.mode() && context.mode().id;
76895 if (mode !== 'browse') return;
76896 d3_event.preventDefault();
76897 search.node().focus();
76900 function keydown(d3_event) {
76901 if (d3_event.keyCode === 27) {
76903 search.node().blur();
76907 function keypress(d3_event) {
76908 var q = search.property('value'),
76909 items = list.selectAll('.feature-list-item');
76911 if (d3_event.keyCode === 13 && // ↩ Return
76912 q.length && items.size()) {
76913 click(d3_event, items.datum());
76917 function inputevent() {
76918 _geocodeResults = undefined;
76922 function clearSearch() {
76923 search.property('value', '');
76927 function mapDrawn(e) {
76933 function features() {
76935 var graph = context.graph();
76936 var visibleCenter = context.map().extent().center();
76937 var q = search.property('value').toLowerCase();
76938 if (!q) return result;
76939 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
76941 if (locationMatch) {
76942 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
76946 type: _t('inspector.location'),
76947 name: dmsCoordinatePair([loc[1], loc[0]]),
76950 } // A location search takes priority over an ID search
76953 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
76956 var elemType = idMatch[1].charAt(0);
76957 var elemId = idMatch[2];
76959 id: elemType + elemId,
76960 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
76961 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
76966 var allEntities = graph.entities;
76967 var localResults = [];
76969 for (var id in allEntities) {
76970 var entity = allEntities[id];
76971 if (!entity) continue;
76972 var name = utilDisplayName(entity) || '';
76973 if (name.toLowerCase().indexOf(q) < 0) continue;
76974 var matched = _mainPresetIndex.match(entity, graph);
76975 var type = matched && matched.name() || utilDisplayType(entity.id);
76976 var extent = entity.extent(graph);
76977 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
76978 localResults.push({
76981 geometry: entity.geometry(graph),
76986 if (localResults.length > 100) break;
76989 localResults = localResults.sort(function byDistance(a, b) {
76990 return a.distance - b.distance;
76992 result = result.concat(localResults);
76994 (_geocodeResults || []).forEach(function (d) {
76995 if (d.osm_type && d.osm_id) {
76996 // some results may be missing these - #1890
76997 // Make a temporary osmEntity so we can preset match
76998 // and better localize the search result - #4725
76999 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
77001 tags[d["class"]] = d.type;
77008 if (d.osm_type === 'way') {
77009 // for ways, add some fake closed nodes
77010 attrs.nodes = ['a', 'a']; // so that geometry area is possible
77013 var tempEntity = osmEntity(attrs);
77014 var tempGraph = coreGraph([tempEntity]);
77015 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
77016 var type = matched && matched.name() || utilDisplayType(id);
77019 geometry: tempEntity.geometry(tempGraph),
77021 name: d.display_name,
77022 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
77027 if (q.match(/^[0-9]+$/)) {
77028 // if query is just a number, possibly an OSM ID without a prefix
77032 type: _t('inspector.node'),
77038 type: _t('inspector.way'),
77043 geometry: 'relation',
77044 type: _t('inspector.relation'),
77052 function drawList() {
77053 var value = search.property('value');
77054 var results = features();
77055 list.classed('filtered', value.length);
77056 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'));
77057 resultsIndicator.append('span').attr('class', 'entity-name');
77058 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
77060 if (services.geocoder) {
77061 list.selectAll('.geocode-item').data([0]).enter().append('button').attr('class', 'geocode-item secondary-action').on('click', geocoderSearch).append('div').attr('class', 'label').append('span').attr('class', 'entity-name').html(_t.html('geocoder.search'));
77064 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
77065 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
77066 list.selectAll('.feature-list-item').data([-1]).remove();
77067 var items = list.selectAll('.feature-list-item').data(results, function (d) {
77070 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
77071 var label = enter.append('div').attr('class', 'label');
77072 label.each(function (d) {
77073 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
77075 label.append('span').attr('class', 'entity-type').html(function (d) {
77078 label.append('span').attr('class', 'entity-name').html(function (d) {
77081 enter.style('opacity', 0).transition().style('opacity', 1);
77083 items.exit().remove();
77086 function mouseover(d3_event, d) {
77087 if (d.id === -1) return;
77088 utilHighlightEntities([d.id], true, context);
77091 function mouseout(d3_event, d) {
77092 if (d.id === -1) return;
77093 utilHighlightEntities([d.id], false, context);
77096 function click(d3_event, d) {
77097 d3_event.preventDefault();
77100 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
77101 } else if (d.entity) {
77102 utilHighlightEntities([d.id], false, context);
77103 context.enter(modeSelect(context, [d.entity.id]));
77104 context.map().zoomToEase(d.entity);
77106 // download, zoom to, and select the entity with the given ID
77107 context.zoomToEntity(d.id);
77111 function geocoderSearch() {
77112 services.geocoder.search(search.property('value'), function (err, resp) {
77113 _geocodeResults = resp || [];
77119 return featureList;
77122 var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
77129 // eslint-disable-next-line es/no-string-prototype-startswith -- safe
77130 var $startsWith = ''.startsWith;
77131 var min$1 = Math.min;
77133 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
77134 // https://github.com/zloirock/core-js/pull/702
77135 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
77136 var descriptor = getOwnPropertyDescriptor$1(String.prototype, 'startsWith');
77137 return descriptor && !descriptor.writable;
77140 // `String.prototype.startsWith` method
77141 // https://tc39.es/ecma262/#sec-string.prototype.startswith
77142 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
77143 startsWith: function startsWith(searchString /* , position = 0 */) {
77144 var that = String(requireObjectCoercible(this));
77145 notARegexp(searchString);
77146 var index = toLength(min$1(arguments.length > 1 ? arguments[1] : undefined, that.length));
77147 var search = String(searchString);
77149 ? $startsWith.call(that, search, index)
77150 : that.slice(index, index + search.length) === search;
77154 function uiSectionEntityIssues(context) {
77155 // Does the user prefer to expand the active issue? Useful for viewing tag diff.
77156 // Expand by default so first timers see it - #6408, #8143
77157 var preference = corePreferences('entity-issues.reference.expanded');
77159 var _expanded = preference === null ? true : preference === 'true';
77161 var _entityIDs = [];
77164 var _activeIssueID;
77166 var section = uiSection('entity-issues', context).shouldDisplay(function () {
77167 return _issues.length > 0;
77168 }).label(function () {
77169 return _t('inspector.title_count', {
77170 title: _t.html('issues.list_title'),
77171 count: _issues.length
77173 }).disclosureContent(renderDisclosureContent);
77174 context.validator().on('validated.entity_issues', function () {
77175 // Refresh on validated events
77177 section.reRender();
77178 }).on('focusedIssue.entity_issues', function (issue) {
77179 makeActiveIssue(issue.id);
77182 function reloadIssues() {
77183 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
77184 includeDisabledRules: true
77188 function makeActiveIssue(issueID) {
77189 _activeIssueID = issueID;
77190 section.selection().selectAll('.issue-container').classed('active', function (d) {
77191 return d.id === _activeIssueID;
77195 function renderDisclosureContent(selection) {
77196 selection.classed('grouped-items-area', true);
77197 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
77198 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
77202 containers.exit().remove(); // Enter
77204 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
77205 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
77206 return 'issue severity-' + d.severity;
77207 }).on('mouseover.highlight', function (d3_event, d) {
77208 // don't hover-highlight the selected entity
77209 var ids = d.entityIds.filter(function (e) {
77210 return _entityIDs.indexOf(e) === -1;
77212 utilHighlightEntities(ids, true, context);
77213 }).on('mouseout.highlight', function (d3_event, d) {
77214 var ids = d.entityIds.filter(function (e) {
77215 return _entityIDs.indexOf(e) === -1;
77217 utilHighlightEntities(ids, false, context);
77219 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
77220 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
77221 makeActiveIssue(d.id); // expand only the clicked item
77223 var extent = d.extent(context.graph());
77226 var setZoom = Math.max(context.map().zoom(), 19);
77227 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
77230 textEnter.each(function (d) {
77231 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
77232 select(this).call(svgIcon(iconName, 'issue-icon'));
77234 textEnter.append('span').attr('class', 'issue-message');
77235 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
77236 infoButton.on('click', function (d3_event) {
77237 d3_event.stopPropagation();
77238 d3_event.preventDefault();
77239 this.blur(); // avoid keeping focus on the button - #4641
77241 var container = select(this.parentNode.parentNode.parentNode);
77242 var info = container.selectAll('.issue-info');
77243 var isExpanded = info.classed('expanded');
77244 _expanded = !isExpanded;
77245 corePreferences('entity-issues.reference.expanded', _expanded); // update preference
77248 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
77249 info.classed('expanded', false);
77252 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
77253 info.style('max-height', null);
77257 itemsEnter.append('ul').attr('class', 'issue-fix-list');
77258 containersEnter.append('div').attr('class', 'issue-info' + (_expanded ? ' expanded' : '')).style('max-height', _expanded ? null : '0').style('opacity', _expanded ? '1' : '0').each(function (d) {
77259 if (typeof d.reference === 'function') {
77260 select(this).call(d.reference);
77262 select(this).html(_t.html('inspector.no_documentation_key'));
77266 containers = containers.merge(containersEnter).classed('active', function (d) {
77267 return d.id === _activeIssueID;
77269 containers.selectAll('.issue-message').html(function (d) {
77270 return d.message(context);
77273 var fixLists = containers.selectAll('.issue-fix-list');
77274 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
77275 return d.fixes ? d.fixes(context) : [];
77276 }, function (fix) {
77279 fixes.exit().remove();
77280 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
77281 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
77282 // not all fixes are actionable
77283 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
77284 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
77286 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
77287 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
77289 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
77290 new Promise(function (resolve, reject) {
77291 d.onClick(context, resolve, reject);
77293 if (d.onClick.length <= 1) {
77294 // if the fix doesn't take any completion parameters then consider it resolved
77297 }).then(function () {
77298 // revalidate whenever the fix has finished running successfully
77299 context.validator().validate();
77301 }).on('mouseover.highlight', function (d3_event, d) {
77302 utilHighlightEntities(d.entityIds, true, context);
77303 }).on('mouseout.highlight', function (d3_event, d) {
77304 utilHighlightEntities(d.entityIds, false, context);
77306 buttons.each(function (d) {
77307 var iconName = d.icon || 'iD-icon-wrench';
77309 if (iconName.startsWith('maki')) {
77313 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
77315 buttons.append('span').attr('class', 'fix-message').html(function (d) {
77318 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
77320 }).attr('disabled', function (d) {
77321 return d.onClick ? null : 'true';
77322 }).attr('title', function (d) {
77323 if (d.disabledReason) {
77324 return d.disabledReason;
77331 section.entityIDs = function (val) {
77332 if (!arguments.length) return _entityIDs;
77334 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
77336 _activeIssueID = null;
77346 function uiPresetIcon() {
77351 var _sizeClass = 'medium';
77353 function isSmall() {
77354 return _sizeClass === 'small';
77357 function presetIcon(selection) {
77358 selection.each(render);
77361 function getIcon(p, geom) {
77362 if (isSmall() && p.isFallback && p.isFallback()) return 'iD-icon-' + p.id;
77363 if (p.icon) return p.icon;
77364 if (geom === 'line') return 'iD-other-line';
77365 if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex';
77366 if (isSmall() && geom === 'point') return '';
77367 return 'maki-marker-stroked';
77370 function renderPointBorder(container, drawPoint) {
77371 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
77372 pointBorder.exit().remove();
77373 var pointBorderEnter = pointBorder.enter();
77376 pointBorderEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-point-border').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(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');
77377 pointBorder = pointBorderEnter.merge(pointBorder);
77380 function renderCategoryBorder(container, category) {
77381 var categoryBorder = container.selectAll('.preset-icon-category-border').data(category ? [0] : []);
77382 categoryBorder.exit().remove();
77383 var categoryBorderEnter = categoryBorder.enter();
77385 var svgEnter = categoryBorderEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-category-border').attr('width', d).attr('height', d).attr('viewBox', "0 0 ".concat(d, " ").concat(d));
77386 ['fill', 'stroke'].forEach(function (klass) {
77387 svgEnter.append('path').attr('class', "area ".concat(klass)).attr('d', 'M9.5,7.5 L25.5,7.5 L28.5,12.5 L49.5,12.5 C51.709139,12.5 53.5,14.290861 53.5,16.5 L53.5,43.5 C53.5,45.709139 51.709139,47.5 49.5,47.5 L10.5,47.5 C8.290861,47.5 6.5,45.709139 6.5,43.5 L6.5,12.5 L9.5,7.5 Z');
77389 categoryBorder = categoryBorderEnter.merge(categoryBorder);
77392 var tagClasses = svgTagClasses().getClassesString(category.members.collection[0].addTags, '');
77393 categoryBorder.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
77394 categoryBorder.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
77398 function renderCircleFill(container, drawVertex) {
77399 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
77400 vertexFill.exit().remove();
77401 var vertexFillEnter = vertexFill.enter();
77405 vertexFillEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-fill-vertex').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h)).append('circle').attr('cx', w / 2).attr('cy', h / 2).attr('r', d / 2);
77406 vertexFill = vertexFillEnter.merge(vertexFill);
77409 function renderSquareFill(container, drawArea, tagClasses) {
77410 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
77411 fill.exit().remove();
77412 var fillEnter = fill.enter();
77413 var d = isSmall() ? 40 : 60;
77417 var c1 = (w - l) / 2;
77419 fillEnter = fillEnter.append('svg').attr('class', 'preset-icon-fill preset-icon-fill-area').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77420 ['fill', 'stroke'].forEach(function (klass) {
77421 fillEnter.append('path').attr('d', "M".concat(c1, " ").concat(c1, " L").concat(c1, " ").concat(c2, " L").concat(c2, " ").concat(c2, " L").concat(c2, " ").concat(c1, " Z")).attr('class', "area ".concat(klass));
77424 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
77425 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
77429 var rMidpoint = 1.25;
77430 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
77431 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
77435 fill = fillEnter.merge(fill);
77436 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
77437 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
77440 function renderLine(container, drawLine, tagClasses) {
77441 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
77442 line.exit().remove();
77443 var lineEnter = line.enter();
77444 var d = isSmall() ? 40 : 60; // draw the line parametrically
77448 var y = Math.round(d * 0.72);
77449 var l = Math.round(d * 0.6);
77451 var x1 = (w - l) / 2;
77453 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77454 ['casing', 'stroke'].forEach(function (klass) {
77455 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
77457 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
77458 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
77460 line = lineEnter.merge(line);
77461 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
77462 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
77465 function renderRoute(container, drawRoute, p) {
77466 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
77467 route.exit().remove();
77468 var routeEnter = route.enter();
77469 var d = isSmall() ? 40 : 60; // draw the route parametrically
77473 var y1 = Math.round(d * 0.80);
77474 var y2 = Math.round(d * 0.68);
77475 var l = Math.round(d * 0.6);
77477 var x1 = (w - l) / 2;
77478 var x2 = x1 + l / 3;
77479 var x3 = x2 + l / 3;
77480 var x4 = x3 + l / 3;
77481 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
77482 ['casing', 'stroke'].forEach(function (klass) {
77483 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
77484 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
77485 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
77487 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
77488 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
77490 route = routeEnter.merge(route);
77493 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
77494 var segmentPresetIDs = routeSegments[routeType];
77496 for (var i in segmentPresetIDs) {
77497 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
77498 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
77499 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
77500 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
77505 function renderSvgIcon(container, picon, geom, isFramed, category, tagClasses) {
77506 var isMaki = picon && /^maki-/.test(picon);
77507 var isTemaki = picon && /^temaki-/.test(picon);
77508 var isFa = picon && /^fa[srb]-/.test(picon);
77509 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
77510 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
77511 icon.exit().remove();
77512 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
77513 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('category', category).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
77514 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
77518 suffix = isSmall() && geom === 'point' ? '-11' : '-15';
77521 icon.selectAll('use').attr('href', '#' + picon + suffix);
77524 function renderImageIcon(container, imageURL) {
77525 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
77526 imageIcon.exit().remove();
77527 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
77528 return container.classed('showing-img', true);
77529 }).on('error', function () {
77530 return container.classed('showing-img', false);
77531 }).merge(imageIcon);
77532 imageIcon.attr('src', imageURL);
77533 } // Route icons are drawn with a zigzag annotation underneath:
77537 // This dataset defines the styles that are used to draw the zigzag segments.
77540 var routeSegments = {
77541 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
77542 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
77543 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
77544 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
77545 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
77546 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
77547 hiking: ['highway/path', 'highway/path', 'highway/path'],
77548 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
77549 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
77550 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
77551 mtb: ['highway/path', 'highway/track', 'highway/bridleway'],
77552 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
77553 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
77554 power: ['power/line', 'power/line', 'power/line'],
77555 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
77556 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
77557 train: ['railway/rail', 'railway/rail', 'railway/rail'],
77558 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
77559 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
77562 function render() {
77563 var p = _preset.apply(this, arguments);
77565 var geom = _geometry ? _geometry.apply(this, arguments) : null;
77567 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
77571 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
77572 var isFallback = isSmall() && p.isFallback && p.isFallback();
77573 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
77574 var picon = getIcon(p, geom);
77575 var isCategory = !p.setTags;
77576 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
77577 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
77578 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
77579 var drawArea = picon && geom === 'area' && !isFallback && !isCategory;
77580 var drawRoute = picon && geom === 'route';
77581 var isFramed = drawVertex || drawArea || drawLine || drawRoute || isCategory;
77582 var tags = !isCategory ? p.setTags({}, geom) : {};
77584 for (var k in tags) {
77585 if (tags[k] === '*') {
77590 var tagClasses = svgTagClasses().getClassesString(tags, '');
77591 var selection = select(this);
77592 var container = selection.selectAll('.preset-icon-container').data([0]);
77593 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
77594 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
77595 renderCategoryBorder(container, isCategory && p);
77596 renderPointBorder(container, drawPoint);
77597 renderCircleFill(container, drawVertex);
77598 renderSquareFill(container, drawArea, tagClasses);
77599 renderLine(container, drawLine, tagClasses);
77600 renderRoute(container, drawRoute, p);
77601 renderSvgIcon(container, picon, geom, isFramed, isCategory, tagClasses);
77602 renderImageIcon(container, imageURL);
77605 presetIcon.preset = function (val) {
77606 if (!arguments.length) return _preset;
77607 _preset = utilFunctor(val);
77611 presetIcon.geometry = function (val) {
77612 if (!arguments.length) return _geometry;
77613 _geometry = utilFunctor(val);
77617 presetIcon.sizeClass = function (val) {
77618 if (!arguments.length) return _sizeClass;
77626 function uiSectionFeatureType(context) {
77627 var dispatch = dispatch$8('choose');
77628 var _entityIDs = [];
77633 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
77635 function renderDisclosureContent(selection) {
77636 selection.classed('preset-list-item', true);
77637 selection.classed('mixed-types', _presets.length > 1);
77638 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
77639 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
77640 presetButton.append('div').attr('class', 'preset-icon-container');
77641 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
77642 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
77643 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
77644 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
77646 if (_tagReference) {
77647 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
77648 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
77651 selection.selectAll('.preset-reset').on('click', function () {
77652 dispatch.call('choose', this, _presets);
77653 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
77654 d3_event.preventDefault();
77655 d3_event.stopPropagation();
77657 var geometries = entityGeometries();
77658 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')));
77659 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
77660 var label = selection.select('.label-inner');
77661 var nameparts = label.selectAll('.namepart').data(names, function (d) {
77664 nameparts.exit().remove();
77665 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
77670 section.entityIDs = function (val) {
77671 if (!arguments.length) return _entityIDs;
77676 section.presets = function (val) {
77677 if (!arguments.length) return _presets; // don't reload the same preset
77679 if (!utilArrayIdentical(val, _presets)) {
77682 if (_presets.length === 1) {
77683 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
77690 function entityGeometries() {
77693 for (var i in _entityIDs) {
77694 var geometry = context.graph().geometry(_entityIDs[i]);
77695 if (!counts[geometry]) counts[geometry] = 0;
77696 counts[geometry] += 1;
77699 return Object.keys(counts).sort(function (geom1, geom2) {
77700 return counts[geom2] - counts[geom1];
77704 return utilRebind(section, dispatch, 'on');
77707 // It borrows some code from uiHelp
77709 function uiFieldHelp(context, fieldName) {
77710 var fieldHelp = {};
77712 var _inspector = select(null);
77714 var _wrap = select(null);
77716 var _body = select(null);
77718 var fieldHelpKeys = {
77719 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']]]
77721 var fieldHelpHeadings = {};
77722 var replacements = {
77723 distField: _t.html('restriction.controls.distance'),
77724 viaField: _t.html('restriction.controls.via'),
77725 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
77726 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
77727 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
77728 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
77729 allowTurn: icon('#iD-turn-yes', 'inline turn'),
77730 restrictTurn: icon('#iD-turn-no', 'inline turn'),
77731 onlyTurn: icon('#iD-turn-only', 'inline turn')
77732 }; // For each section, squash all the texts into a single markdown document
77734 var docs = fieldHelpKeys[fieldName].map(function (key) {
77735 var helpkey = 'help.field.' + fieldName + '.' + key[0];
77736 var text = key[1].reduce(function (all, part) {
77737 var subkey = helpkey + '.' + part;
77738 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
77740 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
77742 return all + hhh + _t.html(subkey, replacements) + '\n\n';
77746 title: _t.html(helpkey + '.title'),
77747 html: marked_1(text.trim())
77754 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
77758 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
77759 _body.classed('hide', true);
77763 function clickHelp(index) {
77764 var d = docs[index];
77765 var tkeys = fieldHelpKeys[fieldName][index][1];
77767 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
77768 return i === index;
77771 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
77774 content.selectAll('p').attr('class', function (d, i) {
77776 }); // insert special content for certain help sections
77778 if (d.key === 'help.field.restrictions.inspecting') {
77779 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
77780 } else if (d.key === 'help.field.restrictions.modifying') {
77781 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
77785 fieldHelp.button = function (selection) {
77786 if (_body.empty()) return;
77787 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
77789 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
77790 d3_event.stopPropagation();
77791 d3_event.preventDefault();
77793 if (_body.classed('hide')) {
77801 function updatePosition() {
77802 var wrap = _wrap.node();
77804 var inspector = _inspector.node();
77806 var wRect = wrap.getBoundingClientRect();
77807 var iRect = inspector.getBoundingClientRect();
77809 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
77812 fieldHelp.body = function (selection) {
77813 // This control expects the field to have a form-field-input-wrap div
77814 _wrap = selection.selectAll('.form-field-input-wrap');
77815 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
77817 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
77818 if (_inspector.empty()) return;
77819 _body = _inspector.selectAll('.field-help-body').data([0]);
77821 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
77824 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
77825 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
77826 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
77827 d3_event.stopPropagation();
77828 d3_event.preventDefault();
77830 }).call(svgIcon('#iD-icon-close'));
77831 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
77832 var titles = docs.map(function (d) {
77835 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
77837 }).on('click', function (d3_event, d) {
77838 d3_event.stopPropagation();
77839 d3_event.preventDefault();
77840 clickHelp(titles.indexOf(d));
77842 enter.append('div').attr('class', 'field-help-content');
77843 _body = _body.merge(enter);
77850 function uiFieldCheck(field, context) {
77851 var dispatch = dispatch$8('change');
77852 var options = field.options;
77858 var input = select(null);
77859 var text = select(null);
77860 var label = select(null);
77861 var reverser = select(null);
77865 var _entityIDs = [];
77870 for (var i in options) {
77871 var v = options[i];
77872 values.push(v === 'undefined' ? undefined : v);
77873 texts.push(field.t.html('options.' + v, {
77878 values = [undefined, 'yes'];
77879 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
77881 if (field.type !== 'defaultCheck') {
77883 texts.push(_t.html('inspector.check.no'));
77885 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
77888 function checkImpliedYes() {
77889 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
77890 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
77892 if (field.id === 'oneway') {
77893 var entity = context.entity(_entityIDs[0]);
77895 for (var key in entity.tags) {
77896 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
77897 _impliedYes = true;
77898 texts[0] = _t.html('_tagging.presets.fields.oneway_yes.options.undefined');
77905 function reverserHidden() {
77906 if (!context.container().select('div.inspector-hover').empty()) return true;
77907 return !(_value === 'yes' || _impliedYes && !_value);
77910 function reverserSetText(selection) {
77911 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
77912 if (reverserHidden() || !entity) return selection;
77913 var first = entity.first();
77914 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
77915 var pseudoDirection = first < last;
77916 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
77917 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
77921 var check = function check(selection) {
77923 label = selection.selectAll('.form-field-input-wrap').data([0]);
77924 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
77925 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
77926 enter.append('span').html(texts[0]).attr('class', 'value');
77928 if (field.type === 'onewayCheck') {
77929 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
77932 label = label.merge(enter);
77933 input = label.selectAll('input');
77934 text = label.selectAll('span.value');
77935 input.on('click', function (d3_event) {
77936 d3_event.stopPropagation();
77939 if (Array.isArray(_tags[field.key])) {
77940 if (values.indexOf('yes') !== -1) {
77941 t[field.key] = 'yes';
77943 t[field.key] = values[0];
77946 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
77947 } // Don't cycle through `alternating` or `reversible` states - #4970
77948 // (They are supported as translated strings, but should not toggle with clicks)
77951 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
77952 t[field.key] = values[0];
77955 dispatch.call('change', this, t);
77958 if (field.type === 'onewayCheck') {
77959 reverser = label.selectAll('.reverser');
77960 reverser.call(reverserSetText).on('click', function (d3_event) {
77961 d3_event.preventDefault();
77962 d3_event.stopPropagation();
77963 context.perform(function (graph) {
77964 for (var i in _entityIDs) {
77965 graph = actionReverse(_entityIDs[i])(graph);
77969 }, _t('operations.reverse.annotation.line', {
77971 })); // must manually revalidate since no 'change' event was called
77973 context.validator().validate();
77974 select(this).call(reverserSetText);
77979 check.entityIDs = function (val) {
77980 if (!arguments.length) return _entityIDs;
77985 check.tags = function (tags) {
77988 function isChecked(val) {
77989 return val !== 'no' && val !== '' && val !== undefined && val !== null;
77992 function textFor(val) {
77993 if (val === '') val = undefined;
77994 var index = values.indexOf(val);
77995 return index !== -1 ? texts[index] : '"' + val + '"';
77999 var isMixed = Array.isArray(tags[field.key]);
78000 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
78002 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
78006 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
78007 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
78008 label.classed('set', !!_value);
78010 if (field.type === 'onewayCheck') {
78011 reverser.classed('hide', reverserHidden()).call(reverserSetText);
78015 check.focus = function () {
78016 input.node().focus();
78019 return utilRebind(check, dispatch, 'on');
78022 function uiFieldCombo(field, context) {
78023 var dispatch = dispatch$8('change');
78025 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
78027 var _isNetwork = field.type === 'networkCombo';
78029 var _isSemi = field.type === 'semiCombo';
78031 var _optarray = field.options;
78033 var _showTagInfoSuggestions = field.type !== 'manyCombo' && field.autoSuggestions !== false;
78035 var _allowCustomValues = field.type !== 'manyCombo' && field.customValues !== false;
78037 var _snake_case = field.snake_case || field.snake_case === undefined;
78039 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
78041 var _container = select(null);
78043 var _inputWrap = select(null);
78045 var _input = select(null);
78047 var _comboData = [];
78048 var _multiData = [];
78049 var _entityIDs = [];
78055 var _staticPlaceholder; // initialize deprecated tags array
78058 var _dataDeprecated = [];
78059 _mainFileFetcher.get('deprecated').then(function (d) {
78060 _dataDeprecated = d;
78061 })["catch"](function () {
78063 }); // ensure multiCombo field.key ends with a ':'
78065 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
78069 function snake(s) {
78070 return s.replace(/\s+/g, '_').toLowerCase();
78073 function clean(s) {
78074 return s.split(';').map(function (s) {
78077 } // returns the tag value for a display value
78078 // (for multiCombo, dval should be the key suffix, not the entire key)
78081 function tagValue(dval) {
78082 dval = clean(dval || '');
78084 var found = _comboData.find(function (o) {
78085 return o.key && clean(o.value) === dval;
78088 if (found) return found.key;
78090 if (field.type === 'typeCombo' && !dval) {
78094 return (_snake_case ? snake(dval) : dval) || undefined;
78095 } // returns the display value for a tag value
78096 // (for multiCombo, tval should be the key suffix, not the entire key)
78099 function displayValue(tval) {
78102 if (field.hasTextForStringId('options.' + tval)) {
78103 return field.t('options.' + tval, {
78108 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
78113 } // Compute the difference between arrays of objects by `value` property
78115 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
78116 // > [{value:1}, {value:3}]
78120 function objectDifference(a, b) {
78121 return a.filter(function (d1) {
78122 return !b.some(function (d2) {
78123 return !d2.isMixed && d1.value === d2.value;
78128 function initCombo(selection, attachTo) {
78129 if (!_allowCustomValues) {
78130 selection.attr('readonly', 'readonly');
78133 if (_showTagInfoSuggestions && services.taginfo) {
78134 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
78135 setTaginfoValues('', setPlaceholder);
78137 selection.call(_combobox, attachTo);
78138 setStaticValues(setPlaceholder);
78142 function setStaticValues(callback) {
78143 if (!_optarray) return;
78144 _comboData = _optarray.map(function (v) {
78147 value: field.t('options.' + v, {
78151 display: field.t.html('options.' + v, {
78154 klass: field.hasTextForStringId('options.' + v) ? '' : 'raw-option'
78158 _combobox.data(objectDifference(_comboData, _multiData));
78160 if (callback) callback(_comboData);
78163 function setTaginfoValues(q, callback) {
78164 var fn = _isMulti ? 'multikeys' : 'values';
78165 var query = (_isMulti ? field.key : '') + q;
78166 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
78168 if (hasCountryPrefix) {
78169 query = _countryCode + ':';
78173 debounce: q !== '',
78178 if (_entityIDs.length) {
78179 params.geometry = context.graph().geometry(_entityIDs[0]);
78182 services.taginfo[fn](params, function (err, data) {
78184 data = data.filter(function (d) {
78185 if (field.type === 'typeCombo' && d.value === 'yes') {
78186 // don't show the fallback value
78188 } // don't show values with very low usage
78191 return !d.count || d.count > 10;
78193 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
78195 if (deprecatedValues) {
78196 // don't suggest deprecated tag values
78197 data = data.filter(function (d) {
78198 return deprecatedValues.indexOf(d.value) === -1;
78202 if (hasCountryPrefix) {
78203 data = data.filter(function (d) {
78204 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
78206 } // hide the caret if there are no suggestions
78209 _container.classed('empty-combobox', data.length === 0);
78211 _comboData = data.map(function (d) {
78213 if (_isMulti) k = k.replace(field.key, '');
78214 var label = field.t('options.' + k, {
78220 display: field.t.html('options.' + k, {
78223 title: d.title || label,
78224 klass: field.hasTextForStringId('options.' + k) ? '' : 'raw-option'
78227 _comboData = objectDifference(_comboData, _multiData);
78228 if (callback) callback(_comboData);
78232 function setPlaceholder(values) {
78233 if (_isMulti || _isSemi) {
78234 _staticPlaceholder = field.placeholder() || _t('inspector.add');
78236 var vals = values.map(function (d) {
78238 }).filter(function (s) {
78239 return s.length < 20;
78241 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
78244 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
78247 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
78248 _staticPlaceholder += '…';
78253 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
78254 ph = _t('inspector.multiple_values');
78256 ph = _staticPlaceholder;
78259 _container.selectAll('input').attr('placeholder', ph);
78262 function change() {
78266 if (_isMulti || _isSemi) {
78267 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
78269 _container.classed('active', false);
78271 utilGetSetValue(_input, '');
78272 var vals = val.split(';').filter(Boolean);
78273 if (!vals.length) return;
78276 utilArrayUniq(vals).forEach(function (v) {
78277 var key = (field.key || '') + v;
78280 // don't set a multicombo value to 'yes' if it already has a non-'no' value
78281 // e.g. `language:de=main`
78282 var old = _tags[key];
78283 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
78286 key = context.cleanTagKey(key);
78287 field.keys.push(key);
78290 } else if (_isSemi) {
78291 var arr = _multiData.map(function (d) {
78295 arr = arr.concat(vals);
78296 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
78299 window.setTimeout(function () {
78300 _input.node().focus();
78303 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
78305 if (!rawValue && Array.isArray(_tags[field.key])) return;
78306 val = context.cleanTagValue(tagValue(rawValue));
78307 t[field.key] = val || undefined;
78310 dispatch.call('change', this, t);
78313 function removeMultikey(d3_event, d) {
78314 d3_event.preventDefault();
78315 d3_event.stopPropagation();
78319 t[d.key] = undefined;
78320 } else if (_isSemi) {
78321 var arr = _multiData.map(function (md) {
78322 return md.key === d.key ? null : md.key;
78323 }).filter(Boolean);
78325 arr = utilArrayUniq(arr);
78326 t[field.key] = arr.length ? arr.join(';') : undefined;
78329 dispatch.call('change', this, t);
78332 function combo(selection) {
78333 _container = selection.selectAll('.form-field-input-wrap').data([0]);
78334 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
78335 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
78337 if (_isMulti || _isSemi) {
78338 _container = _container.selectAll('.chiplist').data([0]);
78339 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations and Via fields
78340 // to mimic highway exit signs
78342 if (field.key === 'destination' || field.key === 'via') {
78343 listClass += ' full-line-chips';
78346 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
78347 window.setTimeout(function () {
78348 _input.node().focus();
78350 }).merge(_container);
78351 _inputWrap = _container.selectAll('.input-wrap').data([0]);
78352 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
78353 _input = _inputWrap.selectAll('input').data([0]);
78355 _input = _container.selectAll('input').data([0]);
78358 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
78361 var extent = combinedEntityExtent();
78362 var countryCode = extent && iso1A2Code(extent.center());
78363 _countryCode = countryCode && countryCode.toLowerCase();
78366 _input.on('change', change).on('blur', change);
78368 _input.on('keydown.field', function (d3_event) {
78369 switch (d3_event.keyCode) {
78372 _input.node().blur(); // blurring also enters the value
78375 d3_event.stopPropagation();
78380 if (_isMulti || _isSemi) {
78381 _combobox.on('accept', function () {
78382 _input.node().blur();
78384 _input.node().focus();
78387 _input.on('focus', function () {
78388 _container.classed('active', true);
78393 combo.tags = function (tags) {
78396 if (_isMulti || _isSemi) {
78401 // Build _multiData array containing keys already set..
78402 for (var k in tags) {
78403 if (field.key && k.indexOf(field.key) !== 0) continue;
78404 if (!field.key && field.keys.indexOf(k) === -1) continue;
78406 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
78407 var suffix = field.key ? k.substr(field.key.length) : k;
78411 value: displayValue(suffix),
78412 isMixed: Array.isArray(v)
78417 // Set keys for form-field modified (needed for undo and reset buttons)..
78418 field.keys = _multiData.map(function (d) {
78420 }); // limit the input length so it fits after prepending the key prefix
78422 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
78424 maxLength = context.maxCharsForTagKey();
78426 } else if (_isSemi) {
78427 var allValues = [];
78430 if (Array.isArray(tags[field.key])) {
78431 tags[field.key].forEach(function (tagVal) {
78432 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
78433 allValues = allValues.concat(thisVals);
78435 if (!commonValues) {
78436 commonValues = thisVals;
78438 commonValues = commonValues.filter(function (value) {
78439 return thisVals.includes(value);
78443 allValues = utilArrayUniq(allValues).filter(Boolean);
78445 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
78446 commonValues = allValues;
78449 _multiData = allValues.map(function (v) {
78452 value: displayValue(v),
78453 isMixed: !commonValues.includes(v)
78456 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
78458 maxLength = context.maxCharsForTagValue() - currLength;
78460 if (currLength > 0) {
78461 // account for the separator if a new value will be appended to existing
78464 } // a negative maxlength doesn't make sense
78467 maxLength = Math.max(0, maxLength);
78468 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
78469 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
78471 var available = objectDifference(_comboData, _multiData);
78473 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
78474 // options and they're all currently used,
78475 // or if the field is already at its character limit
78478 var hideAdd = !_allowCustomValues && !available.length || maxLength <= 0;
78480 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
78483 var chips = _container.selectAll('.chip').data(_multiData);
78485 chips.exit().remove();
78486 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
78487 enter.append('span');
78489 chips = chips.merge(enter).order().classed('raw-value', function (d) {
78491 if (_isMulti) k = k.replace(field.key, '');
78492 return !field.hasTextForStringId('options.' + k);
78493 }).classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
78495 }).attr('title', function (d) {
78496 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
78499 if (allowDragAndDrop) {
78500 registerDragAndDrop(chips);
78503 chips.select('span').html(function (d) {
78506 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
78508 var isMixed = Array.isArray(tags[field.key]);
78509 var mixedValues = isMixed && tags[field.key].map(function (val) {
78510 return displayValue(val);
78511 }).filter(Boolean);
78512 var showsValue = !isMixed && tags[field.key] && !(field.type === 'typeCombo' && tags[field.key] === 'yes');
78513 var isRawValue = showsValue && !field.hasTextForStringId('options.' + tags[field.key]);
78514 var isKnownValue = showsValue && !isRawValue;
78515 var isReadOnly = !_allowCustomValues || isKnownValue;
78516 utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '').classed('raw-value', isRawValue).classed('known-value', isKnownValue).attr('readonly', isReadOnly ? 'readonly' : undefined).attr('title', isMixed ? mixedValues.join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '').classed('mixed', isMixed).on('keydown.deleteCapture', function (d3_event) {
78517 if (isReadOnly && isKnownValue && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
78518 d3_event.preventDefault();
78519 d3_event.stopPropagation();
78521 t[field.key] = undefined;
78522 dispatch.call('change', this, t);
78528 function registerDragAndDrop(selection) {
78529 // allow drag and drop re-ordering of chips
78530 var dragOrigin, targetIndex;
78531 selection.call(d3_drag().on('start', function (d3_event) {
78536 targetIndex = null;
78537 }).on('drag', function (d3_event) {
78538 var x = d3_event.x - dragOrigin.x,
78539 y = d3_event.y - dragOrigin.y;
78540 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
78541 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
78542 var index = selection.nodes().indexOf(this);
78543 select(this).classed('dragging', true);
78544 targetIndex = null;
78545 var targetIndexOffsetTop = null;
78546 var draggedTagWidth = select(this).node().offsetWidth;
78548 if (field.key === 'destination' || field.key === 'via') {
78549 // meaning tags are full width
78550 _container.selectAll('.chip').style('transform', function (d2, index2) {
78551 var node = select(this).node();
78553 if (index === index2) {
78554 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
78555 } else if (index2 > index && d3_event.y > node.offsetTop) {
78556 if (targetIndex === null || index2 > targetIndex) {
78557 targetIndex = index2;
78560 return 'translateY(-100%)'; // move the dragged tag down the order
78561 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
78562 if (targetIndex === null || index2 < targetIndex) {
78563 targetIndex = index2;
78566 return 'translateY(100%)';
78572 _container.selectAll('.chip').each(function (d2, index2) {
78573 var node = select(this).node(); // check the cursor is in the bounding box
78575 if (index !== index2 && d3_event.x < node.offsetLeft + node.offsetWidth + 5 && d3_event.x > node.offsetLeft && d3_event.y < node.offsetTop + node.offsetHeight && d3_event.y > node.offsetTop) {
78576 targetIndex = index2;
78577 targetIndexOffsetTop = node.offsetTop;
78579 }).style('transform', function (d2, index2) {
78580 var node = select(this).node();
78582 if (index === index2) {
78583 return 'translate(' + x + 'px, ' + y + 'px)';
78584 } // only translate tags in the same row
78587 if (node.offsetTop === targetIndexOffsetTop) {
78588 if (index2 < index && index2 >= targetIndex) {
78589 return 'translateX(' + draggedTagWidth + 'px)';
78590 } else if (index2 > index && index2 <= targetIndex) {
78591 return 'translateX(-' + draggedTagWidth + 'px)';
78598 }).on('end', function () {
78599 if (!select(this).classed('dragging')) {
78603 var index = selection.nodes().indexOf(this);
78604 select(this).classed('dragging', false);
78606 _container.selectAll('.chip').style('transform', null);
78608 if (typeof targetIndex === 'number') {
78609 var element = _multiData[index];
78611 _multiData.splice(index, 1);
78613 _multiData.splice(targetIndex, 0, element);
78617 if (_multiData.length) {
78618 t[field.key] = _multiData.map(function (element) {
78619 return element.key;
78622 t[field.key] = undefined;
78625 dispatch.call('change', this, t);
78628 dragOrigin = undefined;
78629 targetIndex = undefined;
78633 combo.focus = function () {
78634 _input.node().focus();
78637 combo.entityIDs = function (val) {
78638 if (!arguments.length) return _entityIDs;
78643 function combinedEntityExtent() {
78644 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
78647 return utilRebind(combo, dispatch, 'on');
78650 function uiFieldText(field, context) {
78651 var dispatch = dispatch$8('change');
78652 var input = select(null);
78653 var outlinkButton = select(null);
78654 var _entityIDs = [];
78658 var _phoneFormats = {};
78660 if (field.type === 'tel') {
78661 _mainFileFetcher.get('phone_formats').then(function (d) {
78663 updatePhonePlaceholder();
78664 })["catch"](function () {
78669 function calcLocked() {
78670 // Protect certain fields that have a companion `*:wikidata` value
78671 var isLocked = (field.id === 'brand' || field.id === 'network' || field.id === 'operator' || field.id === 'flag') && _entityIDs.length && _entityIDs.some(function (entityID) {
78672 var entity = context.graph().hasEntity(entityID);
78673 if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
78675 if (entity.tags.wikidata) return true;
78676 var preset = _mainPresetIndex.match(entity, context.graph());
78677 var isSuggestion = preset && preset.suggestion; // Lock the field if there is a value and a companion `*:wikidata` value
78679 var which = field.id; // 'brand', 'network', 'operator', 'flag'
78681 return isSuggestion && !!entity.tags[which] && !!entity.tags[which + ':wikidata'];
78684 field.locked(isLocked);
78687 function i(selection) {
78689 var isLocked = field.locked();
78690 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
78691 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
78692 input = wrap.selectAll('input').data([0]);
78693 input = input.enter().append('input').attr('type', field.type === 'identifier' || field.type === 'roadheight' ? 'text' : field.type).attr('id', field.domId).classed(field.type, true).call(utilNoAuto).merge(input);
78694 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
78696 if (field.type === 'tel') {
78697 updatePhonePlaceholder();
78698 } else if (field.type === 'number') {
78699 var rtl = _mainLocalizer.textDirection() === 'rtl';
78700 input.attr('type', 'text');
78701 var inc = field.increment;
78702 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
78703 buttons.enter().append('button').attr('class', function (d) {
78704 var which = d > 0 ? 'increment' : 'decrement';
78705 return 'form-field-button ' + which;
78706 }).merge(buttons).on('click', function (d3_event, d) {
78707 d3_event.preventDefault();
78708 var raw_vals = input.node().value || '0';
78709 var vals = raw_vals.split(';');
78710 vals = vals.map(function (v) {
78711 var num = parseFloat(v.trim(), 10);
78712 return isFinite(num) ? clamped(num + d) : v.trim();
78714 input.node().value = vals.join(';');
78717 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
78718 input.attr('type', 'text');
78719 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
78720 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
78721 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
78723 if (domainResults.length >= 2 && domainResults[1]) {
78724 var domain = domainResults[1];
78725 return _t('icons.view_on', {
78731 }).on('click', function (d3_event) {
78732 d3_event.preventDefault();
78733 var value = validIdentifierValueForLink();
78736 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
78737 window.open(url, '_blank');
78739 }).merge(outlinkButton);
78743 function updatePhonePlaceholder() {
78744 if (input.empty() || !Object.keys(_phoneFormats).length) return;
78745 var extent = combinedEntityExtent();
78746 var countryCode = extent && iso1A2Code(extent.center());
78748 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
78750 if (format) input.attr('placeholder', format);
78753 function validIdentifierValueForLink() {
78754 if (field.type === 'identifier' && field.pattern) {
78755 var value = utilGetSetValue(input).trim().split(';')[0];
78756 return value && value.match(new RegExp(field.pattern));
78760 } // clamp number to min/max
78763 function clamped(num) {
78764 if (field.minValue !== undefined) {
78765 num = Math.max(num, field.minValue);
78768 if (field.maxValue !== undefined) {
78769 num = Math.min(num, field.maxValue);
78775 function change(onInput) {
78776 return function () {
78778 var val = utilGetSetValue(input);
78779 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
78781 if (!val && Array.isArray(_tags[field.key])) return;
78784 if (field.type === 'number' && val) {
78785 var vals = val.split(';');
78786 vals = vals.map(function (v) {
78787 var num = parseFloat(v.trim(), 10);
78788 return isFinite(num) ? clamped(num) : v.trim();
78790 val = vals.join(';');
78793 utilGetSetValue(input, val);
78796 t[field.key] = val || undefined;
78797 dispatch.call('change', this, t, onInput);
78801 i.entityIDs = function (val) {
78802 if (!arguments.length) return _entityIDs;
78807 i.tags = function (tags) {
78809 var isMixed = Array.isArray(tags[field.key]);
78810 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);
78812 if (outlinkButton && !outlinkButton.empty()) {
78813 var disabled = !validIdentifierValueForLink();
78814 outlinkButton.classed('disabled', disabled);
78818 i.focus = function () {
78819 var node = input.node();
78820 if (node) node.focus();
78823 function combinedEntityExtent() {
78824 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
78827 return utilRebind(i, dispatch, 'on');
78830 function uiFieldAccess(field, context) {
78831 var dispatch = dispatch$8('change');
78832 var items = select(null);
78836 function access(selection) {
78837 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
78838 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
78839 var list = wrap.selectAll('ul').data([0]);
78840 list = list.enter().append('ul').attr('class', 'rows').merge(list);
78841 items = list.selectAll('li').data(field.keys); // Enter
78843 var enter = items.enter().append('li').attr('class', function (d) {
78844 return 'labeled-input preset-access-' + d;
78846 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
78847 return 'preset-input-access-' + d;
78848 }).html(function (d) {
78849 return field.t.html('types.' + d);
78851 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
78852 return 'preset-input-access preset-input-access-' + d;
78853 }).call(utilNoAuto).each(function (d) {
78854 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
78857 items = items.merge(enter);
78858 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
78861 function change(d3_event, d) {
78863 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
78865 if (!value && typeof _tags[d] !== 'string') return;
78866 tag[d] = value || undefined;
78867 dispatch.call('change', this, tag);
78870 access.options = function (type) {
78871 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
78873 if (type !== 'access') {
78874 options.unshift('yes');
78875 options.push('designated');
78877 if (type === 'bicycle') {
78878 options.push('dismount');
78882 return options.map(function (option) {
78884 title: field.t('options.' + option + '.description'),
78890 var placeholdersByHighway = {
78892 foot: 'designated',
78893 motor_vehicle: 'no'
78897 motor_vehicle: 'no',
78903 motor_vehicle: 'no'
78906 motor_vehicle: 'no',
78907 bicycle: 'designated'
78910 motor_vehicle: 'no',
78911 horse: 'designated'
78915 motor_vehicle: 'no',
78921 motor_vehicle: 'yes',
78926 motor_vehicle: 'yes'
78930 motor_vehicle: 'yes',
78936 motor_vehicle: 'yes',
78942 motor_vehicle: 'yes',
78948 motor_vehicle: 'yes',
78954 motor_vehicle: 'yes',
78960 motor_vehicle: 'yes',
78966 motor_vehicle: 'yes',
78971 motor_vehicle: 'yes'
78975 motor_vehicle: 'yes',
78981 motor_vehicle: 'yes',
78987 motor_vehicle: 'yes',
78993 access.tags = function (tags) {
78995 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
78996 return typeof tags[d] === 'string' ? tags[d] : '';
78997 }).classed('mixed', function (d) {
78998 return tags[d] && Array.isArray(tags[d]);
78999 }).attr('title', function (d) {
79000 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
79001 }).attr('placeholder', function (d) {
79002 if (tags[d] && Array.isArray(tags[d])) {
79003 return _t('inspector.multiple_values');
79006 if (d === 'access') {
79010 if (tags.access && typeof tags.access === 'string') {
79011 return tags.access;
79014 if (tags.highway) {
79015 if (typeof tags.highway === 'string') {
79016 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
79017 return placeholdersByHighway[tags.highway][d];
79020 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
79021 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
79022 }).filter(Boolean);
79024 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
79025 // if all the highway values have the same implied access for this type then use that
79026 return impliedAccesses[0];
79031 return field.placeholder();
79035 access.focus = function () {
79036 items.selectAll('.preset-input-access').node().focus();
79039 return utilRebind(access, dispatch, 'on');
79042 function uiFieldAddress(field, context) {
79043 var dispatch = dispatch$8('change');
79045 var _selection = select(null);
79047 var _wrap = select(null);
79049 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
79051 var _entityIDs = [];
79057 var _addressFormats = [{
79058 format: [['housenumber', 'street'], ['city', 'postcode']]
79060 _mainFileFetcher.get('address_formats').then(function (d) {
79061 _addressFormats = d;
79063 if (!_selection.empty()) {
79064 _selection.call(address);
79066 })["catch"](function () {
79070 function getNearStreets() {
79071 var extent = combinedEntityExtent();
79072 var l = extent.center();
79073 var box = geoExtent(l).padByMeters(200);
79074 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
79075 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
79076 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
79078 title: d.tags.name,
79079 value: d.tags.name,
79080 dist: choice.distance
79082 }).sort(function (a, b) {
79083 return a.dist - b.dist;
79085 return utilArrayUniqBy(streets, 'value');
79087 function isAddressable(d) {
79088 return d.tags.highway && d.tags.name && d.type === 'way';
79092 function getNearCities() {
79093 var extent = combinedEntityExtent();
79094 var l = extent.center();
79095 var box = geoExtent(l).padByMeters(200);
79096 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
79098 title: d.tags['addr:city'] || d.tags.name,
79099 value: d.tags['addr:city'] || d.tags.name,
79100 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
79102 }).sort(function (a, b) {
79103 return a.dist - b.dist;
79105 return utilArrayUniqBy(cities, 'value');
79107 function isAddressable(d) {
79109 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
79110 if (d.tags.border_type === 'city') return true;
79111 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
79114 if (d.tags['addr:city']) return true;
79119 function getNearValues(key) {
79120 var extent = combinedEntityExtent();
79121 var l = extent.center();
79122 var box = geoExtent(l).padByMeters(200);
79123 var results = context.history().intersects(box).filter(function hasTag(d) {
79124 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
79125 }).map(function (d) {
79127 title: d.tags[key],
79128 value: d.tags[key],
79129 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
79131 }).sort(function (a, b) {
79132 return a.dist - b.dist;
79134 return utilArrayUniqBy(results, 'value');
79137 function updateForCountryCode() {
79138 if (!_countryCode) return;
79141 for (var i = 0; i < _addressFormats.length; i++) {
79142 var format = _addressFormats[i];
79144 if (!format.countryCodes) {
79145 addressFormat = format; // choose the default format, keep going
79146 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
79147 addressFormat = format; // choose the country format, stop here
79153 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
79154 var widths = addressFormat.widths || {
79155 housenumber: 1 / 3,
79163 // Normalize widths.
79164 var total = r.reduce(function (sum, key) {
79165 return sum + (widths[key] || 0.5);
79167 return r.map(function (key) {
79170 width: (widths[key] || 0.5) / total
79175 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
79176 return d.toString();
79179 rows.exit().remove();
79180 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
79181 return 'addr-' + d.id;
79182 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
79183 return d.width * 100 + '%';
79186 function addDropdown(d) {
79187 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
79189 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
79190 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
79191 callback(nearValues('addr:' + d.id));
79195 _wrap.selectAll('input').on('blur', change()).on('change', change());
79197 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
79199 if (_tags) updateTags(_tags);
79202 function address(selection) {
79203 _selection = selection;
79204 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79205 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
79206 var extent = combinedEntityExtent();
79211 if (context.inIntro()) {
79212 // localize the address format for the walkthrough
79213 countryCode = _t('intro.graph.countrycode');
79215 var center = extent.center();
79216 countryCode = iso1A2Code(center);
79220 _countryCode = countryCode.toLowerCase();
79221 updateForCountryCode();
79226 function change(onInput) {
79227 return function () {
79230 _wrap.selectAll('input').each(function (subfield) {
79231 var key = field.key + ':' + subfield.id;
79232 var value = this.value;
79233 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
79235 if (Array.isArray(_tags[key]) && !value) return;
79236 tags[key] = value || undefined;
79239 dispatch.call('change', this, tags, onInput);
79243 function updatePlaceholder(inputSelection) {
79244 return inputSelection.attr('placeholder', function (subfield) {
79245 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
79246 return _t('inspector.multiple_values');
79249 if (_countryCode) {
79250 var localkey = subfield.id + '!' + _countryCode;
79251 var tkey = addrField.hasTextForStringId('placeholders.' + localkey) ? localkey : subfield.id;
79252 return addrField.t('placeholders.' + tkey);
79257 function updateTags(tags) {
79258 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
79259 var val = tags[field.key + ':' + subfield.id];
79260 return typeof val === 'string' ? val : '';
79261 }).attr('title', function (subfield) {
79262 var val = tags[field.key + ':' + subfield.id];
79263 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
79264 }).classed('mixed', function (subfield) {
79265 return Array.isArray(tags[field.key + ':' + subfield.id]);
79266 }).call(updatePlaceholder);
79269 function combinedEntityExtent() {
79270 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79273 address.entityIDs = function (val) {
79274 if (!arguments.length) return _entityIDs;
79279 address.tags = function (tags) {
79284 address.focus = function () {
79285 var node = _wrap.selectAll('input').node();
79287 if (node) node.focus();
79290 return utilRebind(address, dispatch, 'on');
79293 function uiFieldCycleway(field, context) {
79294 var dispatch = dispatch$8('change');
79295 var items = select(null);
79296 var wrap = select(null);
79300 function cycleway(selection) {
79301 function stripcolon(s) {
79302 return s.replace(':', '');
79305 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79306 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79307 var div = wrap.selectAll('ul').data([0]);
79308 div = div.enter().append('ul').attr('class', 'rows').merge(div);
79309 var keys = ['cycleway:left', 'cycleway:right'];
79310 items = div.selectAll('li').data(keys);
79311 var enter = items.enter().append('li').attr('class', function (d) {
79312 return 'labeled-input preset-cycleway-' + stripcolon(d);
79314 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
79315 return 'preset-input-cycleway-' + stripcolon(d);
79316 }).html(function (d) {
79317 return field.t.html('types.' + d);
79319 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
79320 return 'preset-input-cycleway preset-input-' + stripcolon(d);
79321 }).call(utilNoAuto).each(function (d) {
79322 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
79324 items = items.merge(enter); // Update
79326 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
79329 function change(d3_event, key) {
79330 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
79332 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
79334 if (newValue === 'none' || newValue === '') {
79335 newValue = undefined;
79338 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
79339 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
79341 if (otherValue && Array.isArray(otherValue)) {
79342 // we must always have an explicit value for comparison
79343 otherValue = otherValue[0];
79346 if (otherValue === 'none' || otherValue === '') {
79347 otherValue = undefined;
79350 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
79351 // sides the same way
79353 if (newValue === otherValue) {
79355 cycleway: newValue,
79356 'cycleway:left': undefined,
79357 'cycleway:right': undefined
79360 // Always set both left and right as changing one can affect the other
79362 cycleway: undefined
79364 tag[key] = newValue;
79365 tag[otherKey] = otherValue;
79368 dispatch.call('change', this, tag);
79371 cycleway.options = function () {
79372 return field.options.map(function (option) {
79374 title: field.t('options.' + option + '.description'),
79380 cycleway.tags = function (tags) {
79381 _tags = tags; // If cycleway is set, use that instead of individual values
79383 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
79384 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
79385 if (commonValue) return commonValue;
79386 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
79387 }).attr('title', function (d) {
79388 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
79391 if (Array.isArray(tags.cycleway)) {
79392 vals = vals.concat(tags.cycleway);
79395 if (Array.isArray(tags[d])) {
79396 vals = vals.concat(tags[d]);
79399 return vals.filter(Boolean).join('\n');
79403 }).attr('placeholder', function (d) {
79404 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
79405 return _t('inspector.multiple_values');
79408 return field.placeholder();
79409 }).classed('mixed', function (d) {
79410 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
79414 cycleway.focus = function () {
79415 var node = wrap.selectAll('input').node();
79416 if (node) node.focus();
79419 return utilRebind(cycleway, dispatch, 'on');
79422 function uiFieldLanes(field, context) {
79423 var dispatch = dispatch$8('change');
79424 var LANE_WIDTH = 40;
79425 var LANE_HEIGHT = 200;
79426 var _entityIDs = [];
79428 function lanes(selection) {
79429 var lanesData = context.entity(_entityIDs[0]).lanes();
79431 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
79432 selection.call(lanes.off);
79436 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79437 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79438 var surface = wrap.selectAll('.surface').data([0]);
79439 var d = utilGetDimensions(wrap);
79440 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
79441 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
79442 var lanesSelection = surface.selectAll('.lanes').data([0]);
79443 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
79444 lanesSelection.attr('transform', function () {
79445 return 'translate(' + freeSpace / 2 + ', 0)';
79447 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
79448 lane.exit().remove();
79449 var enter = lane.enter().append('g').attr('class', 'lane');
79450 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
79451 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
79452 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
79453 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
79454 lane = lane.merge(enter);
79455 lane.attr('transform', function (d) {
79456 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
79458 lane.select('.forward').style('visibility', function (d) {
79459 return d.direction === 'forward' ? 'visible' : 'hidden';
79461 lane.select('.bothways').style('visibility', function (d) {
79462 return d.direction === 'bothways' ? 'visible' : 'hidden';
79464 lane.select('.backward').style('visibility', function (d) {
79465 return d.direction === 'backward' ? 'visible' : 'hidden';
79469 lanes.entityIDs = function (val) {
79473 lanes.tags = function () {};
79475 lanes.focus = function () {};
79477 lanes.off = function () {};
79479 return utilRebind(lanes, dispatch, 'on');
79481 uiFieldLanes.supportsMultiselection = false;
79483 var _languagesArray = [];
79484 function uiFieldLocalized(field, context) {
79485 var dispatch = dispatch$8('change', 'input');
79486 var wikipedia = services.wikipedia;
79487 var input = select(null);
79488 var localizedInputs = select(null);
79492 var _tags; // A concern here in switching to async data means that _languagesArray will not
79493 // be available the first time through, so things like the fetchers and
79494 // the language() function will not work immediately.
79497 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
79500 var _territoryLanguages = {};
79501 _mainFileFetcher.get('territory_languages').then(function (d) {
79502 _territoryLanguages = d;
79503 })["catch"](function () {
79505 }); // reuse these combos
79507 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
79509 var _selection = select(null);
79511 var _multilingual = [];
79513 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
79517 var _entityIDs = [];
79519 function loadLanguagesArray(dataLanguages) {
79520 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
79522 var replacements = {
79524 // in OSM, `sr` implies Cyrillic
79525 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
79529 for (var code in dataLanguages) {
79530 if (replacements[code] === false) continue;
79531 var metaCode = code;
79532 if (replacements[code]) metaCode = replacements[code];
79534 _languagesArray.push({
79535 localName: _mainLocalizer.languageName(metaCode, {
79538 nativeName: dataLanguages[metaCode].nativeName,
79540 label: _mainLocalizer.languageName(metaCode)
79545 function calcLocked() {
79546 // Protect name field for suggestion presets that don't display a brand/operator field
79547 var isLocked = field.id === 'name' && _entityIDs.length && _entityIDs.some(function (entityID) {
79548 var entity = context.graph().hasEntity(entityID);
79549 if (!entity) return false; // Features linked to Wikidata are likely important and should be protected
79551 if (entity.tags.wikidata) return true; // Assume the name has already been confirmed if its source has been researched
79553 if (entity.tags['name:etymology:wikidata']) return true; // Lock the `name` if this is a suggestion preset that assigns the name,
79554 // and the preset does not display a `brand` or `operator` field.
79555 // (For presets like hotels, car dealerships, post offices, the `name` should remain editable)
79556 // see also similar logic in `outdated_tags.js`
79558 var preset = _mainPresetIndex.match(entity, context.graph());
79561 var isSuggestion = preset.suggestion;
79562 var fields = preset.fields();
79563 var showsBrandField = fields.some(function (d) {
79564 return d.id === 'brand';
79566 var showsOperatorField = fields.some(function (d) {
79567 return d.id === 'operator';
79569 var setsName = preset.addTags.name;
79570 var setsBrandWikidata = preset.addTags['brand:wikidata'];
79571 var setsOperatorWikidata = preset.addTags['operator:wikidata'];
79572 return isSuggestion && setsName && (setsBrandWikidata && !showsBrandField || setsOperatorWikidata && !showsOperatorField);
79578 field.locked(isLocked);
79579 } // update _multilingual, maintaining the existing order
79582 function calcMultilingual(tags) {
79583 var existingLangsOrdered = _multilingual.map(function (item) {
79587 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
79589 for (var k in tags) {
79590 var m = k.match(/^(.*):(.*)$/);
79592 if (m && m[1] === field.key && m[2]) {
79598 if (existingLangs.has(item.lang)) {
79599 // update the value
79600 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
79601 existingLangs["delete"](item.lang);
79603 _multilingual.push(item);
79606 } // Don't remove items based on deleted tags, since this makes the UI
79607 // disappear unexpectedly when clearing values - #8164
79610 _multilingual.forEach(function (item) {
79611 if (item.lang && existingLangs.has(item.lang)) {
79617 function localized(selection) {
79618 _selection = selection;
79620 var isLocked = field.locked();
79621 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
79623 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79624 input = wrap.selectAll('.localized-main').data([0]); // enter/update
79626 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
79627 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
79628 var translateButton = wrap.selectAll('.localized-add').data([0]);
79629 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
79630 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
79632 if (_tags && !_multilingual.length) {
79633 calcMultilingual(_tags);
79636 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
79637 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
79638 localizedInputs.call(renderMultilingual);
79639 localizedInputs.selectAll('button, input').classed('disabled', !!isLocked).attr('readonly', isLocked || null);
79641 function addNew(d3_event) {
79642 d3_event.preventDefault();
79643 if (field.locked()) return;
79644 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
79646 var langExists = _multilingual.find(function (datum) {
79647 return datum.lang === defaultLang;
79650 var isLangEn = defaultLang.indexOf('en') > -1;
79652 if (isLangEn || langExists) {
79654 langExists = _multilingual.find(function (datum) {
79655 return datum.lang === defaultLang;
79660 // prepend the value so it appears at the top
79661 _multilingual.unshift({
79666 localizedInputs.call(renderMultilingual);
79670 function change(onInput) {
79671 return function (d3_event) {
79672 if (field.locked()) {
79673 d3_event.preventDefault();
79677 var val = utilGetSetValue(select(this));
79678 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
79680 if (!val && Array.isArray(_tags[field.key])) return;
79682 t[field.key] = val || undefined;
79683 dispatch.call('change', this, t, onInput);
79688 function key(lang) {
79689 return field.key + ':' + lang;
79692 function changeLang(d3_event, d) {
79693 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
79695 var lang = utilGetSetValue(select(this)).toLowerCase();
79697 var language = _languagesArray.find(function (d) {
79698 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
79701 if (language) lang = language.code;
79703 if (d.lang && d.lang !== lang) {
79704 tags[key(d.lang)] = undefined;
79707 var newKey = lang && context.cleanTagKey(key(lang));
79708 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
79710 if (newKey && value) {
79711 tags[newKey] = value;
79712 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
79713 tags[newKey] = _wikiTitles[d.lang];
79717 dispatch.call('change', this, tags);
79720 function changeValue(d3_event, d) {
79721 if (!d.lang) return;
79722 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
79724 if (!value && Array.isArray(d.value)) return;
79726 t[key(d.lang)] = value;
79728 dispatch.call('change', this, t);
79731 function fetchLanguages(value, cb) {
79732 var v = value.toLowerCase(); // show the user's language first
79734 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
79736 if (_countryCode && _territoryLanguages[_countryCode]) {
79737 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
79740 var langItems = [];
79741 langCodes.forEach(function (code) {
79742 var langItem = _languagesArray.find(function (item) {
79743 return item.code === code;
79746 if (langItem) langItems.push(langItem);
79748 langItems = utilArrayUniq(langItems.concat(_languagesArray));
79749 cb(langItems.filter(function (d) {
79750 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;
79751 }).map(function (d) {
79758 function renderMultilingual(selection) {
79759 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
79762 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
79763 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
79764 var wrap = select(this);
79765 var domId = utilUniqueDomId(index);
79766 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
79767 var text = label.append('span').attr('class', 'label-text');
79768 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
79769 text.append('span').attr('class', 'label-textannotation');
79770 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
79771 if (field.locked()) return;
79772 d3_event.preventDefault(); // remove the UI item manually
79774 _multilingual.splice(_multilingual.indexOf(d), 1);
79776 var langKey = d.lang && key(d.lang);
79778 if (langKey && langKey in _tags) {
79779 delete _tags[langKey]; // remove from entity tags
79782 t[langKey] = undefined;
79783 dispatch.call('change', this, t);
79787 renderMultilingual(selection);
79788 }).call(svgIcon('#iD-operation-delete'));
79789 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);
79790 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
79792 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 () {
79793 select(this).style('max-height', '').style('overflow', 'visible');
79795 entries = entries.merge(entriesEnter);
79796 entries.order(); // allow removing the entry UIs even if there isn't a tag to remove
79798 entries.classed('present', true);
79799 utilGetSetValue(entries.select('.localized-lang'), function (d) {
79800 var langItem = _languagesArray.find(function (item) {
79801 return item.code === d.lang;
79804 if (langItem) return langItem.label;
79807 utilGetSetValue(entries.select('.localized-value'), function (d) {
79808 return typeof d.value === 'string' ? d.value : '';
79809 }).attr('title', function (d) {
79810 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
79811 }).attr('placeholder', function (d) {
79812 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
79813 }).classed('mixed', function (d) {
79814 return Array.isArray(d.value);
79818 localized.tags = function (tags) {
79819 _tags = tags; // Fetch translations from wikipedia
79821 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
79823 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
79825 if (wm && wm[0] && wm[1]) {
79826 wikipedia.translations(wm[1], wm[2], function (err, d) {
79827 if (err || !d) return;
79833 var isMixed = Array.isArray(tags[field.key]);
79834 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);
79835 calcMultilingual(tags);
79837 _selection.call(localized);
79840 localized.focus = function () {
79841 input.node().focus();
79844 localized.entityIDs = function (val) {
79845 if (!arguments.length) return _entityIDs;
79847 _multilingual = [];
79852 function loadCountryCode() {
79853 var extent = combinedEntityExtent();
79854 var countryCode = extent && iso1A2Code(extent.center());
79855 _countryCode = countryCode && countryCode.toLowerCase();
79858 function combinedEntityExtent() {
79859 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79862 return utilRebind(localized, dispatch, 'on');
79865 function uiFieldRoadspeed(field, context) {
79866 var dispatch = dispatch$8('change');
79867 var unitInput = select(null);
79868 var input = select(null);
79869 var _entityIDs = [];
79875 var speedCombo = uiCombobox(context, 'roadspeed');
79876 var unitCombo = uiCombobox(context, 'roadspeed-unit').data(['km/h', 'mph'].map(comboValues));
79877 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
79878 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
79880 function roadspeed(selection) {
79881 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79882 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
79883 input = wrap.selectAll('input.roadspeed-number').data([0]);
79884 input = input.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
79885 input.on('change', change).on('blur', change);
79886 var loc = combinedEntityExtent().center();
79887 _isImperial = roadSpeedUnit(loc) === 'mph';
79888 unitInput = wrap.selectAll('input.roadspeed-unit').data([0]);
79889 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'roadspeed-unit').call(unitCombo).merge(unitInput);
79890 unitInput.on('blur', changeUnits).on('change', changeUnits);
79892 function changeUnits() {
79893 _isImperial = utilGetSetValue(unitInput) === 'mph';
79894 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
79895 setUnitSuggestions();
79900 function setUnitSuggestions() {
79901 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
79902 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
79905 function comboValues(d) {
79907 value: d.toString(),
79908 title: d.toString()
79912 function change() {
79914 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
79916 if (!value && Array.isArray(_tags[field.key])) return;
79919 tag[field.key] = undefined;
79920 } else if (isNaN(value) || !_isImperial) {
79921 tag[field.key] = context.cleanTagValue(value);
79923 tag[field.key] = context.cleanTagValue(value + ' mph');
79926 dispatch.call('change', this, tag);
79929 roadspeed.tags = function (tags) {
79931 var value = tags[field.key];
79932 var isMixed = Array.isArray(value);
79935 if (value && value.indexOf('mph') >= 0) {
79936 value = parseInt(value, 10).toString();
79937 _isImperial = true;
79938 } else if (value) {
79939 _isImperial = false;
79943 setUnitSuggestions();
79944 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);
79947 roadspeed.focus = function () {
79948 input.node().focus();
79951 roadspeed.entityIDs = function (val) {
79955 function combinedEntityExtent() {
79956 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
79959 return utilRebind(roadspeed, dispatch, 'on');
79962 function uiFieldRadio(field, context) {
79963 var dispatch = dispatch$8('change');
79964 var placeholder = select(null);
79965 var wrap = select(null);
79966 var labels = select(null);
79967 var radios = select(null);
79968 var radioData = (field.options || field.keys).slice(); // shallow copy
79973 var _entityIDs = [];
79975 function selectedKey() {
79976 var node = wrap.selectAll('.form-field-input-radio label.active input');
79977 return !node.empty() && node.datum();
79980 function radio(selection) {
79981 selection.classed('preset-radio', true);
79982 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
79983 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
79984 enter.append('span').attr('class', 'placeholder');
79985 wrap = wrap.merge(enter);
79986 placeholder = wrap.selectAll('.placeholder');
79987 labels = wrap.selectAll('label').data(radioData);
79988 enter = labels.enter().append('label');
79989 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
79990 return field.t('options.' + d, {
79993 }).attr('checked', false);
79994 enter.append('span').html(function (d) {
79995 return field.t.html('options.' + d, {
79999 labels = labels.merge(enter);
80000 radios = labels.selectAll('input').on('change', changeRadio);
80003 function structureExtras(selection, tags) {
80004 var selected = selectedKey() || tags.layer !== undefined;
80005 var type = _mainPresetIndex.field(selected);
80006 var layer = _mainPresetIndex.field('layer');
80007 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
80008 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
80009 extrasWrap.exit().remove();
80010 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
80011 var list = extrasWrap.selectAll('ul').data([0]);
80012 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
80015 if (!typeField || typeField.id !== selected) {
80016 typeField = uiField(context, type, _entityIDs, {
80018 }).on('change', changeType);
80021 typeField.tags(tags);
80026 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
80030 typeItem.exit().remove(); // Enter
80032 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
80033 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
80034 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
80036 typeItem = typeItem.merge(typeEnter);
80039 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
80043 if (layer && showLayer) {
80045 layerField = uiField(context, layer, _entityIDs, {
80047 }).on('change', changeLayer);
80050 layerField.tags(tags);
80051 field.keys = utilArrayUnion(field.keys, ['layer']);
80054 field.keys = field.keys.filter(function (k) {
80055 return k !== 'layer';
80059 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
80061 layerItem.exit().remove(); // Enter
80063 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
80064 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
80065 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
80067 layerItem = layerItem.merge(layerEnter);
80070 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
80074 function changeType(t, onInput) {
80075 var key = selectedKey();
80079 if (val !== 'no') {
80080 _oldType[key] = val;
80083 if (field.type === 'structureRadio') {
80084 // remove layer if it should not be set
80085 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
80086 t.layer = undefined;
80087 } // add layer if it should be set
80090 if (t.layer === undefined) {
80091 if (key === 'bridge' && val !== 'no') {
80095 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
80101 dispatch.call('change', this, t, onInput);
80104 function changeLayer(t, onInput) {
80105 if (t.layer === '0') {
80106 t.layer = undefined;
80109 dispatch.call('change', this, t, onInput);
80112 function changeRadio() {
80117 t[field.key] = undefined;
80120 radios.each(function (d) {
80121 var active = select(this).property('checked');
80122 if (active) activeKey = d;
80125 if (active) t[field.key] = d;
80127 var val = _oldType[activeKey] || 'yes';
80128 t[d] = active ? val : undefined;
80132 if (field.type === 'structureRadio') {
80133 if (activeKey === 'bridge') {
80135 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
80138 t.layer = undefined;
80142 dispatch.call('change', this, t);
80145 radio.tags = function (tags) {
80146 radios.property('checked', function (d) {
80148 return tags[field.key] === d;
80151 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
80154 function isMixed(d) {
80156 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
80159 return Array.isArray(tags[d]);
80162 labels.classed('active', function (d) {
80164 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
80167 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
80168 }).classed('mixed', isMixed).attr('title', function (d) {
80169 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
80171 var selection = radios.filter(function () {
80172 return this.checked;
80175 if (selection.empty()) {
80176 placeholder.html(_t.html('inspector.none'));
80178 placeholder.html(selection.attr('value'));
80179 _oldType[selection.datum()] = tags[selection.datum()];
80182 if (field.type === 'structureRadio') {
80183 // For waterways without a tunnel tag, set 'culvert' as
80184 // the _oldType to default to if the user picks 'tunnel'
80185 if (!!tags.waterway && !_oldType.tunnel) {
80186 _oldType.tunnel = 'culvert';
80189 wrap.call(structureExtras, tags);
80193 radio.focus = function () {
80194 radios.node().focus();
80197 radio.entityIDs = function (val) {
80198 if (!arguments.length) return _entityIDs;
80204 radio.isAllowed = function () {
80205 return _entityIDs.length === 1;
80208 return utilRebind(radio, dispatch, 'on');
80211 function uiFieldRestrictions(field, context) {
80212 var dispatch = dispatch$8('change');
80213 var breathe = behaviorBreathe();
80214 corePreferences('turn-restriction-via-way', null); // remove old key
80216 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
80218 var storedDistance = corePreferences('turn-restriction-distance');
80220 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
80222 var _maxDistance = storedDistance ? +storedDistance : 30;
80224 var _initialized = false;
80226 var _parent = select(null); // the entire field
80229 var _container = select(null); // just the map
80244 function restrictions(selection) {
80245 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
80247 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
80248 _graph = context.graph();
80249 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
80250 } // It's possible for there to be no actual intersection here.
80251 // for example, a vertex of two `highway=path`
80252 // In this case, hide the field.
80255 var isOK = _intersection && _intersection.vertices.length && // has vertices
80256 _intersection.vertices // has the vertex that the user selected
80257 .filter(function (vertex) {
80258 return vertex.id === _vertexID;
80259 }).length && _intersection.ways.length > 2 && // has more than 2 ways
80260 _intersection.ways // has more than 1 TO way
80261 .filter(function (way) {
80263 }).length > 1; // Also hide in the case where
80265 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
80267 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
80268 selection.call(restrictions.off);
80272 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80273 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80274 var container = wrap.selectAll('.restriction-container').data([0]); // enter
80276 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
80277 containerEnter.append('div').attr('class', 'restriction-help'); // update
80279 _container = containerEnter.merge(container).call(renderViewer);
80280 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
80282 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
80285 function renderControls(selection) {
80286 var distControl = selection.selectAll('.restriction-distance').data([0]);
80287 distControl.exit().remove();
80288 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
80289 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
80290 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
80291 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
80293 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
80294 var val = select(this).property('value');
80295 _maxDistance = +val;
80296 _intersection = null;
80298 _container.selectAll('.layer-osm .layer-turns *').remove();
80300 corePreferences('turn-restriction-distance', _maxDistance);
80302 _parent.call(restrictions);
80304 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
80305 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
80306 viaControl.exit().remove();
80307 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
80308 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
80309 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
80310 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
80312 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
80313 var val = select(this).property('value');
80316 _container.selectAll('.layer-osm .layer-turns *').remove();
80318 corePreferences('turn-restriction-via-way0', _maxViaWay);
80320 _parent.call(restrictions);
80322 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
80325 function renderViewer(selection) {
80326 if (!_intersection) return;
80327 var vgraph = _intersection.graph;
80328 var filter = utilFunctor(true);
80329 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
80330 // Instead of asking the restriction-container for its dimensions,
80331 // we can ask the .sidebar, which can have its dimensions cached.
80332 // width: calc as sidebar - padding
80333 // height: hardcoded (from `80_app.css`)
80334 // var d = utilGetDimensions(selection);
80336 var sdims = utilGetDimensions(context.container().select('.sidebar'));
80337 var d = [sdims[0] - 50, 370];
80338 var c = geoVecScale(d, 0.5);
80340 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
80342 var extent = geoExtent();
80344 for (var i = 0; i < _intersection.vertices.length; i++) {
80345 extent._extend(_intersection.vertices[i].extent());
80346 } // If this is a large intersection, adjust zoom to fit extent
80349 if (_intersection.vertices.length > 1) {
80350 var padding = 180; // in z22 pixels
80352 var tl = projection([extent[0][0], extent[1][1]]);
80353 var br = projection([extent[1][0], extent[0][1]]);
80354 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
80355 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
80356 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
80357 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
80358 z = z - Math.max(hZoomDiff, vZoomDiff);
80359 projection.scale(geoZoomToScale(z));
80362 var padTop = 35; // reserve top space for hint text
80364 var extentCenter = projection(extent.center());
80365 extentCenter[1] = extentCenter[1] - padTop;
80366 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
80367 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
80368 var drawVertices = svgVertices(projection, context);
80369 var drawLines = svgLines(projection, context);
80370 var drawTurns = svgTurns(projection, context);
80371 var firstTime = selection.selectAll('.surface').empty();
80372 selection.call(drawLayers);
80373 var surface = selection.selectAll('.surface').classed('tr', true);
80376 _initialized = true;
80377 surface.call(breathe);
80378 } // This can happen if we've lowered the detail while a FROM way
80379 // is selected, and that way is no longer part of the intersection.
80382 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
80387 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));
80388 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
80389 surface.selectAll('.selected').classed('selected', false);
80390 surface.selectAll('.related').classed('related', false);
80394 way = vgraph.entity(_fromWayID);
80395 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
80398 document.addEventListener('resizeWindow', function () {
80399 utilSetDimensions(_container, null);
80404 function click(d3_event) {
80405 surface.call(breathe.off).call(breathe);
80406 var datum = d3_event.target.__data__;
80407 var entity = datum && datum.properties && datum.properties.entity;
80413 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
80414 _fromWayID = datum.id;
80417 } else if (datum instanceof osmTurn) {
80418 var actions, extraActions, turns, i;
80419 var restrictionType = osmInferRestriction(vgraph, datum, projection);
80421 if (datum.restrictionID && !datum.direct) {
80423 } else if (datum.restrictionID && !datum.only) {
80426 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
80428 datumOnly.only = true; // but change this property
80430 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
80431 // We will remember them in _oldTurns, and restore them if the user clicks again.
80433 turns = _intersection.turns(_fromWayID, 2);
80437 for (i = 0; i < turns.length; i++) {
80438 var turn = turns[i];
80439 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
80441 if (turn.direct && turn.path[1] === datum.path[1]) {
80442 seen[turns[i].restrictionID] = true;
80443 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
80445 _oldTurns.push(turn);
80447 extraActions.push(actionUnrestrictTurn(turn));
80451 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
80452 } else if (datum.restrictionID) {
80454 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
80455 // This relies on the assumption that the intersection was already split up when we
80456 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
80457 turns = _oldTurns || [];
80460 for (i = 0; i < turns.length; i++) {
80461 if (turns[i].key !== datum.key) {
80462 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
80467 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
80470 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
80473 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
80474 // Refresh it and update the help..
80476 var s = surface.selectAll('.' + datum.key);
80477 datum = s.empty() ? null : s.datum();
80478 updateHints(datum);
80486 function mouseover(d3_event) {
80487 var datum = d3_event.target.__data__;
80488 updateHints(datum);
80491 _lastXPos = _lastXPos || sdims[0];
80493 function redraw(minChange) {
80497 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
80500 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
80501 if (context.hasEntity(_vertexID)) {
80504 _container.call(renderViewer);
80509 function highlightPathsFrom(wayID) {
80510 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
80511 surface.selectAll('.' + wayID).classed('related', true);
80514 var turns = _intersection.turns(wayID, _maxViaWay);
80516 for (var i = 0; i < turns.length; i++) {
80517 var turn = turns[i];
80518 var ids = [turn.to.way];
80519 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
80521 if (turn.only || turns.length === 1) {
80522 if (turn.via.ways) {
80523 ids = ids.concat(turn.via.ways);
80525 } else if (turn.to.way === wayID) {
80529 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
80534 function updateHints(datum) {
80535 var help = _container.selectAll('.restriction-help').html('');
80537 var placeholders = {};
80538 ['from', 'via', 'to'].forEach(function (k) {
80539 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
80541 var entity = datum && datum.properties && datum.properties.entity;
80548 way = vgraph.entity(_fromWayID);
80549 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
80550 } // Hovering a way
80553 if (datum instanceof osmWay && datum.__from) {
80555 highlightPathsFrom(_fromWayID ? null : way.id);
80556 surface.selectAll('.' + way.id).classed('related', true);
80557 var clickSelect = !_fromWayID || _fromWayID !== way.id;
80558 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
80559 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
80560 from: placeholders.from,
80561 fromName: displayName(way.id, vgraph)
80562 })); // Hovering a turn arrow
80563 } else if (datum instanceof osmTurn) {
80564 var restrictionType = osmInferRestriction(vgraph, datum, projection);
80565 var turnType = restrictionType.replace(/^(only|no)\_/, '');
80566 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
80567 var klass, turnText, nextText;
80570 klass = 'restrict';
80571 turnText = _t.html('restriction.help.turn.no_' + turnType, {
80574 nextText = _t.html('restriction.help.turn.only_' + turnType, {
80577 } else if (datum.only) {
80579 turnText = _t.html('restriction.help.turn.only_' + turnType, {
80582 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
80587 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
80590 nextText = _t.html('restriction.help.turn.no_' + turnType, {
80595 help.append('div') // "NO Right Turn (indirect)"
80596 .attr('class', 'qualifier ' + klass).html(turnText);
80597 help.append('div') // "FROM {fromName} TO {toName}"
80598 .html(_t.html('restriction.help.from_name_to_name', {
80599 from: placeholders.from,
80600 fromName: displayName(datum.from.way, vgraph),
80601 to: placeholders.to,
80602 toName: displayName(datum.to.way, vgraph)
80605 if (datum.via.ways && datum.via.ways.length) {
80608 for (var i = 0; i < datum.via.ways.length; i++) {
80609 var prev = names[names.length - 1];
80610 var curr = displayName(datum.via.ways[i], vgraph);
80612 if (!prev || curr !== prev) {
80613 // collapse identical names
80618 help.append('div') // "VIA {viaNames}"
80619 .html(_t.html('restriction.help.via_names', {
80620 via: placeholders.via,
80621 viaNames: names.join(', ')
80626 help.append('div') // Click for "No Right Turn"
80627 .html(_t.html('restriction.help.toggle', {
80628 turn: nextText.trim()
80632 highlightPathsFrom(null);
80633 var alongIDs = datum.path.slice();
80634 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
80636 highlightPathsFrom(null);
80639 help.append('div') // "FROM {fromName}"
80640 .html(_t.html('restriction.help.from_name', {
80641 from: placeholders.from,
80642 fromName: displayName(_fromWayID, vgraph)
80645 help.append('div') // "Click to select a FROM segment."
80646 .html(_t.html('restriction.help.select_from', {
80647 from: placeholders.from
80654 function displayMaxDistance(maxDist) {
80655 var isImperial = !_mainLocalizer.usesMetric();
80660 // imprecise conversion for prettier display
80670 distance: _t('units.feet', {
80671 quantity: distToFeet
80676 distance: _t('units.meters', {
80682 return _t.html('restriction.controls.distance_up_to', opts);
80685 function displayMaxVia(maxVia) {
80686 return maxVia === 0 ? _t.html('restriction.controls.via_node_only') : maxVia === 1 ? _t.html('restriction.controls.via_up_to_one') : _t.html('restriction.controls.via_up_to_two');
80689 function displayName(entityID, graph) {
80690 var entity = graph.entity(entityID);
80691 var name = utilDisplayName(entity) || '';
80692 var matched = _mainPresetIndex.match(entity, graph);
80693 var type = matched && matched.name() || utilDisplayType(entity.id);
80694 return name || type;
80697 restrictions.entityIDs = function (val) {
80698 _intersection = null;
80701 _vertexID = val[0];
80704 restrictions.tags = function () {};
80706 restrictions.focus = function () {};
80708 restrictions.off = function (selection) {
80709 if (!_initialized) return;
80710 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
80711 select(window).on('resize.restrictions', null);
80714 return utilRebind(restrictions, dispatch, 'on');
80716 uiFieldRestrictions.supportsMultiselection = false;
80718 function uiFieldTextarea(field, context) {
80719 var dispatch = dispatch$8('change');
80720 var input = select(null);
80724 function textarea(selection) {
80725 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80726 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80727 input = wrap.selectAll('textarea').data([0]);
80728 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
80731 function change(onInput) {
80732 return function () {
80733 var val = utilGetSetValue(input);
80734 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
80736 if (!val && Array.isArray(_tags[field.key])) return;
80738 t[field.key] = val || undefined;
80739 dispatch.call('change', this, t, onInput);
80743 textarea.tags = function (tags) {
80745 var isMixed = Array.isArray(tags[field.key]);
80746 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);
80749 textarea.focus = function () {
80750 input.node().focus();
80753 return utilRebind(textarea, dispatch, 'on');
80756 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
80763 // eslint-disable-next-line es/no-string-prototype-endswith -- safe
80764 var $endsWith = ''.endsWith;
80765 var min = Math.min;
80767 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
80768 // https://github.com/zloirock/core-js/pull/702
80769 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
80770 var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith');
80771 return descriptor && !descriptor.writable;
80774 // `String.prototype.endsWith` method
80775 // https://tc39.es/ecma262/#sec-string.prototype.endswith
80776 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
80777 endsWith: function endsWith(searchString /* , endPosition = @length */) {
80778 var that = String(requireObjectCoercible(this));
80779 notARegexp(searchString);
80780 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
80781 var len = toLength(that.length);
80782 var end = endPosition === undefined ? len : min(toLength(endPosition), len);
80783 var search = String(searchString);
80785 ? $endsWith.call(that, search, end)
80786 : that.slice(end - search.length, end) === search;
80790 function uiFieldWikidata(field, context) {
80791 var wikidata = services.wikidata;
80792 var dispatch = dispatch$8('change');
80794 var _selection = select(null);
80796 var _searchInput = select(null);
80799 var _wikidataEntity = null;
80801 var _entityIDs = [];
80803 var _wikipediaKey = field.keys && field.keys.find(function (key) {
80804 return key.includes('wikipedia');
80806 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
80808 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
80810 function wiki(selection) {
80811 _selection = selection;
80812 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
80813 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
80814 var list = wrap.selectAll('ul').data([0]);
80815 list = list.enter().append('ul').attr('class', 'rows').merge(list);
80816 var searchRow = list.selectAll('li.wikidata-search').data([0]);
80817 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
80818 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
80819 var node = select(this).node();
80820 node.setSelectionRange(0, node.value.length);
80821 }).on('blur', function () {
80822 setLabelForEntity();
80823 }).call(combobox.fetcher(fetchWikidataItems));
80824 combobox.on('accept', function (d) {
80829 }).on('cancel', function () {
80830 setLabelForEntity();
80832 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
80833 domain: 'wikidata.org'
80834 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
80835 d3_event.preventDefault();
80836 if (_wikiURL) window.open(_wikiURL, '_blank');
80838 searchRow = searchRow.merge(searchRowEnter);
80839 _searchInput = searchRow.select('input');
80840 var wikidataProperties = ['description', 'identifier'];
80841 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
80843 var enter = items.enter().append('li').attr('class', function (d) {
80844 return 'labeled-input preset-wikidata-' + d;
80846 enter.append('span').attr('class', 'label').html(function (d) {
80847 return _t.html('wikidata.' + d);
80849 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
80850 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
80851 d3_event.preventDefault();
80852 select(this.parentNode).select('input').node().select();
80853 document.execCommand('copy');
80857 function fetchWikidataItems(q, callback) {
80858 if (!q && _hintKey) {
80859 // other tags may be good search terms
80860 for (var i in _entityIDs) {
80861 var entity = context.hasEntity(_entityIDs[i]);
80863 if (entity.tags[_hintKey]) {
80864 q = entity.tags[_hintKey];
80870 wikidata.itemsForSearchQuery(q, function (err, data) {
80873 for (var i in data) {
80874 data[i].value = data[i].label + ' (' + data[i].id + ')';
80875 data[i].title = data[i].description;
80878 if (callback) callback(data);
80882 function change() {
80884 syncTags[field.key] = _qid;
80885 dispatch.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
80887 var initGraph = context.graph();
80888 var initEntityIDs = _entityIDs;
80889 wikidata.entityByQID(_qid, function (err, entity) {
80890 if (err) return; // If graph has changed, we can't apply this update.
80892 if (context.graph() !== initGraph) return;
80893 if (!entity.sitelinks) return;
80894 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
80896 ['labels', 'descriptions'].forEach(function (key) {
80897 if (!entity[key]) return;
80898 var valueLangs = Object.keys(entity[key]);
80899 if (valueLangs.length === 0) return;
80900 var valueLang = valueLangs[0];
80902 if (langs.indexOf(valueLang) === -1) {
80903 langs.push(valueLang);
80906 var newWikipediaValue;
80908 if (_wikipediaKey) {
80909 var foundPreferred;
80911 for (var i in langs) {
80912 var lang = langs[i];
80913 var siteID = lang.replace('-', '_') + 'wiki';
80915 if (entity.sitelinks[siteID]) {
80916 foundPreferred = true;
80917 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
80923 if (!foundPreferred) {
80924 // No wikipedia sites available in the user's language or the fallback languages,
80925 // default to any wikipedia sitelink
80926 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
80927 return site.endsWith('wiki');
80930 if (wikiSiteKeys.length === 0) {
80931 // if no wikipedia pages are linked to this wikidata entity, delete that tag
80932 newWikipediaValue = null;
80934 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
80935 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
80936 newWikipediaValue = wikiLang + ':' + wikiTitle;
80941 if (newWikipediaValue) {
80942 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
80945 if (typeof newWikipediaValue === 'undefined') return;
80946 var actions = initEntityIDs.map(function (entityID) {
80947 var entity = context.hasEntity(entityID);
80948 if (!entity) return null;
80949 var currTags = Object.assign({}, entity.tags); // shallow copy
80951 if (newWikipediaValue === null) {
80952 if (!currTags[_wikipediaKey]) return null;
80953 delete currTags[_wikipediaKey];
80955 currTags[_wikipediaKey] = newWikipediaValue;
80958 return actionChangeTags(entityID, currTags);
80959 }).filter(Boolean);
80960 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
80962 context.overwrite(function actionUpdateWikipediaTags(graph) {
80963 actions.forEach(function (action) {
80964 graph = action(graph);
80967 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
80968 // changeTags() is not intended to be called asynchronously
80972 function setLabelForEntity() {
80975 if (_wikidataEntity) {
80976 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
80978 if (label.length === 0) {
80979 label = _wikidataEntity.id.toString();
80983 utilGetSetValue(_searchInput, label);
80986 wiki.tags = function (tags) {
80987 var isMixed = Array.isArray(tags[field.key]);
80989 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
80991 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
80993 if (!/^Q[0-9]*$/.test(_qid)) {
80994 // not a proper QID
80997 } // QID value in correct format
81000 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
81001 wikidata.entityByQID(_qid, function (err, entity) {
81007 _wikidataEntity = entity;
81008 setLabelForEntity();
81009 var description = entityPropertyForDisplay(entity, 'descriptions');
81011 _selection.select('button.wiki-link').classed('disabled', false);
81013 _selection.select('.preset-wikidata-description').style('display', function () {
81014 return description.length > 0 ? 'flex' : 'none';
81015 }).select('input').attr('value', description);
81017 _selection.select('.preset-wikidata-identifier').style('display', function () {
81018 return entity.id ? 'flex' : 'none';
81019 }).select('input').attr('value', entity.id);
81020 }); // not a proper QID
81022 function unrecognized() {
81023 _wikidataEntity = null;
81024 setLabelForEntity();
81026 _selection.select('.preset-wikidata-description').style('display', 'none');
81028 _selection.select('.preset-wikidata-identifier').style('display', 'none');
81030 _selection.select('button.wiki-link').classed('disabled', true);
81032 if (_qid && _qid !== '') {
81033 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
81040 function entityPropertyForDisplay(wikidataEntity, propKey) {
81041 if (!wikidataEntity[propKey]) return '';
81042 var propObj = wikidataEntity[propKey];
81043 var langKeys = Object.keys(propObj);
81044 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
81046 var langs = wikidata.languagesToQuery();
81048 for (var i in langs) {
81049 var lang = langs[i];
81050 var valueObj = propObj[lang];
81051 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
81052 } // default to any available value
81055 return propObj[langKeys[0]].value;
81058 wiki.entityIDs = function (val) {
81059 if (!arguments.length) return _entityIDs;
81064 wiki.focus = function () {
81065 _searchInput.node().focus();
81068 return utilRebind(wiki, dispatch, 'on');
81071 function uiFieldWikipedia(field, context) {
81072 var _arguments = arguments;
81073 var dispatch = dispatch$8('change');
81074 var wikipedia = services.wikipedia;
81075 var wikidata = services.wikidata;
81077 var _langInput = select(null);
81079 var _titleInput = select(null);
81087 var _dataWikipedia = [];
81088 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
81089 _dataWikipedia = d;
81090 if (_tags) updateForTags(_tags);
81091 })["catch"](function () {
81094 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
81095 var v = value.toLowerCase();
81096 callback(_dataWikipedia.filter(function (d) {
81097 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
81098 }).map(function (d) {
81104 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
81108 for (var i in _entityIDs) {
81109 var entity = context.hasEntity(_entityIDs[i]);
81111 if (entity.tags.name) {
81112 value = entity.tags.name;
81118 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
81119 searchfn(language()[2], value, function (query, data) {
81120 callback(data.map(function (d) {
81128 function wiki(selection) {
81129 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
81130 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
81131 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
81132 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
81133 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
81134 _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);
81136 _langInput.on('blur', changeLang).on('change', changeLang);
81138 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
81139 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
81140 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
81141 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
81143 _titleInput.on('blur', function () {
81145 }).on('change', function () {
81149 var link = titleContainer.selectAll('.wiki-link').data([0]);
81150 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
81151 domain: 'wikipedia.org'
81152 })).call(svgIcon('#iD-icon-out-link')).merge(link);
81153 link.on('click', function (d3_event) {
81154 d3_event.preventDefault();
81155 if (_wikiURL) window.open(_wikiURL, '_blank');
81159 function defaultLanguageInfo(skipEnglishFallback) {
81160 var langCode = _mainLocalizer.languageCode().toLowerCase();
81162 for (var i in _dataWikipedia) {
81163 var d = _dataWikipedia[i]; // default to the language of iD's current locale
81165 if (d[2] === langCode) return d;
81166 } // fallback to English
81169 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
81172 function language(skipEnglishFallback) {
81173 var value = utilGetSetValue(_langInput).toLowerCase();
81175 for (var i in _dataWikipedia) {
81176 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
81178 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
81179 } // fallback to English
81182 return defaultLanguageInfo(skipEnglishFallback);
81185 function changeLang() {
81186 utilGetSetValue(_langInput, language()[1]);
81190 function change(skipWikidata) {
81191 var value = utilGetSetValue(_titleInput);
81192 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
81194 var langInfo = m && _dataWikipedia.find(function (d) {
81195 return m[1] === d[2];
81201 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
81203 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
81206 var anchor; // try {
81207 // leave this out for now - #6232
81208 // Best-effort `anchordecode:` implementation
81209 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
81212 anchor = decodeURIComponent(m[3]); // }
81214 value += '#' + anchor.replace(/_/g, ' ');
81217 value = value.slice(0, 1).toUpperCase() + value.slice(1);
81218 utilGetSetValue(_langInput, nativeLangName);
81219 utilGetSetValue(_titleInput, value);
81223 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
81225 syncTags.wikipedia = undefined;
81228 dispatch.call('change', this, syncTags);
81229 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
81231 var initGraph = context.graph();
81232 var initEntityIDs = _entityIDs;
81233 wikidata.itemsByTitle(language()[2], value, function (err, data) {
81234 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
81236 if (context.graph() !== initGraph) return;
81237 var qids = Object.keys(data);
81238 var value = qids && qids.find(function (id) {
81239 return id.match(/^Q\d+$/);
81241 var actions = initEntityIDs.map(function (entityID) {
81242 var entity = context.entity(entityID).tags;
81243 var currTags = Object.assign({}, entity); // shallow copy
81245 if (currTags.wikidata !== value) {
81246 currTags.wikidata = value;
81247 return actionChangeTags(entityID, currTags);
81251 }).filter(Boolean);
81252 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
81254 context.overwrite(function actionUpdateWikidataTags(graph) {
81255 actions.forEach(function (action) {
81256 graph = action(graph);
81259 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
81260 // changeTags() is not intended to be called asynchronously
81264 wiki.tags = function (tags) {
81266 updateForTags(tags);
81269 function updateForTags(tags) {
81270 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
81271 // optional suffix of `#anchor`
81273 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
81274 var tagLang = m && m[1];
81275 var tagArticleTitle = m && m[2];
81276 var anchor = m && m[3];
81278 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
81279 return tagLang === d[2];
81280 }); // value in correct format
81284 var nativeLangName = tagLangInfo[1];
81285 utilGetSetValue(_langInput, nativeLangName);
81286 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
81290 // Best-effort `anchorencode:` implementation
81291 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
81293 anchor = anchor.replace(/ /g, '_');
81297 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
81299 utilGetSetValue(_titleInput, value);
81301 if (value && value !== '') {
81302 utilGetSetValue(_langInput, '');
81303 var defaultLangInfo = defaultLanguageInfo();
81304 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
81306 var shownOrDefaultLangInfo = language(true
81307 /* skipEnglishFallback */
81309 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
81315 wiki.entityIDs = function (val) {
81316 if (!_arguments.length) return _entityIDs;
81321 wiki.focus = function () {
81322 _titleInput.node().focus();
81325 return utilRebind(wiki, dispatch, 'on');
81327 uiFieldWikipedia.supportsMultiselection = false;
81330 access: uiFieldAccess,
81331 address: uiFieldAddress,
81332 check: uiFieldCheck,
81333 combo: uiFieldCombo,
81334 cycleway: uiFieldCycleway,
81335 defaultCheck: uiFieldCheck,
81336 email: uiFieldText,
81337 identifier: uiFieldText,
81338 lanes: uiFieldLanes,
81339 localized: uiFieldLocalized,
81340 roadspeed: uiFieldRoadspeed,
81341 roadheight: uiFieldText,
81342 manyCombo: uiFieldCombo,
81343 multiCombo: uiFieldCombo,
81344 networkCombo: uiFieldCombo,
81345 number: uiFieldText,
81346 onewayCheck: uiFieldCheck,
81347 radio: uiFieldRadio,
81348 restrictions: uiFieldRestrictions,
81349 semiCombo: uiFieldCombo,
81350 structureRadio: uiFieldRadio,
81353 textarea: uiFieldTextarea,
81354 typeCombo: uiFieldCombo,
81356 wikidata: uiFieldWikidata,
81357 wikipedia: uiFieldWikipedia
81360 function uiField(context, presetField, entityIDs, options) {
81361 options = Object.assign({
81368 var dispatch = dispatch$8('change', 'revert');
81369 var field = Object.assign({}, presetField); // shallow copy
81371 field.domId = utilUniqueDomId('form-field-' + field.safeid);
81372 var _show = options.show;
81378 if (entityIDs && entityIDs.length) {
81379 _entityExtent = entityIDs.reduce(function (extent, entityID) {
81380 var entity = context.graph().entity(entityID);
81381 return extent.extend(entity.extent(context.graph()));
81385 var _locked = false;
81387 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
81389 })).placement('bottom');
81391 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
81393 if (_show && !field.impl) {
81395 } // Creates the field.. This is done lazily,
81396 // once we know that the field will be shown.
81399 function createField() {
81400 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
81401 dispatch.call('change', field, t, onInput);
81405 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
81407 if (field.impl.entityIDs) {
81408 field.impl.entityIDs(entityIDs);
81413 function isModified() {
81414 if (!entityIDs || !entityIDs.length) return false;
81415 return entityIDs.some(function (entityID) {
81416 var original = context.graph().base().entities[entityID];
81417 var latest = context.graph().entity(entityID);
81418 return field.keys.some(function (key) {
81419 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
81424 function tagsContainFieldKey() {
81425 return field.keys.some(function (key) {
81426 if (field.type === 'multiCombo') {
81427 for (var tagKey in _tags) {
81428 if (tagKey.indexOf(key) === 0) {
81436 return _tags[key] !== undefined;
81440 function revert(d3_event, d) {
81441 d3_event.stopPropagation();
81442 d3_event.preventDefault();
81443 if (!entityIDs || _locked) return;
81444 dispatch.call('revert', d, d.keys);
81447 function remove(d3_event, d) {
81448 d3_event.stopPropagation();
81449 d3_event.preventDefault();
81450 if (_locked) return;
81452 d.keys.forEach(function (key) {
81453 t[key] = undefined;
81455 dispatch.call('change', d, t);
81458 field.render = function (selection) {
81459 var container = selection.selectAll('.form-field').data([field]); // Enter
81461 var enter = container.enter().append('div').attr('class', function (d) {
81462 return 'form-field form-field-' + d.safeid;
81463 }).classed('nowrap', !options.wrap);
81465 if (options.wrap) {
81466 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
81469 var textEnter = labelEnter.append('span').attr('class', 'label-text');
81470 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
81473 textEnter.append('span').attr('class', 'label-textannotation');
81475 if (options.remove) {
81476 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
81479 if (options.revert) {
81480 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
81485 container = container.merge(enter);
81486 container.select('.field-label > .remove-icon') // propagate bound data
81487 .on('click', remove);
81488 container.select('.field-label > .modified-icon') // propagate bound data
81489 .on('click', revert);
81490 container.each(function (d) {
81491 var selection = select(this);
81497 var reference, help; // instantiate field help
81499 if (options.wrap && field.type === 'restrictions') {
81500 help = uiFieldHelp(context, 'restrictions');
81501 } // instantiate tag reference
81504 if (options.wrap && options.info) {
81505 var referenceKey = d.key || '';
81507 if (d.type === 'multiCombo') {
81508 // lookup key without the trailing ':'
81509 referenceKey = referenceKey.replace(/:$/, '');
81512 reference = uiTagReference(d.reference || {
81516 if (_state === 'hover') {
81517 reference.showing(false);
81521 selection.call(d.impl); // add field help components
81524 selection.call(help.body).select('.field-label').call(help.button);
81525 } // add tag reference components
81529 selection.call(reference.body).select('.field-label').call(reference.button);
81532 d.impl.tags(_tags);
81534 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
81536 var annotation = container.selectAll('.field-label .label-textannotation');
81537 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
81538 icon.exit().remove();
81539 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
81540 container.call(_locked ? _lockedTip : _lockedTip.destroy);
81543 field.state = function (val) {
81544 if (!arguments.length) return _state;
81549 field.tags = function (val) {
81550 if (!arguments.length) return _tags;
81553 if (tagsContainFieldKey() && !_show) {
81554 // always show a field if it has a value to display
81565 field.locked = function (val) {
81566 if (!arguments.length) return _locked;
81571 field.show = function () {
81578 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
81580 t[field.key] = field["default"];
81581 dispatch.call('change', this, t);
81583 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
81586 field.isShown = function () {
81588 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
81589 // A non-allowed field is hidden from the user altogether
81592 field.isAllowed = function () {
81593 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
81594 if (field.geometry && !entityIDs.every(function (entityID) {
81595 return field.matchGeometry(context.graph().geometry(entityID));
81598 if (entityIDs && _entityExtent && field.locationSetID) {
81599 // is field allowed in this location?
81600 var validLocations = _mainLocations.locationsAt(_entityExtent.center());
81601 if (!validLocations[field.locationSetID]) return false;
81604 var prerequisiteTag = field.prerequisiteTag;
81606 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
81608 if (!entityIDs.every(function (entityID) {
81609 var entity = context.graph().entity(entityID);
81611 if (prerequisiteTag.key) {
81612 var value = entity.tags[prerequisiteTag.key];
81613 if (!value) return false;
81615 if (prerequisiteTag.valueNot) {
81616 return prerequisiteTag.valueNot !== value;
81619 if (prerequisiteTag.value) {
81620 return prerequisiteTag.value === value;
81622 } else if (prerequisiteTag.keyNot) {
81623 if (entity.tags[prerequisiteTag.keyNot]) return false;
81633 field.focus = function () {
81635 field.impl.focus();
81639 return utilRebind(field, dispatch, 'on');
81642 function uiFormFields(context) {
81643 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
81644 var _fieldsArr = [];
81645 var _lastPlaceholder = '';
81649 function formFields(selection) {
81650 var allowedFields = _fieldsArr.filter(function (field) {
81651 return field.isAllowed();
81654 var shown = allowedFields.filter(function (field) {
81655 return field.isShown();
81657 var notShown = allowedFields.filter(function (field) {
81658 return !field.isShown();
81660 var container = selection.selectAll('.form-fields-container').data([0]);
81661 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
81662 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
81663 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
81665 fields.exit().remove(); // Enter
81667 var enter = fields.enter().append('div').attr('class', function (d) {
81668 return 'wrap-form-field wrap-form-field-' + d.safeid;
81671 fields = fields.merge(enter);
81672 fields.order().each(function (d) {
81673 select(this).call(d.render);
81676 var moreFields = notShown.map(function (field) {
81677 var title = field.title();
81678 titles.push(title);
81679 var terms = field.terms();
81680 if (field.key) terms.push(field.key);
81681 if (field.keys) terms = terms.concat(field.keys);
81683 display: field.label(),
81690 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
81691 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
81692 more.exit().remove();
81693 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
81694 moreEnter.append('span').html(_t.html('inspector.add_fields'));
81695 more = moreEnter.merge(more);
81696 var input = more.selectAll('.value').data([0]);
81697 input.exit().remove();
81698 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
81699 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
81700 if (!d) return; // user entered something that was not matched
81702 var field = d.field;
81704 selection.call(formFields); // rerender
81707 })); // avoid updating placeholder excessively (triggers style recalc)
81709 if (_lastPlaceholder !== placeholder) {
81710 input.attr('placeholder', placeholder);
81711 _lastPlaceholder = placeholder;
81715 formFields.fieldsArr = function (val) {
81716 if (!arguments.length) return _fieldsArr;
81717 _fieldsArr = val || [];
81721 formFields.state = function (val) {
81722 if (!arguments.length) return _state;
81727 formFields.klass = function (val) {
81728 if (!arguments.length) return _klass;
81736 function uiSectionPresetFields(context) {
81737 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
81738 var dispatch = dispatch$8('change', 'revert');
81739 var formFields = uiFormFields(context);
81751 function renderDisclosureContent(selection) {
81753 var graph = context.graph();
81754 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
81755 geoms[graph.entity(entityID).geometry(graph)] = true;
81758 var presetsManager = _mainPresetIndex;
81759 var allFields = [];
81760 var allMoreFields = [];
81761 var sharedTotalFields;
81763 _presets.forEach(function (preset) {
81764 var fields = preset.fields();
81765 var moreFields = preset.moreFields();
81766 allFields = utilArrayUnion(allFields, fields);
81767 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
81769 if (!sharedTotalFields) {
81770 sharedTotalFields = utilArrayUnion(fields, moreFields);
81772 sharedTotalFields = sharedTotalFields.filter(function (field) {
81773 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
81778 var sharedFields = allFields.filter(function (field) {
81779 return sharedTotalFields.indexOf(field) !== -1;
81781 var sharedMoreFields = allMoreFields.filter(function (field) {
81782 return sharedTotalFields.indexOf(field) !== -1;
81785 sharedFields.forEach(function (field) {
81786 if (field.matchAllGeometry(geometries)) {
81787 _fieldsArr.push(uiField(context, field, _entityIDs));
81790 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
81792 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
81793 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
81796 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
81797 additionalFields.sort(function (field1, field2) {
81798 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
81800 additionalFields.forEach(function (field) {
81801 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
81802 _fieldsArr.push(uiField(context, field, _entityIDs, {
81808 _fieldsArr.forEach(function (field) {
81809 field.on('change', function (t, onInput) {
81810 dispatch.call('change', field, _entityIDs, t, onInput);
81811 }).on('revert', function (keys) {
81812 dispatch.call('revert', field, keys);
81817 _fieldsArr.forEach(function (field) {
81818 field.state(_state).tags(_tags);
81821 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
81822 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
81823 // if user presses enter, and combobox is not active, accept edits..
81824 if (d3_event.keyCode === 13 && // ↩ Return
81825 context.container().select('.combobox').empty()) {
81826 context.enter(modeBrowse(context));
81831 section.presets = function (val) {
81832 if (!arguments.length) return _presets;
81834 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
81842 section.state = function (val) {
81843 if (!arguments.length) return _state;
81848 section.tags = function (val) {
81849 if (!arguments.length) return _tags;
81850 _tags = val; // Don't reset _fieldsArr here.
81855 section.entityIDs = function (val) {
81856 if (!arguments.length) return _entityIDs;
81858 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
81866 return utilRebind(section, dispatch, 'on');
81869 function uiSectionRawMemberEditor(context) {
81870 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
81871 if (!_entityIDs || _entityIDs.length !== 1) return false;
81872 var entity = context.hasEntity(_entityIDs[0]);
81873 return entity && entity.type === 'relation';
81874 }).label(function () {
81875 var entity = context.hasEntity(_entityIDs[0]);
81876 if (!entity) return '';
81877 var gt = entity.members.length > _maxMembers ? '>' : '';
81878 var count = gt + entity.members.slice(0, _maxMembers).length;
81879 return _t('inspector.title_count', {
81880 title: _t.html('inspector.members'),
81883 }).disclosureContent(renderDisclosureContent);
81884 var taginfo = services.taginfo;
81888 var _maxMembers = 1000;
81890 function downloadMember(d3_event, d) {
81891 d3_event.preventDefault(); // display the loading indicator
81893 select(this.parentNode).classed('tag-reference-loading', true);
81894 context.loadEntity(d.id, function () {
81895 section.reRender();
81899 function zoomToMember(d3_event, d) {
81900 d3_event.preventDefault();
81901 var entity = context.entity(d.id);
81902 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
81904 utilHighlightEntities([d.id], true, context);
81907 function selectMember(d3_event, d) {
81908 d3_event.preventDefault(); // remove the hover-highlight styling
81910 utilHighlightEntities([d.id], false, context);
81911 var entity = context.entity(d.id);
81912 var mapExtent = context.map().extent();
81914 if (!entity.intersects(mapExtent, context.graph())) {
81915 // zoom to the entity if its extent is not visible now
81916 context.map().zoomToEase(entity);
81919 context.enter(modeSelect(context, [d.id]));
81922 function changeRole(d3_event, d) {
81923 var oldRole = d.role;
81924 var newRole = context.cleanRelationRole(select(this).property('value'));
81926 if (oldRole !== newRole) {
81932 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
81935 context.validator().validate();
81939 function deleteMember(d3_event, d) {
81940 // remove the hover-highlight styling
81941 utilHighlightEntities([d.id], false, context);
81942 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
81946 if (!context.hasEntity(d.relation.id)) {
81947 // Removing the last member will also delete the relation.
81948 // If this happens we need to exit the selection mode
81949 context.enter(modeBrowse(context));
81951 // Changing the mode also runs `validate`, but otherwise we need to
81952 // rerun it manually
81953 context.validator().validate();
81957 function renderDisclosureContent(selection) {
81958 var entityID = _entityIDs[0];
81959 var memberships = [];
81960 var entity = context.entity(entityID);
81961 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
81968 member: context.hasEntity(member.id),
81969 domId: utilUniqueDomId(entityID + '-member-' + index)
81972 var list = selection.selectAll('.member-list').data([0]);
81973 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
81974 var items = list.selectAll('li').data(memberships, function (d) {
81975 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
81977 items.exit().each(unbind).remove();
81978 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
81981 itemsEnter.each(function (d) {
81982 var item = select(this);
81983 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
81986 // highlight the member feature in the map while hovering on the list item
81987 item.on('mouseover', function () {
81988 utilHighlightEntities([d.id], true, context);
81989 }).on('mouseout', function () {
81990 utilHighlightEntities([d.id], false, context);
81992 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
81993 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
81994 var matched = _mainPresetIndex.match(d.member, context.graph());
81995 return matched && matched.name() || utilDisplayType(d.member.id);
81997 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
81998 return utilDisplayName(d.member);
82000 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
82001 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
82003 var labelText = label.append('span').attr('class', 'label-text');
82004 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
82007 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
82010 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
82013 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82014 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
82016 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
82019 wrapEnter.each(bindTypeahead);
82023 items = items.merge(itemsEnter).order();
82024 items.select('input.member-role').property('value', function (d) {
82026 }).on('blur', changeRole).on('change', changeRole);
82027 items.select('button.member-delete').on('click', deleteMember);
82028 var dragOrigin, targetIndex;
82029 items.call(d3_drag().on('start', function (d3_event) {
82034 targetIndex = null;
82035 }).on('drag', function (d3_event) {
82036 var x = d3_event.x - dragOrigin.x,
82037 y = d3_event.y - dragOrigin.y;
82038 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
82039 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
82040 var index = items.nodes().indexOf(this);
82041 select(this).classed('dragging', true);
82042 targetIndex = null;
82043 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
82044 var node = select(this).node();
82046 if (index === index2) {
82047 return 'translate(' + x + 'px, ' + y + 'px)';
82048 } else if (index2 > index && d3_event.y > node.offsetTop) {
82049 if (targetIndex === null || index2 > targetIndex) {
82050 targetIndex = index2;
82053 return 'translateY(-100%)';
82054 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
82055 if (targetIndex === null || index2 < targetIndex) {
82056 targetIndex = index2;
82059 return 'translateY(100%)';
82064 }).on('end', function (d3_event, d) {
82065 if (!select(this).classed('dragging')) return;
82066 var index = items.nodes().indexOf(this);
82067 select(this).classed('dragging', false);
82068 selection.selectAll('li.member-row').style('transform', null);
82070 if (targetIndex !== null) {
82071 // dragged to a new position, reorder
82072 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
82073 context.validator().validate();
82077 function bindTypeahead(d) {
82078 var row = select(this);
82079 var role = row.selectAll('input.member-role');
82080 var origValue = role.property('value');
82082 function sort(value, data) {
82083 var sameletter = [];
82086 for (var i = 0; i < data.length; i++) {
82087 if (data[i].value.substring(0, value.length) === value) {
82088 sameletter.push(data[i]);
82090 other.push(data[i]);
82094 return sameletter.concat(other);
82097 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
82098 // The `geometry` param is used in the `taginfo.js` interface for
82099 // filtering results, as a key into the `tag_members_fractions`
82100 // object. If we don't know the geometry because the member is
82101 // not yet downloaded, it's ok to guess based on type.
82105 geometry = context.graph().geometry(d.member.id);
82106 } else if (d.type === 'relation') {
82107 geometry = 'relation';
82108 } else if (d.type === 'way') {
82111 geometry = 'point';
82114 var rtype = entity.tags.type;
82117 rtype: rtype || '',
82118 geometry: geometry,
82120 }, function (err, data) {
82121 if (!err) callback(sort(role, data));
82123 }).on('cancel', function () {
82124 role.property('value', origValue);
82128 function unbind() {
82129 var row = select(this);
82130 row.selectAll('input.member-role').call(uiCombobox.off, context);
82134 section.entityIDs = function (val) {
82135 if (!arguments.length) return _entityIDs;
82143 function actionDeleteMembers(relationId, memberIndexes) {
82144 return function (graph) {
82145 // Remove the members in descending order so removals won't shift what members
82146 // are at the remaining indexes
82147 memberIndexes.sort(function (a, b) {
82151 for (var i in memberIndexes) {
82152 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
82159 function uiSectionRawMembershipEditor(context) {
82160 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
82161 return _entityIDs && _entityIDs.length;
82162 }).label(function () {
82163 var parents = getSharedParentRelations();
82164 var gt = parents.length > _maxMemberships ? '>' : '';
82165 var count = gt + parents.slice(0, _maxMemberships).length;
82166 return _t('inspector.title_count', {
82167 title: _t.html('inspector.relations'),
82170 }).disclosureContent(renderDisclosureContent);
82171 var taginfo = services.taginfo;
82172 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
82173 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
82174 }).itemsMouseLeave(function (d3_event, d) {
82175 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
82177 var _inChange = false;
82178 var _entityIDs = [];
82182 var _maxMemberships = 1000;
82184 function getSharedParentRelations() {
82187 for (var i = 0; i < _entityIDs.length; i++) {
82188 var entity = context.graph().hasEntity(_entityIDs[i]);
82189 if (!entity) continue;
82192 parents = context.graph().parentRelations(entity);
82194 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
82197 if (!parents.length) break;
82203 function getMemberships() {
82204 var memberships = [];
82205 var relations = getSharedParentRelations().slice(0, _maxMemberships);
82206 var isMultiselect = _entityIDs.length > 1;
82207 var i, relation, membership, index, member, indexedMember;
82209 for (i = 0; i < relations.length; i++) {
82210 relation = relations[i];
82212 relation: relation,
82214 hash: osmEntity.key(relation)
82217 for (index = 0; index < relation.members.length; index++) {
82218 member = relation.members[index];
82220 if (_entityIDs.indexOf(member.id) !== -1) {
82221 indexedMember = Object.assign({}, member, {
82224 membership.members.push(indexedMember);
82225 membership.hash += ',' + index.toString();
82227 if (!isMultiselect) {
82228 // For single selections, list one entry per membership per relation.
82229 // For multiselections, list one entry per relation.
82230 memberships.push(membership);
82232 relation: relation,
82234 hash: osmEntity.key(relation)
82240 if (membership.members.length) memberships.push(membership);
82243 memberships.forEach(function (membership) {
82244 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
82246 membership.members.forEach(function (member) {
82247 if (roles.indexOf(member.role) === -1) roles.push(member.role);
82249 membership.role = roles.length === 1 ? roles[0] : roles;
82251 return memberships;
82254 function selectRelation(d3_event, d) {
82255 d3_event.preventDefault(); // remove the hover-highlight styling
82257 utilHighlightEntities([d.relation.id], false, context);
82258 context.enter(modeSelect(context, [d.relation.id]));
82261 function zoomToRelation(d3_event, d) {
82262 d3_event.preventDefault();
82263 var entity = context.entity(d.relation.id);
82264 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
82266 utilHighlightEntities([d.relation.id], true, context);
82269 function changeRole(d3_event, d) {
82270 if (d === 0) return; // called on newrow (shouldn't happen)
82272 if (_inChange) return; // avoid accidental recursive call #5731
82274 var newRole = context.cleanRelationRole(select(this).property('value'));
82275 if (!newRole.trim() && typeof d.role !== 'string') return;
82276 var membersToUpdate = d.members.filter(function (member) {
82277 return member.role !== newRole;
82280 if (membersToUpdate.length) {
82282 context.perform(function actionChangeMemberRoles(graph) {
82283 membersToUpdate.forEach(function (member) {
82284 var newMember = Object.assign({}, member, {
82287 delete newMember.index;
82288 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
82291 }, _t('operations.change_role.annotation', {
82292 n: membersToUpdate.length
82294 context.validator().validate();
82300 function addMembership(d, role) {
82301 this.blur(); // avoid keeping focus on the button
82303 _showBlank = false;
82305 function actionAddMembers(relationId, ids, role) {
82306 return function (graph) {
82307 for (var i in ids) {
82310 type: graph.entity(ids[i]).type,
82313 graph = actionAddMember(relationId, member)(graph);
82321 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
82322 n: _entityIDs.length
82324 context.validator().validate();
82326 var relation = osmRelation();
82327 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
82329 context.enter(modeSelect(context, [relation.id]).newFeature(true));
82333 function deleteMembership(d3_event, d) {
82334 this.blur(); // avoid keeping focus on the button
82336 if (d === 0) return; // called on newrow (shouldn't happen)
82337 // remove the hover-highlight styling
82339 utilHighlightEntities([d.relation.id], false, context);
82340 var indexes = d.members.map(function (member) {
82341 return member.index;
82343 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
82344 n: _entityIDs.length
82346 context.validator().validate();
82349 function fetchNearbyRelations(q, callback) {
82350 var newRelation = {
82352 value: _t('inspector.new_relation'),
82353 display: _t.html('inspector.new_relation')
82355 var entityID = _entityIDs[0];
82357 var graph = context.graph();
82359 function baseDisplayLabel(entity) {
82360 var matched = _mainPresetIndex.match(entity, graph);
82361 var presetName = matched && matched.name() || _t('inspector.relation');
82362 var entityName = utilDisplayName(entity) || '';
82363 return presetName + ' ' + entityName;
82366 var explicitRelation = q && context.hasEntity(q.toLowerCase());
82368 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
82369 // loaded relation is specified explicitly, only show that
82371 relation: explicitRelation,
82372 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
82375 context.history().intersects(context.map().extent()).forEach(function (entity) {
82376 if (entity.type !== 'relation' || entity.id === entityID) return;
82377 var value = baseDisplayLabel(entity);
82378 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
82384 result.sort(function (a, b) {
82385 return osmRelation.creationOrder(a.relation, b.relation);
82386 }); // Dedupe identical names by appending relation id - see #2891
82388 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
82389 return v.length > 1;
82391 dupeGroups.forEach(function (group) {
82392 group.forEach(function (obj) {
82393 obj.value += ' ' + obj.relation.id;
82398 result.forEach(function (obj) {
82399 obj.title = obj.value;
82401 result.unshift(newRelation);
82405 function renderDisclosureContent(selection) {
82406 var memberships = getMemberships();
82407 var list = selection.selectAll('.member-list').data([0]);
82408 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
82409 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
82412 items.exit().each(unbind).remove(); // Enter
82414 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
82416 itemsEnter.on('mouseover', function (d3_event, d) {
82417 utilHighlightEntities([d.relation.id], true, context);
82418 }).on('mouseout', function (d3_event, d) {
82419 utilHighlightEntities([d.relation.id], false, context);
82421 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
82424 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
82425 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
82426 var matched = _mainPresetIndex.match(d.relation, context.graph());
82427 return matched && matched.name() || _t('inspector.relation');
82429 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
82430 return utilDisplayName(d.relation);
82432 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
82433 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
82434 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82435 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
82437 }).property('type', 'text').property('value', function (d) {
82438 return typeof d.role === 'string' ? d.role : '';
82439 }).attr('title', function (d) {
82440 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
82441 }).attr('placeholder', function (d) {
82442 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
82443 }).classed('mixed', function (d) {
82444 return Array.isArray(d.role);
82445 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
82448 wrapEnter.each(bindTypeahead);
82451 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
82453 newMembership.exit().remove(); // Enter
82455 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
82456 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
82457 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
82458 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
82459 list.selectAll('.member-row-new').remove();
82461 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
82462 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
82464 newMembership = newMembership.merge(newMembershipEnter);
82465 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
82466 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
82468 var addRow = selection.selectAll('.add-row').data([0]); // enter
82470 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
82471 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
82472 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
82473 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
82474 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
82476 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
82479 addRow = addRow.merge(addRowEnter);
82480 addRow.select('.add-relation').on('click', function () {
82482 section.reRender();
82483 list.selectAll('.member-entity-input').node().focus();
82486 function acceptEntity(d) {
82490 } // remove hover-higlighting
82493 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
82494 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
82495 addMembership(d, role);
82498 function cancelEntity() {
82499 var input = newMembership.selectAll('.member-entity-input');
82500 input.property('value', ''); // remove hover-higlighting
82502 context.surface().selectAll('.highlighted').classed('highlighted', false);
82505 function bindTypeahead(d) {
82506 var row = select(this);
82507 var role = row.selectAll('input.member-role');
82508 var origValue = role.property('value');
82510 function sort(value, data) {
82511 var sameletter = [];
82514 for (var i = 0; i < data.length; i++) {
82515 if (data[i].value.substring(0, value.length) === value) {
82516 sameletter.push(data[i]);
82518 other.push(data[i]);
82522 return sameletter.concat(other);
82525 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
82526 var rtype = d.relation.tags.type;
82529 rtype: rtype || '',
82530 geometry: context.graph().geometry(_entityIDs[0]),
82532 }, function (err, data) {
82533 if (!err) callback(sort(role, data));
82535 }).on('cancel', function () {
82536 role.property('value', origValue);
82540 function unbind() {
82541 var row = select(this);
82542 row.selectAll('input.member-role').call(uiCombobox.off, context);
82546 section.entityIDs = function (val) {
82547 if (!arguments.length) return _entityIDs;
82549 _showBlank = false;
82556 function uiSectionSelectionList(context) {
82557 var _selectedIDs = [];
82558 var section = uiSection('selected-features', context).shouldDisplay(function () {
82559 return _selectedIDs.length > 1;
82560 }).label(function () {
82561 return _t('inspector.title_count', {
82562 title: _t.html('inspector.features'),
82563 count: _selectedIDs.length
82565 }).disclosureContent(renderDisclosureContent);
82566 context.history().on('change.selectionList', function (difference) {
82568 section.reRender();
82572 section.entityIDs = function (val) {
82573 if (!arguments.length) return _selectedIDs;
82574 _selectedIDs = val;
82578 function selectEntity(d3_event, entity) {
82579 context.enter(modeSelect(context, [entity.id]));
82582 function deselectEntity(d3_event, entity) {
82583 var selectedIDs = _selectedIDs.slice();
82585 var index = selectedIDs.indexOf(entity.id);
82588 selectedIDs.splice(index, 1);
82589 context.enter(modeSelect(context, selectedIDs));
82593 function renderDisclosureContent(selection) {
82594 var list = selection.selectAll('.feature-list').data([0]);
82595 list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
82597 var entities = _selectedIDs.map(function (id) {
82598 return context.hasEntity(id);
82599 }).filter(Boolean);
82601 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
82602 items.exit().remove(); // Enter
82604 var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
82605 select(this).on('mouseover', function () {
82606 utilHighlightEntities([d.id], true, context);
82607 }).on('mouseout', function () {
82608 utilHighlightEntities([d.id], false, context);
82611 var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
82612 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
82613 label.append('span').attr('class', 'entity-type');
82614 label.append('span').attr('class', 'entity-name');
82615 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
82617 items = items.merge(enter);
82618 items.selectAll('.entity-geom-icon use').attr('href', function () {
82619 var entity = this.parentNode.parentNode.__data__;
82620 return '#iD-icon-' + entity.geometry(context.graph());
82622 items.selectAll('.entity-type').html(function (entity) {
82623 return _mainPresetIndex.match(entity, context.graph()).name();
82625 items.selectAll('.entity-name').html(function (d) {
82626 // fetch latest entity
82627 var entity = context.entity(d.id);
82628 return utilDisplayName(entity);
82635 function uiEntityEditor(context) {
82636 var dispatch = dispatch$8('choose');
82637 var _state = 'select';
82638 var _coalesceChanges = false;
82639 var _modified = false;
82645 var _activePresets = [];
82651 function entityEditor(selection) {
82652 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
82654 var header = selection.selectAll('.header').data([0]); // Enter
82656 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
82657 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
82658 headerEnter.append('button').attr('class', 'close').on('click', function () {
82659 context.enter(modeBrowse(context));
82660 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
82661 headerEnter.append('h3'); // Update
82663 header = header.merge(headerEnter);
82664 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
82665 header.selectAll('.preset-reset').on('click', function () {
82666 dispatch.call('choose', this, _activePresets);
82669 var body = selection.selectAll('.inspector-body').data([0]); // Enter
82671 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
82673 body = body.merge(bodyEnter);
82676 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
82677 dispatch.call('choose', this, presets);
82678 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
82681 _sections.forEach(function (section) {
82682 if (section.entityIDs) {
82683 section.entityIDs(_entityIDs);
82686 if (section.presets) {
82687 section.presets(_activePresets);
82690 if (section.tags) {
82691 section.tags(combinedTags);
82694 if (section.state) {
82695 section.state(_state);
82698 body.call(section.render);
82701 context.history().on('change.entity-editor', historyChanged);
82703 function historyChanged(difference) {
82704 if (selection.selectAll('.entity-editor').empty()) return;
82705 if (_state === 'hide') return;
82706 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
82707 if (!significant) return;
82708 _entityIDs = _entityIDs.filter(context.hasEntity);
82709 if (!_entityIDs.length) return;
82710 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
82711 loadActivePresets();
82712 var graph = context.graph();
82713 entityEditor.modified(_base !== graph);
82714 entityEditor(selection);
82716 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
82717 // flash the button to indicate the preset changed
82718 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
82721 } // Tag changes that fire on input can all get coalesced into a single
82722 // history operation when the user leaves the field. #2342
82723 // Use explicit entityIDs in case the selection changes before the event is fired.
82726 function changeTags(entityIDs, changed, onInput) {
82729 for (var i in entityIDs) {
82730 var entityID = entityIDs[i];
82731 var entity = context.entity(entityID);
82732 var tags = Object.assign({}, entity.tags); // shallow copy
82734 for (var k in changed) {
82736 var v = changed[k];
82738 if (v !== undefined || tags.hasOwnProperty(k)) {
82744 tags = utilCleanTags(tags);
82747 if (!fastDeepEqual(entity.tags, tags)) {
82748 actions.push(actionChangeTags(entityID, tags));
82752 if (actions.length) {
82753 var combinedAction = function combinedAction(graph) {
82754 actions.forEach(function (action) {
82755 graph = action(graph);
82760 var annotation = _t('operations.change_tags.annotation');
82762 if (_coalesceChanges) {
82763 context.overwrite(combinedAction, annotation);
82765 context.perform(combinedAction, annotation);
82766 _coalesceChanges = !!onInput;
82768 } // if leaving field (blur event), rerun validation
82772 context.validator().validate();
82776 function revertTags(keys) {
82779 for (var i in _entityIDs) {
82780 var entityID = _entityIDs[i];
82781 var original = context.graph().base().entities[entityID];
82784 for (var j in keys) {
82786 changed[key] = original ? original.tags[key] : undefined;
82789 var entity = context.entity(entityID);
82790 var tags = Object.assign({}, entity.tags); // shallow copy
82792 for (var k in changed) {
82794 var v = changed[k];
82796 if (v !== undefined || tags.hasOwnProperty(k)) {
82801 tags = utilCleanTags(tags);
82803 if (!fastDeepEqual(entity.tags, tags)) {
82804 actions.push(actionChangeTags(entityID, tags));
82808 if (actions.length) {
82809 var combinedAction = function combinedAction(graph) {
82810 actions.forEach(function (action) {
82811 graph = action(graph);
82816 var annotation = _t('operations.change_tags.annotation');
82818 if (_coalesceChanges) {
82819 context.overwrite(combinedAction, annotation);
82821 context.perform(combinedAction, annotation);
82822 _coalesceChanges = false;
82826 context.validator().validate();
82829 entityEditor.modified = function (val) {
82830 if (!arguments.length) return _modified;
82832 return entityEditor;
82835 entityEditor.state = function (val) {
82836 if (!arguments.length) return _state;
82838 return entityEditor;
82841 entityEditor.entityIDs = function (val) {
82842 if (!arguments.length) return _entityIDs; // always reload these even if the entityIDs are unchanged, since we
82843 // could be reselecting after something like dragging a node
82845 _base = context.graph();
82846 _coalesceChanges = false;
82847 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
82850 loadActivePresets(true);
82851 return entityEditor.modified(false);
82854 entityEditor.newFeature = function (val) {
82855 if (!arguments.length) return _newFeature;
82857 return entityEditor;
82860 function loadActivePresets(isForNewSelection) {
82861 var graph = context.graph();
82864 for (var i in _entityIDs) {
82865 var entity = graph.hasEntity(_entityIDs[i]);
82866 if (!entity) return;
82867 var match = _mainPresetIndex.match(entity, graph);
82868 if (!counts[match.id]) counts[match.id] = 0;
82869 counts[match.id] += 1;
82872 var matches = Object.keys(counts).sort(function (p1, p2) {
82873 return counts[p2] - counts[p1];
82874 }).map(function (pID) {
82875 return _mainPresetIndex.item(pID);
82878 if (!isForNewSelection) {
82879 // A "weak" preset doesn't set any tags. (e.g. "Address")
82880 var weakPreset = _activePresets.length === 1 && !_activePresets[0].isFallback() && Object.keys(_activePresets[0].addTags || {}).length === 0; // Don't replace a weak preset with a fallback preset (e.g. "Point")
82882 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
82885 entityEditor.presets(matches);
82888 entityEditor.presets = function (val) {
82889 if (!arguments.length) return _activePresets; // don't reload the same preset
82891 if (!utilArrayIdentical(val, _activePresets)) {
82892 _activePresets = val;
82895 return entityEditor;
82898 return utilRebind(entityEditor, dispatch, 'on');
82901 function uiPresetList(context) {
82902 var dispatch = dispatch$8('cancel', 'choose');
82908 var _currentPresets;
82910 var _autofocus = false;
82912 function presetList(selection) {
82913 if (!_entityIDs) return;
82914 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
82915 selection.html('');
82916 var messagewrap = selection.append('div').attr('class', 'header fillL');
82917 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
82918 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
82919 dispatch.call('cancel', this);
82920 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
82922 function initialKeydown(d3_event) {
82923 // hack to let delete shortcut work when search is autofocused
82924 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
82925 d3_event.preventDefault();
82926 d3_event.stopPropagation();
82927 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
82928 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
82929 d3_event.preventDefault();
82930 d3_event.stopPropagation();
82932 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
82933 // don't check for delete/undo hack on future keydown events
82934 select(this).on('keydown', keydown);
82935 keydown.call(this, d3_event);
82939 function keydown(d3_event) {
82941 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
82942 search.node().selectionStart === search.property('value').length) {
82943 d3_event.preventDefault();
82944 d3_event.stopPropagation(); // move focus to the first item in the preset list
82946 var buttons = list.selectAll('.preset-list-button');
82947 if (!buttons.empty()) buttons.nodes()[0].focus();
82951 function keypress(d3_event) {
82953 var value = search.property('value');
82955 if (d3_event.keyCode === 13 && // ↩ Return
82957 list.selectAll('.preset-list-item:first-child').each(function (d) {
82958 d.choose.call(this);
82963 function inputevent() {
82964 var value = search.property('value');
82965 list.classed('filtered', value.length);
82966 var results, messageText;
82968 if (value.length) {
82969 results = presets.search(value, entityGeometries()[0], _currLoc);
82970 messageText = _t('inspector.results', {
82971 n: results.collection.length,
82975 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc);
82976 messageText = _t('inspector.choose');
82979 list.call(drawList, results);
82980 message.html(messageText);
82983 var searchWrap = selection.append('div').attr('class', 'search-header');
82984 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
82985 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);
82988 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
82989 // so try again on the next pass
82991 setTimeout(function () {
82992 search.node().focus();
82996 var listWrap = selection.append('div').attr('class', 'inspector-body');
82997 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro(), _currLoc));
82998 context.features().on('change.preset-list', updateForFeatureHiddenState);
83001 function drawList(list, presets) {
83002 presets = presets.matchAllGeometry(entityGeometries());
83003 var collection = presets.collection.reduce(function (collection, preset) {
83004 if (!preset) return collection;
83006 if (preset.members) {
83007 if (preset.members.collection.filter(function (preset) {
83008 return preset.addable();
83010 collection.push(CategoryItem(preset));
83012 } else if (preset.addable()) {
83013 collection.push(PresetItem(preset));
83018 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
83019 return d.preset.id;
83022 items.exit().remove();
83023 items.enter().append('div').attr('class', function (item) {
83024 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
83025 }).classed('current', function (item) {
83026 return _currentPresets.indexOf(item.preset) !== -1;
83027 }).each(function (item) {
83028 select(this).call(item);
83029 }).style('opacity', 0).transition().style('opacity', 1);
83030 updateForFeatureHiddenState();
83033 function itemKeydown(d3_event) {
83034 // the actively focused item
83035 var item = select(this.closest('.preset-list-item'));
83036 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
83038 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
83039 d3_event.preventDefault();
83040 d3_event.stopPropagation(); // the next item in the list at the same level
83042 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
83044 if (nextItem.empty()) {
83045 // if there is a parent item
83046 if (!parentItem.empty()) {
83047 // the item is the last item of a sublist,
83048 // select the next item at the parent level
83049 nextItem = select(parentItem.node().nextElementSibling);
83050 } // if the focused item is expanded
83052 } else if (select(this).classed('expanded')) {
83053 // select the first subitem instead
83054 nextItem = item.select('.subgrid .preset-list-item:first-child');
83057 if (!nextItem.empty()) {
83058 // focus on the next item
83059 nextItem.select('.preset-list-button').node().focus();
83060 } // arrow up, move focus to the previous, higher item
83062 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
83063 d3_event.preventDefault();
83064 d3_event.stopPropagation(); // the previous item in the list at the same level
83066 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
83068 if (previousItem.empty()) {
83069 // if there is a parent item
83070 if (!parentItem.empty()) {
83071 // the item is the first subitem of a sublist select the parent item
83072 previousItem = parentItem;
83073 } // if the previous item is expanded
83075 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
83076 // select the last subitem of the sublist of the previous item
83077 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
83080 if (!previousItem.empty()) {
83081 // focus on the previous item
83082 previousItem.select('.preset-list-button').node().focus();
83084 // the focus is at the top of the list, move focus back to the search field
83085 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
83086 search.node().focus();
83087 } // arrow left, move focus to the parent item if there is one
83089 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
83090 d3_event.preventDefault();
83091 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
83093 if (!parentItem.empty()) {
83094 parentItem.select('.preset-list-button').node().focus();
83095 } // arrow right, choose this item
83097 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
83098 d3_event.preventDefault();
83099 d3_event.stopPropagation();
83100 item.datum().choose.call(select(this).node());
83104 function CategoryItem(preset) {
83109 function item(selection) {
83110 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
83113 var isExpanded = select(this).classed('expanded');
83114 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
83115 select(this).classed('expanded', !isExpanded);
83116 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
83120 var geometries = entityGeometries();
83121 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 (d3_event) {
83122 // right arrow, expand the focused item
83123 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
83124 d3_event.preventDefault();
83125 d3_event.stopPropagation(); // if the item isn't expanded
83127 if (!select(this).classed('expanded')) {
83128 // toggle expansion (expand the item)
83129 click.call(this, d3_event);
83130 } // left arrow, collapse the focused item
83132 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
83133 d3_event.preventDefault();
83134 d3_event.stopPropagation(); // if the item is expanded
83136 if (select(this).classed('expanded')) {
83137 // toggle expansion (collapse the item)
83138 click.call(this, d3_event);
83141 itemKeydown.call(this, d3_event);
83144 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83145 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
83146 return preset.nameLabel() + '…';
83148 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
83149 box.append('div').attr('class', 'arrow');
83150 sublist = box.append('div').attr('class', 'preset-list fillL3');
83153 item.choose = function () {
83154 if (!box || !sublist) return;
83158 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
83161 var members = preset.members.matchAllGeometry(entityGeometries());
83162 sublist.call(drawList, members);
83163 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
83167 item.preset = preset;
83171 function PresetItem(preset) {
83172 function item(selection) {
83173 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
83174 var geometries = entityGeometries();
83175 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);
83176 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83177 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
83178 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
83181 wrap.call(item.reference.button);
83182 selection.call(item.reference.body);
83185 item.choose = function () {
83186 if (select(this).classed('disabled')) return;
83188 if (!context.inIntro()) {
83189 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
83192 context.perform(function (graph) {
83193 for (var i in _entityIDs) {
83194 var entityID = _entityIDs[i];
83195 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
83196 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
83200 }, _t('operations.change_tags.annotation'));
83201 context.validator().validate(); // rerun validation
83203 dispatch.call('choose', this, preset);
83206 item.help = function (d3_event) {
83207 d3_event.stopPropagation();
83208 item.reference.toggle();
83211 item.preset = preset;
83212 item.reference = uiTagReference(preset.reference());
83216 function updateForFeatureHiddenState() {
83217 if (!_entityIDs.every(context.hasEntity)) return;
83218 var geometries = entityGeometries();
83219 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
83221 button.call(uiTooltip().destroyAny);
83222 button.each(function (item, index) {
83223 var hiddenPresetFeaturesId;
83225 for (var i in geometries) {
83226 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
83227 if (hiddenPresetFeaturesId) break;
83230 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
83231 select(this).classed('disabled', isHiddenPreset);
83233 if (isHiddenPreset) {
83234 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
83235 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
83236 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
83237 })).placement(index < 2 ? 'bottom' : 'top'));
83242 presetList.autofocus = function (val) {
83243 if (!arguments.length) return _autofocus;
83248 presetList.entityIDs = function (val) {
83249 if (!arguments.length) return _entityIDs;
83253 if (_entityIDs && _entityIDs.length) {
83254 // calculate current location
83255 var extent = _entityIDs.reduce(function (extent, entityID) {
83256 var entity = context.graph().entity(entityID);
83257 return extent.extend(entity.extent(context.graph()));
83260 _currLoc = extent.center(); // match presets
83262 var presets = _entityIDs.map(function (entityID) {
83263 return _mainPresetIndex.match(context.entity(entityID), context.graph());
83266 presetList.presets(presets);
83272 presetList.presets = function (val) {
83273 if (!arguments.length) return _currentPresets;
83274 _currentPresets = val;
83278 function entityGeometries() {
83281 for (var i in _entityIDs) {
83282 var entityID = _entityIDs[i];
83283 var entity = context.entity(entityID);
83284 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
83286 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
83287 geometry = 'point';
83290 if (!counts[geometry]) counts[geometry] = 0;
83291 counts[geometry] += 1;
83294 return Object.keys(counts).sort(function (geom1, geom2) {
83295 return counts[geom2] - counts[geom1];
83299 return utilRebind(presetList, dispatch, 'on');
83302 function uiViewOnOSM(context) {
83303 var _what; // an osmEntity or osmNote
83306 function viewOnOSM(selection) {
83309 if (_what instanceof osmEntity) {
83310 url = context.connection().entityURL(_what);
83311 } else if (_what instanceof osmNote) {
83312 url = context.connection().noteURL(_what);
83315 var data = !_what || _what.isNew() ? [] : [_what];
83316 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
83320 link.exit().remove(); // enter
83322 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
83323 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
83326 viewOnOSM.what = function (_) {
83327 if (!arguments.length) return _what;
83335 function uiInspector(context) {
83336 var presetList = uiPresetList(context);
83337 var entityEditor = uiEntityEditor(context);
83338 var wrap = select(null),
83339 presetPane = select(null),
83340 editorPane = select(null);
83341 var _state = 'select';
83345 var _newFeature = false;
83347 function inspector(selection) {
83348 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
83349 inspector.setPreset();
83351 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
83352 wrap = selection.selectAll('.panewrap').data([0]);
83353 var enter = wrap.enter().append('div').attr('class', 'panewrap');
83354 enter.append('div').attr('class', 'preset-list-pane pane');
83355 enter.append('div').attr('class', 'entity-editor-pane pane');
83356 wrap = wrap.merge(enter);
83357 presetPane = wrap.selectAll('.preset-list-pane');
83358 editorPane = wrap.selectAll('.entity-editor-pane');
83360 function shouldDefaultToPresetList() {
83361 // always show the inspector on hover
83362 if (_state !== 'select') return false; // can only change preset on single selection
83364 if (_entityIDs.length !== 1) return false;
83365 var entityID = _entityIDs[0];
83366 var entity = context.hasEntity(entityID);
83367 if (!entity) return false; // default to inspector if there are already tags
83369 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
83371 if (_newFeature) return true; // all existing features except vertices should default to inspector
83373 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
83375 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
83377 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
83379 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
83384 if (shouldDefaultToPresetList()) {
83385 wrap.style('right', '-100%');
83386 editorPane.classed('hide', true);
83387 presetPane.classed('hide', false).call(presetList);
83389 wrap.style('right', '0%');
83390 presetPane.classed('hide', true);
83391 editorPane.classed('hide', false).call(entityEditor);
83394 var footer = selection.selectAll('.footer').data([0]);
83395 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
83396 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
83399 inspector.showList = function (presets) {
83400 presetPane.classed('hide', false);
83401 wrap.transition().styleTween('right', function () {
83402 return interpolate$1('0%', '-100%');
83403 }).on('end', function () {
83404 editorPane.classed('hide', true);
83408 presetList.presets(presets);
83411 presetPane.call(presetList.autofocus(true));
83414 inspector.setPreset = function (preset) {
83415 // upon setting multipolygon, go to the area preset list instead of the editor
83416 if (preset && preset.id === 'type/multipolygon') {
83417 presetPane.call(presetList.autofocus(true));
83419 editorPane.classed('hide', false);
83420 wrap.transition().styleTween('right', function () {
83421 return interpolate$1('-100%', '0%');
83422 }).on('end', function () {
83423 presetPane.classed('hide', true);
83427 entityEditor.presets([preset]);
83430 editorPane.call(entityEditor);
83434 inspector.state = function (val) {
83435 if (!arguments.length) return _state;
83437 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
83439 context.container().selectAll('.field-help-body').remove();
83443 inspector.entityIDs = function (val) {
83444 if (!arguments.length) return _entityIDs;
83449 inspector.newFeature = function (val) {
83450 if (!arguments.length) return _newFeature;
83458 function uiImproveOsmComments() {
83461 function issueComments(selection) {
83462 // make the div immediately so it appears above the buttons
83463 var comments = selection.selectAll('.comments-container').data([0]);
83464 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
83466 services.improveOSM.getComments(_qaItem).then(function (d) {
83467 if (!d.comments) return; // nothing to do here
83469 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
83470 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
83471 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
83472 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
83473 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
83474 var osm = services.osm;
83475 var selection = select(this);
83477 if (osm && d.username) {
83478 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
83481 selection.html(function (d) {
83485 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
83486 return _t.html('note.status.commented', {
83487 when: localeDateString(d.timestamp)
83490 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
83493 })["catch"](function (err) {
83494 console.log(err); // eslint-disable-line no-console
83498 function localeDateString(s) {
83499 if (!s) return null;
83505 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
83507 if (isNaN(d.getTime())) return null;
83508 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
83511 issueComments.issue = function (val) {
83512 if (!arguments.length) return _qaItem;
83514 return issueComments;
83517 return issueComments;
83520 function uiImproveOsmDetails(context) {
83523 function issueDetail(d) {
83524 if (d.desc) return d.desc;
83525 var issueKey = d.issueKey;
83526 d.replacements = d.replacements || {};
83527 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
83529 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
83532 function improveOsmDetails(selection) {
83533 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
83534 return "".concat(d.id, "-").concat(d.status || 0);
83536 details.exit().remove();
83537 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
83539 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
83540 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
83541 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
83543 var relatedEntities = [];
83544 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
83545 var link = select(this);
83546 var isObjectLink = link.classed('error_object_link');
83547 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
83548 var entity = context.hasEntity(entityID);
83549 relatedEntities.push(entityID); // Add click handler
83551 link.on('mouseenter', function () {
83552 utilHighlightEntities([entityID], true, context);
83553 }).on('mouseleave', function () {
83554 utilHighlightEntities([entityID], false, context);
83555 }).on('click', function (d3_event) {
83556 d3_event.preventDefault();
83557 utilHighlightEntities([entityID], false, context);
83558 var osmlayer = context.layers().layer('osm');
83560 if (!osmlayer.enabled()) {
83561 osmlayer.enabled(true);
83564 context.map().centerZoom(_qaItem.loc, 20);
83567 context.enter(modeSelect(context, [entityID]));
83569 context.loadEntity(entityID, function (err, result) {
83571 var entity = result.data.find(function (e) {
83572 return e.id === entityID;
83574 if (entity) context.enter(modeSelect(context, [entityID]));
83577 }); // Replace with friendly name if possible
83578 // (The entity may not yet be loaded into the graph)
83581 var name = utilDisplayName(entity); // try to use common name
83583 if (!name && !isObjectLink) {
83584 var preset = _mainPresetIndex.match(entity, context.graph());
83585 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
83589 this.innerText = name;
83592 }); // Don't hide entities related to this error - #5880
83594 context.features().forceVisible(relatedEntities);
83595 context.map().pan([0, 0]); // trigger a redraw
83598 improveOsmDetails.issue = function (val) {
83599 if (!arguments.length) return _qaItem;
83601 return improveOsmDetails;
83604 return improveOsmDetails;
83607 function uiImproveOsmHeader() {
83610 function issueTitle(d) {
83611 var issueKey = d.issueKey;
83612 d.replacements = d.replacements || {};
83613 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
83615 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
83618 function improveOsmHeader(selection) {
83619 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
83620 return "".concat(d.id, "-").concat(d.status || 0);
83622 header.exit().remove();
83623 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
83624 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
83626 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
83627 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
83629 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');
83630 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
83631 var picon = d.icon;
83636 var isMaki = /^maki-/.test(picon);
83637 return "#".concat(picon).concat(isMaki ? '-11' : '');
83640 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
83643 improveOsmHeader.issue = function (val) {
83644 if (!arguments.length) return _qaItem;
83646 return improveOsmHeader;
83649 return improveOsmHeader;
83652 function uiImproveOsmEditor(context) {
83653 var dispatch = dispatch$8('change');
83654 var qaDetails = uiImproveOsmDetails(context);
83655 var qaComments = uiImproveOsmComments();
83656 var qaHeader = uiImproveOsmHeader();
83660 function improveOsmEditor(selection) {
83661 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
83662 headerEnter.append('button').attr('class', 'close').on('click', function () {
83663 return context.enter(modeBrowse(context));
83664 }).call(svgIcon('#iD-icon-close'));
83665 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
83666 var body = selection.selectAll('.body').data([0]);
83667 body = body.enter().append('div').attr('class', 'body').merge(body);
83668 var editor = body.selectAll('.qa-editor').data([0]);
83669 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);
83672 function improveOsmSaveSection(selection) {
83673 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83675 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
83676 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
83677 return "".concat(d.id, "-").concat(d.status || 0);
83680 saveSection.exit().remove(); // enter
83682 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
83683 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
83684 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
83685 return d.newComment;
83686 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
83688 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
83690 function changeInput() {
83691 var input = select(this);
83692 var val = input.property('value').trim();
83696 } // store the unsaved comment with the issue itself
83699 _qaItem = _qaItem.update({
83702 var qaService = services.improveOSM;
83705 qaService.replaceItem(_qaItem);
83708 saveSection.call(qaSaveButtons);
83712 function qaSaveButtons(selection) {
83713 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83715 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
83716 return d.status + d.id;
83719 buttonSection.exit().remove(); // enter
83721 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
83722 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
83723 buttonEnter.append('button').attr('class', 'button close-button action');
83724 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
83726 buttonSection = buttonSection.merge(buttonEnter);
83727 buttonSection.select('.comment-button').attr('disabled', function (d) {
83728 return d.newComment ? null : true;
83729 }).on('click.comment', function (d3_event, d) {
83730 this.blur(); // avoid keeping focus on the button - #4641
83732 var qaService = services.improveOSM;
83735 qaService.postUpdate(d, function (err, item) {
83736 return dispatch.call('change', item);
83740 buttonSection.select('.close-button').html(function (d) {
83741 var andComment = d.newComment ? '_comment' : '';
83742 return _t.html("QA.keepRight.close".concat(andComment));
83743 }).on('click.close', function (d3_event, d) {
83744 this.blur(); // avoid keeping focus on the button - #4641
83746 var qaService = services.improveOSM;
83749 d.newStatus = 'SOLVED';
83750 qaService.postUpdate(d, function (err, item) {
83751 return dispatch.call('change', item);
83755 buttonSection.select('.ignore-button').html(function (d) {
83756 var andComment = d.newComment ? '_comment' : '';
83757 return _t.html("QA.keepRight.ignore".concat(andComment));
83758 }).on('click.ignore', function (d3_event, d) {
83759 this.blur(); // avoid keeping focus on the button - #4641
83761 var qaService = services.improveOSM;
83764 d.newStatus = 'INVALID';
83765 qaService.postUpdate(d, function (err, item) {
83766 return dispatch.call('change', item);
83770 } // NOTE: Don't change method name until UI v3 is merged
83773 improveOsmEditor.error = function (val) {
83774 if (!arguments.length) return _qaItem;
83776 return improveOsmEditor;
83779 return utilRebind(improveOsmEditor, dispatch, 'on');
83782 function uiKeepRightDetails(context) {
83785 function issueDetail(d) {
83786 var itemType = d.itemType,
83787 parentIssueType = d.parentIssueType;
83788 var unknown = _t.html('inspector.unknown');
83789 var replacements = d.replacements || {};
83790 replacements["default"] = unknown; // special key `default` works as a fallback string
83792 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
83794 if (detail === unknown) {
83795 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
83801 function keepRightDetails(selection) {
83802 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
83803 return "".concat(d.id, "-").concat(d.status || 0);
83805 details.exit().remove();
83806 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
83808 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
83809 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
83810 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
83812 var relatedEntities = [];
83813 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
83814 var link = select(this);
83815 var isObjectLink = link.classed('error_object_link');
83816 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
83817 var entity = context.hasEntity(entityID);
83818 relatedEntities.push(entityID); // Add click handler
83820 link.on('mouseenter', function () {
83821 utilHighlightEntities([entityID], true, context);
83822 }).on('mouseleave', function () {
83823 utilHighlightEntities([entityID], false, context);
83824 }).on('click', function (d3_event) {
83825 d3_event.preventDefault();
83826 utilHighlightEntities([entityID], false, context);
83827 var osmlayer = context.layers().layer('osm');
83829 if (!osmlayer.enabled()) {
83830 osmlayer.enabled(true);
83833 context.map().centerZoomEase(_qaItem.loc, 20);
83836 context.enter(modeSelect(context, [entityID]));
83838 context.loadEntity(entityID, function (err, result) {
83840 var entity = result.data.find(function (e) {
83841 return e.id === entityID;
83843 if (entity) context.enter(modeSelect(context, [entityID]));
83846 }); // Replace with friendly name if possible
83847 // (The entity may not yet be loaded into the graph)
83850 var name = utilDisplayName(entity); // try to use common name
83852 if (!name && !isObjectLink) {
83853 var preset = _mainPresetIndex.match(entity, context.graph());
83854 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
83858 this.innerText = name;
83861 }); // Don't hide entities related to this issue - #5880
83863 context.features().forceVisible(relatedEntities);
83864 context.map().pan([0, 0]); // trigger a redraw
83867 keepRightDetails.issue = function (val) {
83868 if (!arguments.length) return _qaItem;
83870 return keepRightDetails;
83873 return keepRightDetails;
83876 function uiKeepRightHeader() {
83879 function issueTitle(d) {
83880 var itemType = d.itemType,
83881 parentIssueType = d.parentIssueType;
83882 var unknown = _t.html('inspector.unknown');
83883 var replacements = d.replacements || {};
83884 replacements["default"] = unknown; // special key `default` works as a fallback string
83886 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
83888 if (title === unknown) {
83889 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
83895 function keepRightHeader(selection) {
83896 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
83897 return "".concat(d.id, "-").concat(d.status || 0);
83899 header.exit().remove();
83900 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
83901 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
83904 iconEnter.append('div').attr('class', function (d) {
83905 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
83906 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
83907 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
83910 keepRightHeader.issue = function (val) {
83911 if (!arguments.length) return _qaItem;
83913 return keepRightHeader;
83916 return keepRightHeader;
83919 function uiViewOnKeepRight() {
83922 function viewOnKeepRight(selection) {
83925 if (services.keepRight && _qaItem instanceof QAItem) {
83926 url = services.keepRight.issueURL(_qaItem);
83929 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
83931 link.exit().remove(); // enter
83933 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
83934 .attr('href', function (d) {
83936 }).call(svgIcon('#iD-icon-out-link', 'inline'));
83937 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
83940 viewOnKeepRight.what = function (val) {
83941 if (!arguments.length) return _qaItem;
83943 return viewOnKeepRight;
83946 return viewOnKeepRight;
83949 function uiKeepRightEditor(context) {
83950 var dispatch = dispatch$8('change');
83951 var qaDetails = uiKeepRightDetails(context);
83952 var qaHeader = uiKeepRightHeader();
83956 function keepRightEditor(selection) {
83957 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
83958 headerEnter.append('button').attr('class', 'close').on('click', function () {
83959 return context.enter(modeBrowse(context));
83960 }).call(svgIcon('#iD-icon-close'));
83961 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
83962 var body = selection.selectAll('.body').data([0]);
83963 body = body.enter().append('div').attr('class', 'body').merge(body);
83964 var editor = body.selectAll('.qa-editor').data([0]);
83965 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
83966 var footer = selection.selectAll('.footer').data([0]);
83967 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
83970 function keepRightSaveSection(selection) {
83971 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
83973 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
83974 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
83975 return "".concat(d.id, "-").concat(d.status || 0);
83978 saveSection.exit().remove(); // enter
83980 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
83981 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
83982 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
83983 return d.newComment || d.comment;
83984 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
83986 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
83988 function changeInput() {
83989 var input = select(this);
83990 var val = input.property('value').trim();
83992 if (val === _qaItem.comment) {
83994 } // store the unsaved comment with the issue itself
83997 _qaItem = _qaItem.update({
84000 var qaService = services.keepRight;
84003 qaService.replaceItem(_qaItem); // update keepright cache
84006 saveSection.call(qaSaveButtons);
84010 function qaSaveButtons(selection) {
84011 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84013 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
84014 return d.status + d.id;
84017 buttonSection.exit().remove(); // enter
84019 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84020 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
84021 buttonEnter.append('button').attr('class', 'button close-button action');
84022 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
84024 buttonSection = buttonSection.merge(buttonEnter);
84025 buttonSection.select('.comment-button') // select and propagate data
84026 .attr('disabled', function (d) {
84027 return d.newComment ? null : true;
84028 }).on('click.comment', function (d3_event, d) {
84029 this.blur(); // avoid keeping focus on the button - #4641
84031 var qaService = services.keepRight;
84034 qaService.postUpdate(d, function (err, item) {
84035 return dispatch.call('change', item);
84039 buttonSection.select('.close-button') // select and propagate data
84040 .html(function (d) {
84041 var andComment = d.newComment ? '_comment' : '';
84042 return _t.html("QA.keepRight.close".concat(andComment));
84043 }).on('click.close', function (d3_event, d) {
84044 this.blur(); // avoid keeping focus on the button - #4641
84046 var qaService = services.keepRight;
84049 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
84051 qaService.postUpdate(d, function (err, item) {
84052 return dispatch.call('change', item);
84056 buttonSection.select('.ignore-button') // select and propagate data
84057 .html(function (d) {
84058 var andComment = d.newComment ? '_comment' : '';
84059 return _t.html("QA.keepRight.ignore".concat(andComment));
84060 }).on('click.ignore', function (d3_event, d) {
84061 this.blur(); // avoid keeping focus on the button - #4641
84063 var qaService = services.keepRight;
84066 d.newStatus = 'ignore'; // ignore permanently (false positive)
84068 qaService.postUpdate(d, function (err, item) {
84069 return dispatch.call('change', item);
84073 } // NOTE: Don't change method name until UI v3 is merged
84076 keepRightEditor.error = function (val) {
84077 if (!arguments.length) return _qaItem;
84079 return keepRightEditor;
84082 return utilRebind(keepRightEditor, dispatch, 'on');
84085 function uiOsmoseDetails(context) {
84088 function issueString(d, type) {
84089 if (!d) return ''; // Issue strings are cached from Osmose API
84091 var s = services.osmose.getStrings(d.itemType);
84092 return type in s ? s[type] : '';
84095 function osmoseDetails(selection) {
84096 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
84097 return "".concat(d.id, "-").concat(d.status || 0);
84099 details.exit().remove();
84100 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
84102 if (issueString(_qaItem, 'detail')) {
84103 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84104 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
84105 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
84106 return issueString(d, 'detail');
84107 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84108 } // Elements (populated later as data is requested)
84111 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84112 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
84114 if (issueString(_qaItem, 'fix')) {
84115 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84117 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
84119 _div.append('p').html(function (d) {
84120 return issueString(d, 'fix');
84121 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84122 } // Common Pitfalls (mustn't exist for every issue type)
84125 if (issueString(_qaItem, 'trap')) {
84126 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
84128 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
84130 _div2.append('p').html(function (d) {
84131 return issueString(d, 'trap');
84132 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84133 } // Save current item to check if UI changed by time request resolves
84136 var thisItem = _qaItem;
84137 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
84138 // No details to add if there are no associated issue elements
84139 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
84141 if (context.selectedErrorID() !== thisItem.id && context.container().selectAll(".qaItem.osmose.hover.itemId-".concat(thisItem.id)).empty()) return; // Things like keys and values are dynamically added to a subtitle string
84144 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
84145 detailsDiv.append('p').html(function (d) {
84147 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
84148 } // Create list of linked issue elements
84151 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
84152 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
84154 }).each(function () {
84155 var link = select(this);
84156 var entityID = this.textContent;
84157 var entity = context.hasEntity(entityID); // Add click handler
84159 link.on('mouseenter', function () {
84160 utilHighlightEntities([entityID], true, context);
84161 }).on('mouseleave', function () {
84162 utilHighlightEntities([entityID], false, context);
84163 }).on('click', function (d3_event) {
84164 d3_event.preventDefault();
84165 utilHighlightEntities([entityID], false, context);
84166 var osmlayer = context.layers().layer('osm');
84168 if (!osmlayer.enabled()) {
84169 osmlayer.enabled(true);
84172 context.map().centerZoom(d.loc, 20);
84175 context.enter(modeSelect(context, [entityID]));
84177 context.loadEntity(entityID, function (err, result) {
84179 var entity = result.data.find(function (e) {
84180 return e.id === entityID;
84182 if (entity) context.enter(modeSelect(context, [entityID]));
84185 }); // Replace with friendly name if possible
84186 // (The entity may not yet be loaded into the graph)
84189 var name = utilDisplayName(entity); // try to use common name
84192 var preset = _mainPresetIndex.match(entity, context.graph());
84193 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
84197 this.innerText = name;
84200 }); // Don't hide entities related to this issue - #5880
84202 context.features().forceVisible(d.elems);
84203 context.map().pan([0, 0]); // trigger a redraw
84204 })["catch"](function (err) {
84205 console.log(err); // eslint-disable-line no-console
84209 osmoseDetails.issue = function (val) {
84210 if (!arguments.length) return _qaItem;
84212 return osmoseDetails;
84215 return osmoseDetails;
84218 function uiOsmoseHeader() {
84221 function issueTitle(d) {
84222 var unknown = _t('inspector.unknown');
84223 if (!d) return unknown; // Issue titles supplied by Osmose
84225 var s = services.osmose.getStrings(d.itemType);
84226 return 'title' in s ? s.title : unknown;
84229 function osmoseHeader(selection) {
84230 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
84231 return "".concat(d.id, "-").concat(d.status || 0);
84233 header.exit().remove();
84234 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
84235 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
84237 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
84238 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
84240 svgEnter.append('polygon').attr('fill', function (d) {
84241 return services.osmose.getColor(d.item);
84242 }).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');
84243 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
84244 var picon = d.icon;
84249 var isMaki = /^maki-/.test(picon);
84250 return "#".concat(picon).concat(isMaki ? '-11' : '');
84253 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
84256 osmoseHeader.issue = function (val) {
84257 if (!arguments.length) return _qaItem;
84259 return osmoseHeader;
84262 return osmoseHeader;
84265 function uiViewOnOsmose() {
84268 function viewOnOsmose(selection) {
84271 if (services.osmose && _qaItem instanceof QAItem) {
84272 url = services.osmose.itemURL(_qaItem);
84275 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
84277 link.exit().remove(); // enter
84279 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
84280 .attr('href', function (d) {
84282 }).call(svgIcon('#iD-icon-out-link', 'inline'));
84283 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
84286 viewOnOsmose.what = function (val) {
84287 if (!arguments.length) return _qaItem;
84289 return viewOnOsmose;
84292 return viewOnOsmose;
84295 function uiOsmoseEditor(context) {
84296 var dispatch = dispatch$8('change');
84297 var qaDetails = uiOsmoseDetails(context);
84298 var qaHeader = uiOsmoseHeader();
84302 function osmoseEditor(selection) {
84303 var header = selection.selectAll('.header').data([0]);
84304 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
84305 headerEnter.append('button').attr('class', 'close').on('click', function () {
84306 return context.enter(modeBrowse(context));
84307 }).call(svgIcon('#iD-icon-close'));
84308 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
84309 var body = selection.selectAll('.body').data([0]);
84310 body = body.enter().append('div').attr('class', 'body').merge(body);
84311 var editor = body.selectAll('.qa-editor').data([0]);
84312 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
84313 var footer = selection.selectAll('.footer').data([0]);
84314 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
84317 function osmoseSaveSection(selection) {
84318 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84320 var isShown = _qaItem && isSelected;
84321 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
84322 return "".concat(d.id, "-").concat(d.status || 0);
84325 saveSection.exit().remove(); // enter
84327 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
84329 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
84332 function qaSaveButtons(selection) {
84333 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
84335 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
84336 return d.status + d.id;
84339 buttonSection.exit().remove(); // enter
84341 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84342 buttonEnter.append('button').attr('class', 'button close-button action');
84343 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
84345 buttonSection = buttonSection.merge(buttonEnter);
84346 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
84347 this.blur(); // avoid keeping focus on the button - #4641
84349 var qaService = services.osmose;
84352 d.newStatus = 'done';
84353 qaService.postUpdate(d, function (err, item) {
84354 return dispatch.call('change', item);
84358 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
84359 this.blur(); // avoid keeping focus on the button - #4641
84361 var qaService = services.osmose;
84364 d.newStatus = 'false';
84365 qaService.postUpdate(d, function (err, item) {
84366 return dispatch.call('change', item);
84370 } // NOTE: Don't change method name until UI v3 is merged
84373 osmoseEditor.error = function (val) {
84374 if (!arguments.length) return _qaItem;
84376 return osmoseEditor;
84379 return utilRebind(osmoseEditor, dispatch, 'on');
84382 function uiNoteComments() {
84385 function noteComments(selection) {
84386 if (_note.isNew()) return; // don't draw .comments-container
84388 var comments = selection.selectAll('.comments-container').data([0]);
84389 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
84390 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
84391 commentEnter.append('div').attr('class', function (d) {
84392 return 'comment-avatar user-' + d.uid;
84393 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
84394 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
84395 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
84396 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
84397 var selection = select(this);
84398 var osm = services.osm;
84400 if (osm && d.user) {
84401 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
84404 selection.html(function (d) {
84405 return d.user || _t.html('note.anonymous');
84408 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
84409 return _t('note.status.' + d.action, {
84410 when: localeDateString(d.date)
84413 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
84415 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
84416 comments.call(replaceAvatars);
84419 function replaceAvatars(selection) {
84420 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
84421 var osm = services.osm;
84422 if (showThirdPartyIcons !== 'true' || !osm) return;
84423 var uids = {}; // gather uids in the comment thread
84425 _note.comments.forEach(function (d) {
84426 if (d.uid) uids[d.uid] = true;
84429 Object.keys(uids).forEach(function (uid) {
84430 osm.loadUser(uid, function (err, user) {
84431 if (!user || !user.image_url) return;
84432 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);
84437 function localeDateString(s) {
84438 if (!s) return null;
84444 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
84446 var d = new Date(s);
84447 if (isNaN(d.getTime())) return null;
84448 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
84451 noteComments.note = function (val) {
84452 if (!arguments.length) return _note;
84454 return noteComments;
84457 return noteComments;
84460 function uiNoteHeader() {
84463 function noteHeader(selection) {
84464 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
84465 return d.status + d.id;
84467 header.exit().remove();
84468 var headerEnter = header.enter().append('div').attr('class', 'note-header');
84469 var iconEnter = headerEnter.append('div').attr('class', function (d) {
84470 return 'note-header-icon ' + d.status;
84471 }).classed('new', function (d) {
84474 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
84475 iconEnter.each(function (d) {
84479 statusIcon = '#iD-icon-plus';
84480 } else if (d.status === 'open') {
84481 statusIcon = '#iD-icon-close';
84483 statusIcon = '#iD-icon-apply';
84486 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
84488 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
84489 if (_note.isNew()) {
84490 return _t('note.new');
84493 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
84497 noteHeader.note = function (val) {
84498 if (!arguments.length) return _note;
84506 function uiNoteReport() {
84509 function noteReport(selection) {
84512 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
84513 url = services.osm.noteReportURL(_note);
84516 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
84518 link.exit().remove(); // enter
84520 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
84522 }).call(svgIcon('#iD-icon-out-link', 'inline'));
84523 linkEnter.append('span').html(_t.html('note.report'));
84526 noteReport.note = function (val) {
84527 if (!arguments.length) return _note;
84535 function uiNoteEditor(context) {
84536 var dispatch = dispatch$8('change');
84537 var noteComments = uiNoteComments();
84538 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
84542 var _newNote; // var _fieldsArr;
84545 function noteEditor(selection) {
84546 var header = selection.selectAll('.header').data([0]);
84547 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
84548 headerEnter.append('button').attr('class', 'close').on('click', function () {
84549 context.enter(modeBrowse(context));
84550 }).call(svgIcon('#iD-icon-close'));
84551 headerEnter.append('h3').html(_t.html('note.title'));
84552 var body = selection.selectAll('.body').data([0]);
84553 body = body.enter().append('div').attr('class', 'body').merge(body);
84554 var editor = body.selectAll('.note-editor').data([0]);
84555 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
84556 var footer = selection.selectAll('.footer').data([0]);
84557 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
84559 var osm = services.osm;
84562 osm.on('change.note-save', function () {
84563 selection.call(noteEditor);
84568 function noteSaveSection(selection) {
84569 var isSelected = _note && _note.id === context.selectedNoteID();
84571 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
84572 return d.status + d.id;
84575 noteSave.exit().remove(); // enter
84577 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
84578 // if (_note.isNew()) {
84579 // var presets = presetManager;
84580 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
84582 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
84584 // _fieldsArr.forEach(function(field) {
84586 // .on('change', changeCategory);
84590 // .attr('class', 'note-category')
84591 // .call(formFields.fieldsArr(_fieldsArr));
84593 // function changeCategory() {
84594 // // NOTE: perhaps there is a better way to get value
84595 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
84596 // // store the unsaved category with the note itself
84597 // _note = _note.update({ newCategory: val });
84598 // var osm = services.osm;
84600 // osm.replaceNote(_note); // update note cache
84603 // .call(noteSaveButtons);
84606 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
84607 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
84609 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
84610 return d.newComment;
84611 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
84613 if (!commentTextarea.empty() && _newNote) {
84614 // autofocus the comment field for new notes
84615 commentTextarea.node().focus();
84619 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
84621 function keydown(d3_event) {
84622 if (!(d3_event.keyCode === 13 && // ↩ Return
84623 d3_event.metaKey)) return;
84624 var osm = services.osm;
84626 var hasAuth = osm.authenticated();
84627 if (!hasAuth) return;
84628 if (!_note.newComment) return;
84629 d3_event.preventDefault();
84630 select(this).on('keydown.note-input', null); // focus on button and submit
84632 window.setTimeout(function () {
84633 if (_note.isNew()) {
84634 noteSave.selectAll('.save-button').node().focus();
84637 noteSave.selectAll('.comment-button').node().focus();
84643 function changeInput() {
84644 var input = select(this);
84645 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
84647 _note = _note.update({
84650 var osm = services.osm;
84653 osm.replaceNote(_note); // update note cache
84656 noteSave.call(noteSaveButtons);
84660 function userDetails(selection) {
84661 var detailSection = selection.selectAll('.detail-section').data([0]);
84662 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
84663 var osm = services.osm;
84664 if (!osm) return; // Add warning if user is not logged in
84666 var hasAuth = osm.authenticated();
84667 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
84668 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
84669 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
84670 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
84671 authEnter.append('span').html(_t.html('note.login'));
84672 authEnter.append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('login')).on('click.note-login', function (d3_event) {
84673 d3_event.preventDefault();
84674 osm.authenticate();
84676 authEnter.transition().duration(200).style('opacity', 1);
84677 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
84678 prose.exit().remove();
84679 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
84680 osm.userDetails(function (err, user) {
84682 var userLink = select(document.createElement('div'));
84684 if (user.image_url) {
84685 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
84688 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
84689 prose.html(_t.html('note.upload_explanation_with_user', {
84690 user: userLink.html()
84695 function noteSaveButtons(selection) {
84696 var osm = services.osm;
84697 var hasAuth = osm && osm.authenticated();
84699 var isSelected = _note && _note.id === context.selectedNoteID();
84701 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
84702 return d.status + d.id;
84705 buttonSection.exit().remove(); // enter
84707 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
84709 if (_note.isNew()) {
84710 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
84711 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
84713 buttonEnter.append('button').attr('class', 'button status-button action');
84714 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
84718 buttonSection = buttonSection.merge(buttonEnter);
84719 buttonSection.select('.cancel-button') // select and propagate data
84720 .on('click.cancel', clickCancel);
84721 buttonSection.select('.save-button') // select and propagate data
84722 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
84723 buttonSection.select('.status-button') // select and propagate data
84724 .attr('disabled', hasAuth ? null : true).html(function (d) {
84725 var action = d.status === 'open' ? 'close' : 'open';
84726 var andComment = d.newComment ? '_comment' : '';
84727 return _t('note.' + action + andComment);
84728 }).on('click.status', clickStatus);
84729 buttonSection.select('.comment-button') // select and propagate data
84730 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
84732 function isSaveDisabled(d) {
84733 return hasAuth && d.status === 'open' && d.newComment ? null : true;
84737 function clickCancel(d3_event, d) {
84738 this.blur(); // avoid keeping focus on the button - #4641
84740 var osm = services.osm;
84746 context.enter(modeBrowse(context));
84747 dispatch.call('change');
84750 function clickSave(d3_event, d) {
84751 this.blur(); // avoid keeping focus on the button - #4641
84753 var osm = services.osm;
84756 osm.postNoteCreate(d, function (err, note) {
84757 dispatch.call('change', note);
84762 function clickStatus(d3_event, d) {
84763 this.blur(); // avoid keeping focus on the button - #4641
84765 var osm = services.osm;
84768 var setStatus = d.status === 'open' ? 'closed' : 'open';
84769 osm.postNoteUpdate(d, setStatus, function (err, note) {
84770 dispatch.call('change', note);
84775 function clickComment(d3_event, d) {
84776 this.blur(); // avoid keeping focus on the button - #4641
84778 var osm = services.osm;
84781 osm.postNoteUpdate(d, d.status, function (err, note) {
84782 dispatch.call('change', note);
84787 noteEditor.note = function (val) {
84788 if (!arguments.length) return _note;
84793 noteEditor.newNote = function (val) {
84794 if (!arguments.length) return _newNote;
84799 return utilRebind(noteEditor, dispatch, 'on');
84802 function uiSidebar(context) {
84803 var inspector = uiInspector(context);
84804 var dataEditor = uiDataEditor(context);
84805 var noteEditor = uiNoteEditor(context);
84806 var improveOsmEditor = uiImproveOsmEditor(context);
84807 var keepRightEditor = uiKeepRightEditor(context);
84808 var osmoseEditor = uiOsmoseEditor(context);
84812 var _wasData = false;
84813 var _wasNote = false;
84814 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
84816 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
84818 function sidebar(selection) {
84819 var container = context.container();
84820 var minWidth = 240;
84822 var containerWidth;
84823 var dragOffset; // Set the initial width constraints
84825 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
84826 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
84827 var downPointerId, lastClientX, containerLocGetter;
84829 function pointerdown(d3_event) {
84830 if (downPointerId) return;
84831 if ('button' in d3_event && d3_event.button !== 0) return;
84832 downPointerId = d3_event.pointerId || 'mouse';
84833 lastClientX = d3_event.clientX;
84834 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
84836 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
84837 sidebarWidth = selection.node().getBoundingClientRect().width;
84838 containerWidth = container.node().getBoundingClientRect().width;
84839 var widthPct = sidebarWidth / containerWidth * 100;
84840 selection.style('width', widthPct + '%') // lock in current width
84841 .style('max-width', '85%'); // but allow larger widths
84843 resizer.classed('dragging', true);
84844 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
84845 // disable page scrolling while resizing on touch input
84846 d3_event.preventDefault();
84849 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
84852 function pointermove(d3_event) {
84853 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
84854 d3_event.preventDefault();
84855 var dx = d3_event.clientX - lastClientX;
84856 lastClientX = d3_event.clientX;
84857 var isRTL = _mainLocalizer.textDirection() === 'rtl';
84858 var scaleX = isRTL ? 0 : 1;
84859 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
84860 var x = containerLocGetter(d3_event)[0] - dragOffset;
84861 sidebarWidth = isRTL ? containerWidth - x : x;
84862 var isCollapsed = selection.classed('collapsed');
84863 var shouldCollapse = sidebarWidth < minWidth;
84864 selection.classed('collapsed', shouldCollapse);
84866 if (shouldCollapse) {
84867 if (!isCollapsed) {
84868 selection.style(xMarginProperty, '-400px').style('width', '400px');
84869 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
84872 var widthPct = sidebarWidth / containerWidth * 100;
84873 selection.style(xMarginProperty, null).style('width', widthPct + '%');
84876 context.ui().onResize([-sidebarWidth * scaleX, 0]);
84878 context.ui().onResize([-dx * scaleX, 0]);
84883 function pointerup(d3_event) {
84884 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
84885 downPointerId = null;
84886 resizer.classed('dragging', false);
84887 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
84890 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
84891 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
84893 var hoverModeSelect = function hoverModeSelect(targets) {
84894 context.container().selectAll('.feature-list-item button').classed('hover', false);
84896 if (context.selectedIDs().length > 1 && targets && targets.length) {
84897 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
84898 return targets.indexOf(node) !== -1;
84901 if (!elements.empty()) {
84902 elements.classed('hover', true);
84907 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
84909 function hover(targets) {
84910 var datum = targets && targets.length && targets[0];
84912 if (datum && datum.__featurehash__) {
84913 // hovering on data
84915 sidebar.show(dataEditor.datum(datum));
84916 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84917 } else if (datum instanceof osmNote) {
84918 if (context.mode().id === 'drag-note') return;
84920 var osm = services.osm;
84923 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
84926 sidebar.show(noteEditor.note(datum));
84927 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84928 } else if (datum instanceof QAItem) {
84930 var errService = services[datum.service];
84933 // marker may contain stale data - get latest
84934 datum = errService.getError(datum.id);
84935 } // Currently only three possible services
84940 if (datum.service === 'keepRight') {
84941 errEditor = keepRightEditor;
84942 } else if (datum.service === 'osmose') {
84943 errEditor = osmoseEditor;
84945 errEditor = improveOsmEditor;
84948 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
84949 return d.id === datum.id;
84951 sidebar.show(errEditor.error(datum));
84952 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
84953 } else if (!_current && datum instanceof osmEntity) {
84954 featureListWrap.classed('inspector-hidden', true);
84955 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
84957 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
84958 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
84959 inspectorWrap.call(inspector);
84961 } else if (!_current) {
84962 featureListWrap.classed('inspector-hidden', false);
84963 inspectorWrap.classed('inspector-hidden', true);
84964 inspector.state('hide');
84965 } else if (_wasData || _wasNote || _wasQaItem) {
84968 _wasQaItem = false;
84969 context.container().selectAll('.note').classed('hover', false);
84970 context.container().selectAll('.qaItem').classed('hover', false);
84975 sidebar.hover = throttle(hover, 200);
84977 sidebar.intersects = function (extent) {
84978 var rect = selection.node().getBoundingClientRect();
84979 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
84982 sidebar.select = function (ids, newFeature) {
84985 if (ids && ids.length) {
84986 var entity = ids.length === 1 && context.entity(ids[0]);
84988 if (entity && newFeature && selection.classed('collapsed')) {
84989 // uncollapse the sidebar
84990 var extent = entity.extent(context.graph());
84991 sidebar.expand(sidebar.intersects(extent));
84994 featureListWrap.classed('inspector-hidden', true);
84995 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
84996 // themselves may have changed
84998 inspector.state('select').entityIDs(ids).newFeature(newFeature);
84999 inspectorWrap.call(inspector);
85001 inspector.state('hide');
85005 sidebar.showPresetList = function () {
85006 inspector.showList();
85009 sidebar.show = function (component, element) {
85010 featureListWrap.classed('inspector-hidden', true);
85011 inspectorWrap.classed('inspector-hidden', true);
85012 if (_current) _current.remove();
85013 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
85016 sidebar.hide = function () {
85017 featureListWrap.classed('inspector-hidden', false);
85018 inspectorWrap.classed('inspector-hidden', true);
85019 if (_current) _current.remove();
85023 sidebar.expand = function (moveMap) {
85024 if (selection.classed('collapsed')) {
85025 sidebar.toggle(moveMap);
85029 sidebar.collapse = function (moveMap) {
85030 if (!selection.classed('collapsed')) {
85031 sidebar.toggle(moveMap);
85035 sidebar.toggle = function (moveMap) {
85036 // Don't allow sidebar to toggle when the user is in the walkthrough.
85037 if (context.inIntro()) return;
85038 var isCollapsed = selection.classed('collapsed');
85039 var isCollapsing = !isCollapsed;
85040 var isRTL = _mainLocalizer.textDirection() === 'rtl';
85041 var scaleX = isRTL ? 0 : 1;
85042 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
85043 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
85045 selection.style('width', sidebarWidth + 'px');
85046 var startMargin, endMargin, lastMargin;
85048 if (isCollapsing) {
85049 startMargin = lastMargin = 0;
85050 endMargin = -sidebarWidth;
85052 startMargin = lastMargin = -sidebarWidth;
85056 if (!isCollapsing) {
85057 // unhide the sidebar's content before it transitions onscreen
85058 selection.classed('collapsed', isCollapsing);
85061 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
85062 var i = d3_interpolateNumber(startMargin, endMargin);
85063 return function (t) {
85064 var dx = lastMargin - Math.round(i(t));
85065 lastMargin = lastMargin - dx;
85066 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
85068 }).on('end', function () {
85069 if (isCollapsing) {
85070 // hide the sidebar's content after it transitions offscreen
85071 selection.classed('collapsed', isCollapsing);
85072 } // switch back from px to %
85075 if (!isCollapsing) {
85076 var containerWidth = container.node().getBoundingClientRect().width;
85077 var widthPct = sidebarWidth / containerWidth * 100;
85078 selection.style(xMarginProperty, null).style('width', widthPct + '%');
85081 }; // toggle the sidebar collapse when double-clicking the resizer
85084 resizer.on('dblclick', function (d3_event) {
85085 d3_event.preventDefault();
85087 if (d3_event.sourceEvent) {
85088 d3_event.sourceEvent.preventDefault();
85092 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
85094 context.map().on('crossEditableZoom.sidebar', function (within) {
85095 if (!within && !selection.select('.inspector-hover').empty()) {
85101 sidebar.showPresetList = function () {};
85103 sidebar.hover = function () {};
85105 sidebar.hover.cancel = function () {};
85107 sidebar.intersects = function () {};
85109 sidebar.select = function () {};
85111 sidebar.show = function () {};
85113 sidebar.hide = function () {};
85115 sidebar.expand = function () {};
85117 sidebar.collapse = function () {};
85119 sidebar.toggle = function () {};
85124 function uiSourceSwitch(context) {
85127 function click(d3_event) {
85128 d3_event.preventDefault();
85129 var osm = context.connection();
85131 if (context.inIntro()) return;
85132 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
85133 var isLive = select(this).classed('live');
85135 context.enter(modeBrowse(context));
85136 context.history().clearSaved(); // remove saved history
85138 context.flush(); // remove stored data
85140 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
85141 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
85144 var sourceSwitch = function sourceSwitch(selection) {
85145 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
85148 sourceSwitch.keys = function (_) {
85149 if (!arguments.length) return keys;
85151 return sourceSwitch;
85154 return sourceSwitch;
85157 function uiSpinner(context) {
85158 var osm = context.connection();
85159 return function (selection) {
85160 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
85163 osm.on('loading.spinner', function () {
85164 img.transition().style('opacity', 1);
85165 }).on('loaded.spinner', function () {
85166 img.transition().style('opacity', 0);
85172 function uiSplash(context) {
85173 return function (selection) {
85174 // Exception - if there are restorable changes, skip this splash screen.
85175 // This is because we currently only support one `uiModal` at a time
85176 // and we need to show them `uiRestore`` instead of this one.
85177 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
85179 var updateMessage = '';
85180 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
85181 var showSplash = !corePreferences('sawSplash');
85183 if (sawPrivacyVersion !== context.privacyVersion) {
85184 updateMessage = _t('splash.privacy_update');
85188 if (!showSplash) return;
85189 corePreferences('sawSplash', true);
85190 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
85192 _mainFileFetcher.get('intro_graph');
85193 var modalSelection = uiModal(selection);
85194 modalSelection.select('.modal').attr('class', 'modal-splash modal');
85195 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
85196 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
85197 var modalSection = introModal.append('div').attr('class', 'modal-section');
85198 modalSection.append('p').html(_t.html('splash.text', {
85199 version: context.version,
85200 website: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/develop/CHANGELOG.md#whats-new">changelog</a>',
85201 github: '<a target="_blank" href="https://github.com/openstreetmap/iD/issues">github.com</a>'
85203 modalSection.append('p').html(_t.html('splash.privacy', {
85204 updateMessage: updateMessage,
85205 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
85207 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
85208 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
85209 context.container().call(uiIntro(context));
85210 modalSelection.close();
85212 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
85213 walkthrough.append('div').html(_t.html('splash.walkthrough'));
85214 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
85215 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
85216 startEditing.append('div').html(_t.html('splash.start'));
85217 modalSelection.select('button.close').attr('class', 'hide');
85221 function uiStatus(context) {
85222 var osm = context.connection();
85223 return function (selection) {
85226 function update(err, apiStatus) {
85227 selection.html('');
85230 if (apiStatus === 'connectionSwitched') {
85231 // if the connection was just switched, we can't rely on
85232 // the status (we're getting the status of the previous api)
85234 } else if (apiStatus === 'rateLimited') {
85235 selection.html(_t.html('osm_api_status.message.rateLimit')).append('a').attr('href', '#').attr('class', 'api-status-login').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('login')).on('click.login', function (d3_event) {
85236 d3_event.preventDefault();
85237 osm.authenticate();
85240 // don't allow retrying too rapidly
85241 var throttledRetry = throttle(function () {
85242 // try loading the visible tiles
85243 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
85245 osm.reloadApiStatus();
85246 }, 2000); // eslint-disable-next-line no-warning-comments
85247 // TODO: nice messages for different error types
85250 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
85251 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
85252 d3_event.preventDefault();
85256 } else if (apiStatus === 'readonly') {
85257 selection.html(_t.html('osm_api_status.message.readonly'));
85258 } else if (apiStatus === 'offline') {
85259 selection.html(_t.html('osm_api_status.message.offline'));
85262 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
85265 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
85267 window.setInterval(function () {
85268 osm.reloadApiStatus();
85269 }, 90000); // load the initial status in case no OSM data was loaded yet
85271 osm.reloadApiStatus();
85275 function modeDrawArea(context, wayID, startGraph, button) {
85280 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
85281 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
85283 mode.wayID = wayID;
85285 mode.enter = function () {
85286 context.install(behavior);
85289 mode.exit = function () {
85290 context.uninstall(behavior);
85293 mode.selectedIDs = function () {
85297 mode.activeID = function () {
85298 return behavior && behavior.activeID() || [];
85304 function modeAddArea(context, mode) {
85305 mode.id = 'add-area';
85306 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
85307 var defaultTags = {
85310 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
85312 function actionClose(wayId) {
85313 return function (graph) {
85314 return graph.replace(graph.entity(wayId).close());
85318 function start(loc) {
85319 var startGraph = context.graph();
85320 var node = osmNode({
85326 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
85327 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85330 function startFromWay(loc, edge) {
85331 var startGraph = context.graph();
85332 var node = osmNode({
85338 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
85342 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85345 function startFromNode(node) {
85346 var startGraph = context.graph();
85350 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
85351 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
85354 mode.enter = function () {
85355 context.install(behavior);
85358 mode.exit = function () {
85359 context.uninstall(behavior);
85365 function modeAddLine(context, mode) {
85366 mode.id = 'add-line';
85367 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
85368 var defaultTags = {};
85369 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
85371 function start(loc) {
85372 var startGraph = context.graph();
85373 var node = osmNode({
85379 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
85380 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85383 function startFromWay(loc, edge) {
85384 var startGraph = context.graph();
85385 var node = osmNode({
85391 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
85395 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85398 function startFromNode(node) {
85399 var startGraph = context.graph();
85403 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
85404 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
85407 mode.enter = function () {
85408 context.install(behavior);
85411 mode.exit = function () {
85412 context.uninstall(behavior);
85418 function modeAddPoint(context, mode) {
85419 mode.id = 'add-point';
85420 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
85421 var defaultTags = {};
85422 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
85424 function add(loc) {
85425 var node = osmNode({
85429 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
85430 enterSelectMode(node);
85433 function addWay(loc, edge) {
85434 var node = osmNode({
85437 context.perform(actionAddMidpoint({
85440 }, node), _t('operations.add.annotation.vertex'));
85441 enterSelectMode(node);
85444 function enterSelectMode(node) {
85445 context.enter(modeSelect(context, [node.id]).newFeature(true));
85448 function addNode(node) {
85449 if (Object.keys(defaultTags).length === 0) {
85450 enterSelectMode(node);
85454 var tags = Object.assign({}, node.tags); // shallow copy
85456 for (var key in defaultTags) {
85457 tags[key] = defaultTags[key];
85460 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
85461 enterSelectMode(node);
85464 function cancel() {
85465 context.enter(modeBrowse(context));
85468 mode.enter = function () {
85469 context.install(behavior);
85472 mode.exit = function () {
85473 context.uninstall(behavior);
85479 function modeSelectNote(context, selectedNoteID) {
85485 var _keybinding = utilKeybinding('select-note');
85487 var _noteEditor = uiNoteEditor(context).on('change', function () {
85488 context.map().pan([0, 0]); // trigger a redraw
85490 var note = checkSelectedID();
85492 context.ui().sidebar.show(_noteEditor.note(note));
85495 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
85496 var _newFeature = false;
85498 function checkSelectedID() {
85499 if (!services.osm) return;
85500 var note = services.osm.getNote(selectedNoteID);
85503 context.enter(modeBrowse(context));
85507 } // class the note as selected, or return to browse mode if the note is gone
85510 function selectNote(d3_event, drawn) {
85511 if (!checkSelectedID()) return;
85512 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
85514 if (selection.empty()) {
85515 // Return to browse mode if selected DOM elements have
85516 // disappeared because the user moved them out of view..
85517 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
85519 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
85520 context.enter(modeBrowse(context));
85523 selection.classed('selected', true);
85524 context.selectedNoteID(selectedNoteID);
85529 if (context.container().select('.combobox').size()) return;
85530 context.enter(modeBrowse(context));
85533 mode.zoomToSelected = function () {
85534 if (!services.osm) return;
85535 var note = services.osm.getNote(selectedNoteID);
85538 context.map().centerZoomEase(note.loc, 20);
85542 mode.newFeature = function (val) {
85543 if (!arguments.length) return _newFeature;
85548 mode.enter = function () {
85549 var note = checkSelectedID();
85552 _behaviors.forEach(context.install);
85554 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
85556 select(document).call(_keybinding);
85558 var sidebar = context.ui().sidebar;
85559 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
85561 sidebar.expand(sidebar.intersects(note.extent()));
85562 context.map().on('drawn.select', selectNote);
85565 mode.exit = function () {
85566 _behaviors.forEach(context.uninstall);
85568 select(document).call(_keybinding.unbind);
85569 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
85570 context.map().on('drawn.select', null);
85571 context.ui().sidebar.hide();
85572 context.selectedNoteID(null);
85578 function modeAddNote(context) {
85582 description: _t.html('modes.add_note.description'),
85583 key: _t('modes.add_note.key')
85585 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
85587 function add(loc) {
85588 var osm = services.osm;
85590 var note = osmNote({
85595 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
85597 context.map().pan([0, 0]);
85598 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
85601 function cancel() {
85602 context.enter(modeBrowse(context));
85605 mode.enter = function () {
85606 context.install(behavior);
85609 mode.exit = function () {
85610 context.uninstall(behavior);
85616 var JXON = new function () {
85617 var sValueProp = 'keyValue',
85618 sAttributesProp = 'keyAttributes',
85621 /* you can customize these values */
85624 rIsBool = /^(?:true|false)$/i;
85626 function parseText(sValue) {
85627 if (rIsNull.test(sValue)) {
85631 if (rIsBool.test(sValue)) {
85632 return sValue.toLowerCase() === 'true';
85635 if (isFinite(sValue)) {
85636 return parseFloat(sValue);
85639 if (isFinite(Date.parse(sValue))) {
85640 return new Date(sValue);
85646 function EmptyTree() {}
85648 EmptyTree.prototype.toString = function () {
85652 EmptyTree.prototype.valueOf = function () {
85656 function objectify(vValue) {
85657 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
85660 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
85661 var nLevelStart = aCache.length,
85662 bChildren = oParentNode.hasChildNodes(),
85663 bAttributes = oParentNode.hasAttributes(),
85664 bHighVerb = Boolean(nVerb & 2);
85668 sCollectedTxt = '',
85669 vResult = bHighVerb ? {} :
85670 /* put here the default value for empty nodes: */
85674 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
85675 oNode = oParentNode.childNodes.item(nItem);
85677 if (oNode.nodeType === 4) {
85678 /* nodeType is 'CDATASection' (4) */
85679 sCollectedTxt += oNode.nodeValue;
85680 } else if (oNode.nodeType === 3) {
85681 /* nodeType is 'Text' (3) */
85682 sCollectedTxt += oNode.nodeValue.trim();
85683 } else if (oNode.nodeType === 1 && !oNode.prefix) {
85684 /* nodeType is 'Element' (1) */
85685 aCache.push(oNode);
85690 var nLevelEnd = aCache.length,
85691 vBuiltVal = parseText(sCollectedTxt);
85693 if (!bHighVerb && (bChildren || bAttributes)) {
85694 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
85697 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
85698 sProp = aCache[nElId].nodeName.toLowerCase();
85699 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
85701 if (vResult.hasOwnProperty(sProp)) {
85702 if (vResult[sProp].constructor !== Array) {
85703 vResult[sProp] = [vResult[sProp]];
85706 vResult[sProp].push(vContent);
85708 vResult[sProp] = vContent;
85714 var nAttrLen = oParentNode.attributes.length,
85715 sAPrefix = bNesteAttr ? '' : sAttrPref,
85716 oAttrParent = bNesteAttr ? {} : vResult;
85718 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
85719 oAttrib = oParentNode.attributes.item(nAttrib);
85720 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
85725 Object.freeze(oAttrParent);
85728 vResult[sAttributesProp] = oAttrParent;
85729 nLength -= nAttrLen - 1;
85733 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
85734 vResult[sValueProp] = vBuiltVal;
85735 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
85736 vResult = vBuiltVal;
85739 if (bFreeze && (bHighVerb || nLength > 0)) {
85740 Object.freeze(vResult);
85743 aCache.length = nLevelStart;
85747 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
85748 var vValue, oChild;
85750 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
85751 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
85752 /* verbosity level is 0 */
85753 } else if (oParentObj.constructor === Date) {
85754 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
85757 for (var sName in oParentObj) {
85758 vValue = oParentObj[sName];
85760 if (isFinite(sName) || vValue instanceof Function) {
85763 /* verbosity level is 0 */
85766 if (sName === sValueProp) {
85767 if (vValue !== null && vValue !== true) {
85768 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
85770 } else if (sName === sAttributesProp) {
85771 /* verbosity level is 3 */
85772 for (var sAttrib in vValue) {
85773 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
85775 } else if (sName.charAt(0) === sAttrPref) {
85776 oParentEl.setAttribute(sName.slice(1), vValue);
85777 } else if (vValue.constructor === Array) {
85778 for (var nItem = 0; nItem < vValue.length; nItem++) {
85779 oChild = oXMLDoc.createElement(sName);
85780 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
85781 oParentEl.appendChild(oChild);
85784 oChild = oXMLDoc.createElement(sName);
85786 if (vValue instanceof Object) {
85787 loadObjTree(oXMLDoc, oChild, vValue);
85788 } else if (vValue !== null && vValue !== true) {
85789 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
85792 oParentEl.appendChild(oChild);
85797 this.build = function (oXMLParent, nVerbosity
85804 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
85805 /* put here the default verbosity level: */
85808 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
85811 this.unbuild = function (oObjTree) {
85812 var oNewDoc = document.implementation.createDocument('', '', null);
85813 loadObjTree(oNewDoc, oNewDoc, oObjTree);
85817 this.stringify = function (oObjTree) {
85818 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
85820 }(); // var myObject = JXON.build(doc);
85821 // we got our javascript object! try: alert(JSON.stringify(myObject));
85822 // var newDoc = JXON.unbuild(myObject);
85823 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
85825 function uiConflicts(context) {
85826 var dispatch = dispatch$8('cancel', 'save');
85827 var keybinding = utilKeybinding('conflicts');
85833 var _shownConflictIndex;
85835 function keybindingOn() {
85836 select(document).call(keybinding.on('⎋', cancel, true));
85839 function keybindingOff() {
85840 select(document).call(keybinding.unbind);
85843 function tryAgain() {
85845 dispatch.call('save');
85848 function cancel() {
85850 dispatch.call('cancel');
85853 function conflicts(selection) {
85855 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
85856 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
85857 headerEnter.append('h3').html(_t.html('save.conflict.header'));
85858 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
85859 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
85861 var detected = utilDetect();
85862 var changeset = new osmChangeset();
85863 delete changeset.id; // Export without changeset_id
85865 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
85866 var blob = new Blob([data], {
85867 type: 'text/xml;charset=utf-8;'
85869 var fileName = 'changes.osc';
85870 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
85872 if (detected.download) {
85873 // All except IE11 and Edge
85874 linkEnter // download the data as a file
85875 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
85878 linkEnter // open data uri in a new tab
85879 .attr('target', '_blank').on('click.download', function () {
85880 navigator.msSaveBlob(blob, fileName);
85884 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
85885 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
85886 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
85887 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
85888 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
85889 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
85892 function showConflict(selection, index) {
85893 index = utilWrap(index, _conflictList.length);
85894 _shownConflictIndex = index;
85895 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
85897 if (index === _conflictList.length - 1) {
85898 window.setTimeout(function () {
85899 parent.select('.conflicts-button').attr('disabled', null);
85900 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
85904 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
85905 conflict.exit().remove();
85906 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
85907 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
85909 total: _conflictList.length
85911 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
85913 }).on('click', function (d3_event, d) {
85914 d3_event.preventDefault();
85915 zoomToEntity(d.id);
85917 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
85918 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
85919 return d.details || [];
85920 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
85923 details.append('div').attr('class', 'conflict-choices').call(addChoices);
85924 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
85925 return _t.html('save.conflict.' + d);
85926 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
85927 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
85928 }).on('click', function (d3_event, d) {
85929 d3_event.preventDefault();
85930 var container = parent.selectAll('.conflict-container');
85931 var sign = d === 'previous' ? -1 : 1;
85932 container.selectAll('.conflict').remove();
85933 container.call(showConflict, index + sign);
85937 function addChoices(selection) {
85938 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
85939 return d.choices || [];
85942 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
85943 var labelEnter = choicesEnter.append('label');
85944 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
85946 }).on('change', function (d3_event, d) {
85947 var ul = this.parentNode.parentNode.parentNode;
85948 ul.__data__.chosen = d.id;
85949 choose(d3_event, ul, d);
85951 labelEnter.append('span').html(function (d) {
85955 choicesEnter.merge(choices).each(function (d) {
85956 var ul = this.parentNode;
85958 if (ul.__data__.chosen === d.id) {
85959 choose(null, ul, d);
85964 function choose(d3_event, ul, datum) {
85965 if (d3_event) d3_event.preventDefault();
85966 select(ul).selectAll('li').classed('active', function (d) {
85967 return d === datum;
85968 }).selectAll('input').property('checked', function (d) {
85969 return d === datum;
85971 var extent = geoExtent();
85973 entity = context.graph().hasEntity(datum.id);
85974 if (entity) extent._extend(entity.extent(context.graph()));
85976 entity = context.graph().hasEntity(datum.id);
85977 if (entity) extent._extend(entity.extent(context.graph()));
85978 zoomToEntity(datum.id, extent);
85981 function zoomToEntity(id, extent) {
85982 context.surface().selectAll('.hover').classed('hover', false);
85983 var entity = context.graph().hasEntity(id);
85987 context.map().trimmedExtent(extent);
85989 context.map().zoomToEase(entity);
85992 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
85994 } // The conflict list should be an array of objects like:
85997 // name: entityName(local),
85998 // details: merge.conflicts(),
86001 // choice(id, keepMine, forceLocal),
86002 // choice(id, keepTheirs, forceRemote)
86007 conflicts.conflictList = function (_) {
86008 if (!arguments.length) return _conflictList;
86013 conflicts.origChanges = function (_) {
86014 if (!arguments.length) return _origChanges;
86019 conflicts.shownEntityIds = function () {
86020 if (_conflictList && typeof _shownConflictIndex === 'number') {
86021 return [_conflictList[_shownConflictIndex].id];
86027 return utilRebind(conflicts, dispatch, 'on');
86030 function uiConfirm(selection) {
86031 var modalSelection = uiModal(selection);
86032 modalSelection.select('.modal').classed('modal-alert', true);
86033 var section = modalSelection.select('.content');
86034 section.append('div').attr('class', 'modal-section header');
86035 section.append('div').attr('class', 'modal-section message-text');
86036 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
86038 modalSelection.okButton = function () {
86039 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
86040 modalSelection.remove();
86041 }).html(_t.html('confirm.okay')).node().focus();
86042 return modalSelection;
86045 return modalSelection;
86048 function uiChangesetEditor(context) {
86049 var dispatch = dispatch$8('change');
86050 var formFields = uiFormFields(context);
86051 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
86059 function changesetEditor(selection) {
86063 function render(selection) {
86064 var initial = false;
86068 var presets = _mainPresetIndex;
86069 _fieldsArr = [uiField(context, presets.field('comment'), null, {
86072 }), uiField(context, presets.field('source'), null, {
86075 }), uiField(context, presets.field('hashtags'), null, {
86080 _fieldsArr.forEach(function (field) {
86081 field.on('change', function (t, onInput) {
86082 dispatch.call('change', field, undefined, t, onInput);
86087 _fieldsArr.forEach(function (field) {
86091 selection.call(formFields.fieldsArr(_fieldsArr));
86094 var commentField = selection.select('.form-field-comment textarea');
86095 var commentNode = commentField.node();
86098 commentNode.focus();
86099 commentNode.select();
86100 } // trigger a 'blur' event so that comment field can be cleaned
86101 // and checked for hashtags, even if retrieved from localstorage
86104 utilTriggerEvent(commentField, 'blur');
86105 var osm = context.connection();
86108 osm.userChangesets(function (err, changesets) {
86110 var comments = changesets.map(function (changeset) {
86111 var comment = changeset.tags.comment;
86116 }).filter(Boolean);
86117 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
86120 } // Add warning if comment mentions Google
86123 var hasGoogle = _tags.comment.match(/google/i);
86125 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
86126 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
86127 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
86128 commentEnter.append('a').attr('target', '_blank').call(svgIcon('#iD-icon-alert', 'inline')).attr('href', _t('commit.google_warning_link')).append('span').html(_t.html('commit.google_warning'));
86129 commentEnter.transition().duration(200).style('opacity', 1);
86132 changesetEditor.tags = function (_) {
86133 if (!arguments.length) return _tags;
86134 _tags = _; // Don't reset _fieldsArr here.
86136 return changesetEditor;
86139 changesetEditor.changesetID = function (_) {
86140 if (!arguments.length) return _changesetID;
86141 if (_changesetID === _) return changesetEditor;
86144 return changesetEditor;
86147 return utilRebind(changesetEditor, dispatch, 'on');
86150 function uiSectionChanges(context) {
86151 var detected = utilDetect();
86152 var _discardTags = {};
86153 _mainFileFetcher.get('discarded').then(function (d) {
86155 })["catch"](function () {
86158 var section = uiSection('changes-list', context).label(function () {
86159 var history = context.history();
86160 var summary = history.difference().summary();
86161 return _t('inspector.title_count', {
86162 title: _t.html('commit.changes'),
86163 count: summary.length
86165 }).disclosureContent(renderDisclosureContent);
86167 function renderDisclosureContent(selection) {
86168 var history = context.history();
86169 var summary = history.difference().summary();
86170 var container = selection.selectAll('.commit-section').data([0]);
86171 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
86172 containerEnter.append('ul').attr('class', 'changeset-list');
86173 container = containerEnter.merge(container);
86174 var items = container.select('ul').selectAll('li').data(summary);
86175 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
86176 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
86177 buttons.each(function (d) {
86178 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
86180 buttons.append('span').attr('class', 'change-type').html(function (d) {
86181 return _t.html('commit.' + d.changeType) + ' ';
86183 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
86184 var matched = _mainPresetIndex.match(d.entity, d.graph);
86185 return matched && matched.name() || utilDisplayType(d.entity.id);
86187 buttons.append('span').attr('class', 'entity-name').html(function (d) {
86188 var name = utilDisplayName(d.entity) || '',
86195 return string += ' ' + name;
86197 items = itemsEnter.merge(items); // Download changeset link
86199 var changeset = new osmChangeset().update({
86202 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
86203 delete changeset.id; // Export without chnageset_id
86205 var data = JXON.stringify(changeset.osmChangeJXON(changes));
86206 var blob = new Blob([data], {
86207 type: 'text/xml;charset=utf-8;'
86209 var fileName = 'changes.osc';
86210 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
86212 if (detected.download) {
86213 // All except IE11 and Edge
86214 linkEnter // download the data as a file
86215 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
86218 linkEnter // open data uri in a new tab
86219 .attr('target', '_blank').on('click.download', function () {
86220 navigator.msSaveBlob(blob, fileName);
86224 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
86226 function mouseover(d) {
86228 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
86232 function mouseout() {
86233 context.surface().selectAll('.hover').classed('hover', false);
86236 function click(d3_event, change) {
86237 if (change.changeType !== 'deleted') {
86238 var entity = change.entity;
86239 context.map().zoomToEase(entity);
86240 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
86248 function uiCommitWarnings(context) {
86249 function commitWarnings(selection) {
86250 var issuesBySeverity = context.validator().getIssuesBySeverity({
86253 includeDisabledRules: true
86256 for (var severity in issuesBySeverity) {
86257 var issues = issuesBySeverity[severity];
86259 if (severity !== 'error') {
86260 // exclude 'fixme' and similar - #8603
86261 issues = issues.filter(function (issue) {
86262 return issue.type !== 'help_request';
86266 var section = severity + '-section';
86267 var issueItem = severity + '-item';
86268 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
86269 container.exit().remove();
86270 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
86271 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
86272 containerEnter.append('ul').attr('class', 'changeset-list');
86273 container = containerEnter.merge(container);
86274 var items = container.select('ul').selectAll('li').data(issues, function (d) {
86277 items.exit().remove();
86278 var itemsEnter = items.enter().append('li').attr('class', issueItem);
86279 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
86281 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
86283 }).on('mouseout', function () {
86284 context.surface().selectAll('.hover').classed('hover', false);
86285 }).on('click', function (d3_event, d) {
86286 context.validator().focusIssue(d);
86288 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
86289 buttons.append('strong').attr('class', 'issue-message');
86290 buttons.filter(function (d) {
86292 }).call(uiTooltip().title(function (d) {
86294 }).placement('top'));
86295 items = itemsEnter.merge(items);
86296 items.selectAll('.issue-message').html(function (d) {
86297 return d.message(context);
86302 return commitWarnings;
86305 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
86306 // from https://stackoverflow.com/a/25575009
86308 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
86309 function uiCommit(context) {
86310 var dispatch = dispatch$8('cancel');
86316 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
86317 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
86318 var commitChanges = uiSectionChanges(context);
86319 var commitWarnings = uiCommitWarnings(context);
86321 function commit(selection) {
86322 _selection = selection; // Initialize changeset if one does not exist yet.
86324 if (!context.changeset) initChangeset();
86325 loadDerivedChangesetTags();
86326 selection.call(render);
86329 function initChangeset() {
86330 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
86331 var commentDate = +corePreferences('commentDate') || 0;
86332 var currDate = Date.now();
86333 var cutoff = 2 * 86400 * 1000; // 2 days
86335 if (commentDate > currDate || currDate - commentDate > cutoff) {
86336 corePreferences('comment', null);
86337 corePreferences('hashtags', null);
86338 corePreferences('source', null);
86339 } // load in explicitly-set values, if any
86342 if (context.defaultChangesetComment()) {
86343 corePreferences('comment', context.defaultChangesetComment());
86344 corePreferences('commentDate', Date.now());
86347 if (context.defaultChangesetSource()) {
86348 corePreferences('source', context.defaultChangesetSource());
86349 corePreferences('commentDate', Date.now());
86352 if (context.defaultChangesetHashtags()) {
86353 corePreferences('hashtags', context.defaultChangesetHashtags());
86354 corePreferences('commentDate', Date.now());
86357 var detected = utilDetect();
86359 comment: corePreferences('comment') || '',
86360 created_by: context.cleanTagValue('iD ' + context.version),
86361 host: context.cleanTagValue(detected.host),
86362 locale: context.cleanTagValue(_mainLocalizer.localeCode())
86363 }; // call findHashtags initially - this will remove stored
86364 // hashtags if any hashtags are found in the comment - #4304
86366 findHashtags(tags, true);
86367 var hashtags = corePreferences('hashtags');
86370 tags.hashtags = hashtags;
86373 var source = corePreferences('source');
86376 tags.source = source;
86379 var photoOverlaysUsed = context.history().photoOverlaysUsed();
86381 if (photoOverlaysUsed.length) {
86382 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
86384 if (sources.indexOf('streetlevel imagery') === -1) {
86385 sources.push('streetlevel imagery');
86386 } // add the photo overlays used during editing as sources
86389 photoOverlaysUsed.forEach(function (photoOverlay) {
86390 if (sources.indexOf(photoOverlay) === -1) {
86391 sources.push(photoOverlay);
86394 tags.source = context.cleanTagValue(sources.join(';'));
86397 context.changeset = new osmChangeset({
86400 } // Calculates read-only metadata tags based on the user's editing session and applies
86401 // them to the changeset.
86404 function loadDerivedChangesetTags() {
86405 var osm = context.connection();
86407 var tags = Object.assign({}, context.changeset.tags); // shallow copy
86408 // assign tags for imagery used
86410 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
86411 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
86413 var osmClosed = osm.getClosedIDs();
86416 if (osmClosed.length) {
86417 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
86420 if (services.keepRight) {
86421 var krClosed = services.keepRight.getClosedIDs();
86423 if (krClosed.length) {
86424 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
86428 if (services.improveOSM) {
86429 var iOsmClosed = services.improveOSM.getClosedCounts();
86431 for (itemType in iOsmClosed) {
86432 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
86436 if (services.osmose) {
86437 var osmoseClosed = services.osmose.getClosedCounts();
86439 for (itemType in osmoseClosed) {
86440 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
86442 } // remove existing issue counts
86445 for (var key in tags) {
86446 if (key.match(/(^warnings:)|(^resolved:)/)) {
86451 function addIssueCounts(issues, prefix) {
86452 var issuesByType = utilArrayGroupBy(issues, 'type');
86454 for (var issueType in issuesByType) {
86455 var issuesOfType = issuesByType[issueType];
86457 if (issuesOfType[0].subtype) {
86458 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
86460 for (var issueSubtype in issuesBySubtype) {
86461 var issuesOfSubtype = issuesBySubtype[issueSubtype];
86462 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
86465 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
86468 } // add counts of warnings generated by the user's edits
86471 var warnings = context.validator().getIssuesBySeverity({
86474 includeIgnored: true,
86475 includeDisabledRules: true
86476 }).warning.filter(function (issue) {
86477 return issue.type !== 'help_request';
86478 }); // exclude 'fixme' and similar - #8603
86480 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
86482 var resolvedIssues = context.validator().getResolvedIssues();
86483 addIssueCounts(resolvedIssues, 'resolved');
86484 context.changeset = context.changeset.update({
86489 function render(selection) {
86490 var osm = context.connection();
86492 var header = selection.selectAll('.header').data([0]);
86493 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
86494 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
86495 headerTitle.append('button').attr('class', 'close').on('click', function () {
86496 dispatch.call('cancel', this);
86497 }).call(svgIcon('#iD-icon-close'));
86498 var body = selection.selectAll('.body').data([0]);
86499 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
86501 var changesetSection = body.selectAll('.changeset-editor').data([0]);
86502 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
86503 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
86505 body.call(commitWarnings); // Upload Explanation
86507 var saveSection = body.selectAll('.save-section').data([0]);
86508 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
86509 var prose = saveSection.selectAll('.commit-info').data([0]);
86511 if (prose.enter().size()) {
86512 // first time, make sure to update user details in prose
86513 _userDetails = null;
86516 prose = prose.enter().append('p').attr('class', 'commit-info').html(_t.html('commit.upload_explanation')).merge(prose); // always check if this has changed, but only update prose.html()
86517 // if needed, because it can trigger a style recalculation
86519 osm.userDetails(function (err, user) {
86521 if (_userDetails === user) return; // no change
86523 _userDetails = user;
86524 var userLink = select(document.createElement('div'));
86526 if (user.image_url) {
86527 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
86530 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
86531 prose.html(_t.html('commit.upload_explanation_with_user', {
86532 user: userLink.html()
86534 }); // Request Review
86536 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
86538 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
86539 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
86540 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
86542 if (!labelEnter.empty()) {
86543 labelEnter.call(uiTooltip().title(_t.html('commit.request_review_info')).placement('top'));
86546 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
86547 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
86549 requestReview = requestReview.merge(requestReviewEnter);
86550 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
86552 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
86554 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
86555 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
86556 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
86557 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
86558 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
86560 buttonSection = buttonSection.merge(buttonEnter);
86561 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
86562 dispatch.call('cancel', this);
86564 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
86565 if (!select(this).classed('disabled')) {
86566 this.blur(); // avoid keeping focus on the button - #4641
86568 for (var key in context.changeset.tags) {
86569 // remove any empty keys before upload
86570 if (!key) delete context.changeset.tags[key];
86573 context.uploader().save(context.changeset);
86575 }); // remove any existing tooltip
86577 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
86579 if (uploadBlockerTooltipText) {
86580 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
86581 } // Raw Tag Editor
86584 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
86585 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
86586 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
86588 var changesSection = body.selectAll('.commit-changes-section').data([0]);
86589 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
86591 changesSection.call(commitChanges.render);
86593 function toggleRequestReview() {
86594 var rr = requestReviewInput.property('checked');
86596 review_requested: rr ? 'yes' : undefined
86598 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
86603 function getUploadBlockerMessage() {
86604 var errors = context.validator().getIssuesBySeverity({
86609 if (errors.length) {
86610 return _t('commit.outstanding_errors_message', {
86611 count: errors.length
86614 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
86616 if (!hasChangesetComment) {
86617 return _t('commit.comment_needed_message');
86624 function changeTags(_, changed, onInput) {
86625 if (changed.hasOwnProperty('comment')) {
86626 if (changed.comment === undefined) {
86627 changed.comment = '';
86631 corePreferences('comment', changed.comment);
86632 corePreferences('commentDate', Date.now());
86636 if (changed.hasOwnProperty('source')) {
86637 if (changed.source === undefined) {
86638 corePreferences('source', null);
86639 } else if (!onInput) {
86640 corePreferences('source', changed.source);
86641 corePreferences('commentDate', Date.now());
86643 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
86646 updateChangeset(changed, onInput);
86649 _selection.call(render);
86653 function findHashtags(tags, commentOnly) {
86654 var detectedHashtags = commentHashtags();
86656 if (detectedHashtags.length) {
86657 // always remove stored hashtags if there are hashtags in the comment - #4304
86658 corePreferences('hashtags', null);
86661 if (!detectedHashtags.length || !commentOnly) {
86662 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
86665 var allLowerCase = new Set();
86666 return detectedHashtags.filter(function (hashtag) {
86667 // Compare tags as lowercase strings, but keep original case tags
86668 var lowerCase = hashtag.toLowerCase();
86670 if (!allLowerCase.has(lowerCase)) {
86671 allLowerCase.add(lowerCase);
86676 }); // Extract hashtags from `comment`
86678 function commentHashtags() {
86679 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
86680 .match(hashtagRegex);
86681 return matches || [];
86682 } // Extract and clean hashtags from `hashtags`
86685 function hashtagHashtags() {
86686 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
86687 if (s[0] !== '#') {
86692 var matched = s.match(hashtagRegex);
86693 return matched && matched[0];
86694 }).filter(Boolean); // exclude falsy
86696 return matches || [];
86700 function isReviewRequested(tags) {
86701 var rr = tags.review_requested;
86702 if (rr === undefined) return false;
86703 rr = rr.trim().toLowerCase();
86704 return !(rr === '' || rr === 'no');
86707 function updateChangeset(changed, onInput) {
86708 var tags = Object.assign({}, context.changeset.tags); // shallow copy
86710 Object.keys(changed).forEach(function (k) {
86711 var v = changed[k];
86712 k = context.cleanTagKey(k);
86713 if (readOnlyTags.indexOf(k) !== -1) return;
86715 if (v === undefined) {
86717 } else if (onInput) {
86720 tags[k] = context.cleanTagValue(v);
86725 // when changing the comment, override hashtags with any found in comment.
86726 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
86727 var arr = findHashtags(tags, commentOnly);
86730 tags.hashtags = context.cleanTagValue(arr.join(';'));
86731 corePreferences('hashtags', tags.hashtags);
86733 delete tags.hashtags;
86734 corePreferences('hashtags', null);
86736 } // always update userdetails, just in case user reauthenticates as someone else
86739 if (_userDetails && _userDetails.changesets_count !== undefined) {
86740 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
86742 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
86744 if (changesetsCount <= 100) {
86746 s = corePreferences('walkthrough_completed');
86749 tags['ideditor:walkthrough_completed'] = s;
86752 s = corePreferences('walkthrough_progress');
86755 tags['ideditor:walkthrough_progress'] = s;
86758 s = corePreferences('walkthrough_started');
86761 tags['ideditor:walkthrough_started'] = s;
86765 delete tags.changesets_count;
86768 if (!fastDeepEqual(context.changeset.tags, tags)) {
86769 context.changeset = context.changeset.update({
86775 commit.reset = function () {
86776 context.changeset = null;
86779 return utilRebind(commit, dispatch, 'on');
86782 // for punction see https://stackoverflow.com/a/21224179
86784 function simplify(str) {
86785 if (typeof str !== 'string') return '';
86786 return diacritics.remove(str.replace(/&/g, 'and').replace(/İ/ig, 'i').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\u200b-\u200f\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\ufeff\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
86789 // `resolveStrings`
86790 // Resolves the text strings for a given community index item
86793 // `item`: Object containing the community index item
86794 // `defaults`: Object containing the community index default strings
86795 // `localizerFn?`: optional function we will call to do the localization.
86796 // This function should be like the iD `t()` function that
86797 // accepts a `stringID` and returns a localized string
86800 // An Object containing all the resolved strings:
86802 // name: 'talk-ru Mailing List',
86803 // url: 'https://lists.openstreetmap.org/listinfo/talk-ru',
86804 // signupUrl: 'https://example.url/signup',
86805 // description: 'A one line description',
86806 // extendedDescription: 'Extended description',
86807 // nameHTML: '<a href="the url">the name</a>',
86808 // urlHTML: '<a href="the url">the url</a>',
86809 // signupUrlHTML: '<a href="the signupUrl">the signupUrl</a>',
86810 // descriptionHTML: the description, with urls and signupUrls linkified,
86811 // extendedDescriptionHTML: the extendedDescription with urls and signupUrls linkified
86815 function resolveStrings(item, defaults, localizerFn) {
86816 var itemStrings = Object.assign({}, item.strings); // shallow clone
86818 var defaultStrings = Object.assign({}, defaults[item.type]); // shallow clone
86820 var anyToken = new RegExp(/(\{\w+\})/, 'gi'); // Pre-localize the item and default strings
86823 if (itemStrings.community) {
86824 var communityID = simplify(itemStrings.community);
86825 itemStrings.community = localizerFn("_communities.".concat(communityID));
86828 ['name', 'description', 'extendedDescription'].forEach(function (prop) {
86829 if (defaultStrings[prop]) defaultStrings[prop] = localizerFn("_defaults.".concat(item.type, ".").concat(prop));
86830 if (itemStrings[prop]) itemStrings[prop] = localizerFn("".concat(item.id, ".").concat(prop));
86834 var replacements = {
86835 account: item.account,
86836 community: itemStrings.community,
86837 signupUrl: itemStrings.signupUrl,
86838 url: itemStrings.url
86839 }; // Resolve URLs first (which may refer to {account})
86841 if (!replacements.signupUrl) {
86842 replacements.signupUrl = resolve(itemStrings.signupUrl || defaultStrings.signupUrl);
86845 if (!replacements.url) {
86846 replacements.url = resolve(itemStrings.url || defaultStrings.url);
86850 name: resolve(itemStrings.name || defaultStrings.name),
86851 url: resolve(itemStrings.url || defaultStrings.url),
86852 signupUrl: resolve(itemStrings.signupUrl || defaultStrings.signupUrl),
86853 description: resolve(itemStrings.description || defaultStrings.description),
86854 extendedDescription: resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription)
86855 }; // Generate linkified strings
86857 resolved.nameHTML = linkify(resolved.url, resolved.name);
86858 resolved.urlHTML = linkify(resolved.url);
86859 resolved.signupUrlHTML = linkify(resolved.signupUrl);
86860 resolved.descriptionHTML = resolve(itemStrings.description || defaultStrings.description, true);
86861 resolved.extendedDescriptionHTML = resolve(itemStrings.extendedDescription || defaultStrings.extendedDescription, true);
86864 function resolve(s, addLinks) {
86865 if (!s) return undefined;
86868 for (var key in replacements) {
86869 var token = "{".concat(key, "}");
86870 var regex = new RegExp(token, 'g');
86872 if (regex.test(result)) {
86873 var replacement = replacements[key];
86875 if (!replacement) {
86876 throw new Error("Cannot resolve token: ".concat(token));
86878 if (addLinks && (key === 'signupUrl' || key === 'url')) {
86879 replacement = linkify(replacement);
86882 result = result.replace(regex, replacement);
86885 } // There shouldn't be any leftover tokens in a resolved string
86888 var leftovers = result.match(anyToken);
86891 throw new Error("Cannot resolve tokens: ".concat(leftovers));
86892 } // Linkify subreddits like `/r/openstreetmap`
86893 // https://github.com/osmlab/osm-community-index/issues/82
86894 // https://github.com/openstreetmap/iD/issues/4997
86897 if (addLinks && item.type === 'reddit') {
86898 result = result.replace(/(\/r\/\w+\/*)/i, function (match) {
86899 return linkify(resolved.url, match);
86906 function linkify(url, text) {
86907 if (!url) return undefined;
86908 text = text || url;
86909 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
86914 function uiSuccess(context) {
86916 var dispatch = dispatch$8('cancel');
86922 ensureOSMCommunityIndex(); // start fetching the data
86924 function ensureOSMCommunityIndex() {
86925 var data = _mainFileFetcher;
86926 return Promise.all([data.get('oci_features'), data.get('oci_resources'), data.get('oci_defaults')]).then(function (vals) {
86927 if (_oci) return _oci; // Merge Custom Features
86929 if (vals[0] && Array.isArray(vals[0].features)) {
86930 _mainLocations.mergeCustomGeoJSON(vals[0]);
86933 var ociResources = Object.values(vals[1].resources);
86935 if (ociResources.length) {
86936 // Resolve all locationSet features.
86937 return _mainLocations.mergeLocationSets(ociResources).then(function () {
86939 resources: ociResources,
86940 defaults: vals[2].defaults
86948 defaults: vals[2].defaults
86953 } // string-to-date parsing in JavaScript is weird
86956 function parseEventDate(when) {
86958 var raw = when.trim();
86961 if (!/Z$/.test(raw)) {
86962 // if no trailing 'Z', add one
86963 raw += 'Z'; // this forces date to be parsed as a UTC date
86966 var parsed = new Date(raw);
86967 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
86970 function success(selection) {
86971 var header = selection.append('div').attr('class', 'header fillL');
86972 header.append('h3').html(_t.html('success.just_edited'));
86973 header.append('button').attr('class', 'close').on('click', function () {
86974 return dispatch.call('cancel');
86975 }).call(svgIcon('#iD-icon-close'));
86976 var body = selection.append('div').attr('class', 'body save-success fillL');
86977 var summary = body.append('div').attr('class', 'save-summary');
86978 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
86981 summary.append('p').html(_t.html('success.help_html')).append('a').attr('class', 'link-out').attr('target', '_blank').attr('href', _t('success.help_link_url')).call(svgIcon('#iD-icon-out-link', 'inline')).append('span').html(_t.html('success.help_link_text'));
86982 var osm = context.connection();
86984 var changesetURL = osm.changesetURL(_changeset.id);
86985 var table = summary.append('table').attr('class', 'summary-table');
86986 var row = table.append('tr').attr('class', 'summary-row');
86987 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');
86988 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
86989 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
86990 summaryDetail.append('div').html(_t.html('success.changeset_id', {
86991 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
86992 })); // Get OSM community index features intersecting the map..
86994 ensureOSMCommunityIndex().then(function (oci) {
86995 var loc = context.map().center();
86996 var validLocations = _mainLocations.locationsAt(loc); // Gather the communities
86998 var communities = [];
86999 oci.resources.forEach(function (resource) {
87000 var area = validLocations[resource.locationSetID];
87001 if (!area) return; // Resolve strings
87003 var localizer = function localizer(stringID) {
87004 return _t.html("community.".concat(stringID));
87007 resource.resolved = resolveStrings(resource, oci.defaults, localizer);
87010 order: resource.order || 0,
87013 }); // sort communities by feature area ascending, community order descending
87015 communities.sort(function (a, b) {
87016 return a.area - b.area || b.order - a.order;
87018 body.call(showCommunityLinks, communities.map(function (c) {
87024 function showCommunityLinks(selection, resources) {
87025 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
87026 communityLinks.append('h3').html(_t.html('success.like_osm'));
87027 var table = communityLinks.append('table').attr('class', 'community-table');
87028 var row = table.selectAll('.community-row').data(resources);
87029 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
87030 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
87031 return d.resolved.url;
87032 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
87033 return "#community-".concat(d.type);
87035 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
87036 communityDetail.each(showCommunityDetails);
87037 communityLinks.append('div').attr('class', 'community-missing').html(_t.html('success.missing')).append('a').attr('class', 'link-out').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/osmlab/osm-community-index/issues').append('span').html(_t.html('success.tell_us'));
87040 function showCommunityDetails(d) {
87041 var selection = select(this);
87042 var communityID = d.id;
87043 selection.append('div').attr('class', 'community-name').html(d.resolved.nameHTML);
87044 selection.append('div').attr('class', 'community-description').html(d.resolved.descriptionHTML); // Create an expanding section if any of these are present..
87046 if (d.resolved.extendedDescriptionHTML || d.languageCodes && d.languageCodes.length) {
87047 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
87050 var nextEvents = (d.events || []).map(function (event) {
87051 event.date = parseEventDate(event.when);
87053 }).filter(function (event) {
87054 // date is valid and future (or today)
87055 var t = event.date.getTime();
87056 var now = new Date().setHours(0, 0, 0, 0);
87057 return !isNaN(t) && t >= now;
87058 }).sort(function (a, b) {
87059 // sort by date ascending
87060 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
87061 }).slice(0, MAXEVENTS); // limit number of events shown
87063 if (nextEvents.length) {
87064 selection.append('div').call(uiDisclosure(context, "community-events-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.events')).content(showNextEvents)).select('.hide-toggle').append('span').attr('class', 'badge-text').html(nextEvents.length);
87067 function showMore(selection) {
87068 var more = selection.selectAll('.community-more').data([0]);
87069 var moreEnter = more.enter().append('div').attr('class', 'community-more');
87071 if (d.resolved.extendedDescriptionHTML) {
87072 moreEnter.append('div').attr('class', 'community-extended-description').html(d.resolved.extendedDescriptionHTML);
87075 if (d.languageCodes && d.languageCodes.length) {
87076 var languageList = d.languageCodes.map(function (code) {
87077 return _mainLocalizer.languageName(code);
87079 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
87080 languages: languageList
87085 function showNextEvents(selection) {
87086 var events = selection.append('div').attr('class', 'community-events');
87087 var item = events.selectAll('.community-event').data(nextEvents);
87088 var itemEnter = item.enter().append('div').attr('class', 'community-event');
87089 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
87091 }).html(function (d) {
87094 if (d.i18n && d.id) {
87095 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
87102 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
87110 if (d.date.getHours() || d.date.getMinutes()) {
87111 // include time if it has one
87112 options.hour = 'numeric';
87113 options.minute = 'numeric';
87116 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
87118 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
87119 var where = d.where;
87121 if (d.i18n && d.id) {
87122 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
87129 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
87130 var description = d.description;
87132 if (d.i18n && d.id) {
87133 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
87134 "default": description
87138 return description;
87143 success.changeset = function (val) {
87144 if (!arguments.length) return _changeset;
87149 success.location = function (val) {
87150 if (!arguments.length) return _location;
87155 return utilRebind(success, dispatch, 'on');
87158 function modeSave(context) {
87162 var keybinding = utilKeybinding('modeSave');
87163 var commit = uiCommit(context).on('cancel', cancel);
87165 var _conflictsUi; // uiConflicts
87172 var uploader = context.uploader().on('saveStarted.modeSave', function () {
87174 }) // fire off some async work that we want to be ready later
87175 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
87177 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
87179 function cancel() {
87180 context.enter(modeBrowse(context));
87183 function showProgress(num, total) {
87184 var modal = context.container().select('.loading-modal .modal-section');
87185 var progress = modal.selectAll('.progress').data([0]); // enter/update
87187 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
87193 function showConflicts(changeset, conflicts, origChanges) {
87194 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
87195 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
87196 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
87197 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87198 selection.remove();
87200 uploader.cancelConflictResolution();
87201 }).on('save', function () {
87202 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87203 selection.remove();
87204 uploader.processResolvedConflicts(changeset);
87206 selection.call(_conflictsUi);
87209 function showErrors(errors) {
87211 var selection = uiConfirm(context.container());
87212 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
87213 addErrors(selection, errors);
87214 selection.okButton();
87217 function addErrors(selection, data) {
87218 var message = selection.select('.modal-section.message-text');
87219 var items = message.selectAll('.error-container').data(data);
87220 var enter = items.enter().append('div').attr('class', 'error-container');
87221 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
87222 return d.msg || _t('save.unknown_error_details');
87223 }).on('click', function (d3_event) {
87224 d3_event.preventDefault();
87225 var error = select(this);
87226 var detail = select(this.nextElementSibling);
87227 var exp = error.classed('expanded');
87228 detail.style('display', exp ? 'none' : 'block');
87229 error.classed('expanded', !exp);
87231 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
87232 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
87233 return d.details || [];
87234 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
87237 items.exit().remove();
87240 function showSuccess(changeset) {
87243 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
87244 context.ui().sidebar.hide();
87247 context.enter(modeBrowse(context).sidebar(ui));
87250 function keybindingOn() {
87251 select(document).call(keybinding.on('⎋', cancel, true));
87254 function keybindingOff() {
87255 select(document).call(keybinding.unbind);
87256 } // Reverse geocode current map location so we can display a message on
87257 // the success screen like "Thank you for editing around place, region."
87260 function prepareForSuccess() {
87261 _success = uiSuccess(context);
87263 if (!services.geocoder) return;
87264 services.geocoder.reverse(context.map().center(), function (err, result) {
87265 if (err || !result || !result.address) return;
87266 var addr = result.address;
87267 var place = addr && (addr.town || addr.city || addr.county) || '';
87268 var region = addr && (addr.state || addr.country) || '';
87269 var separator = place && region ? _t('success.thank_you_where.separator') : '';
87270 _location = _t('success.thank_you_where.format', {
87272 separator: separator,
87278 mode.selectedIDs = function () {
87279 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
87282 mode.enter = function () {
87284 context.ui().sidebar.expand();
87287 context.ui().sidebar.show(commit);
87291 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
87292 var osm = context.connection();
87299 if (osm.authenticated()) {
87302 osm.authenticate(function (err) {
87312 mode.exit = function () {
87314 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
87315 context.ui().sidebar.hide();
87321 function modeSelectError(context, selectedErrorID, selectedErrorService) {
87323 id: 'select-error',
87326 var keybinding = utilKeybinding('select-error');
87327 var errorService = services[selectedErrorService];
87330 switch (selectedErrorService) {
87332 errorEditor = uiImproveOsmEditor(context).on('change', function () {
87333 context.map().pan([0, 0]); // trigger a redraw
87335 var error = checkSelectedID();
87336 if (!error) return;
87337 context.ui().sidebar.show(errorEditor.error(error));
87342 errorEditor = uiKeepRightEditor(context).on('change', function () {
87343 context.map().pan([0, 0]); // trigger a redraw
87345 var error = checkSelectedID();
87346 if (!error) return;
87347 context.ui().sidebar.show(errorEditor.error(error));
87352 errorEditor = uiOsmoseEditor(context).on('change', function () {
87353 context.map().pan([0, 0]); // trigger a redraw
87355 var error = checkSelectedID();
87356 if (!error) return;
87357 context.ui().sidebar.show(errorEditor.error(error));
87362 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
87364 function checkSelectedID() {
87365 if (!errorService) return;
87366 var error = errorService.getError(selectedErrorID);
87369 context.enter(modeBrowse(context));
87375 mode.zoomToSelected = function () {
87376 if (!errorService) return;
87377 var error = errorService.getError(selectedErrorID);
87380 context.map().centerZoomEase(error.loc, 20);
87384 mode.enter = function () {
87385 var error = checkSelectedID();
87386 if (!error) return;
87387 behaviors.forEach(context.install);
87388 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
87389 select(document).call(keybinding);
87391 var sidebar = context.ui().sidebar;
87392 sidebar.show(errorEditor.error(error));
87393 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
87395 function selectError(d3_event, drawn) {
87396 if (!checkSelectedID()) return;
87397 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
87399 if (selection.empty()) {
87400 // Return to browse mode if selected DOM elements have
87401 // disappeared because the user moved them out of view..
87402 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
87404 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
87405 context.enter(modeBrowse(context));
87408 selection.classed('selected', true);
87409 context.selectedErrorID(selectedErrorID);
87414 if (context.container().select('.combobox').size()) return;
87415 context.enter(modeBrowse(context));
87419 mode.exit = function () {
87420 behaviors.forEach(context.uninstall);
87421 select(document).call(keybinding.unbind);
87422 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
87423 context.map().on('drawn.select-error', null);
87424 context.ui().sidebar.hide();
87425 context.selectedErrorID(null);
87426 context.features().forceVisible([]);
87432 function uiToolOldDrawModes(context) {
87435 label: _t.html('toolbar.add_feature')
87437 var modes = [modeAddPoint(context, {
87438 title: _t.html('modes.add_point.title'),
87440 description: _t.html('modes.add_point.description'),
87441 preset: _mainPresetIndex.item('point'),
87443 }), modeAddLine(context, {
87444 title: _t.html('modes.add_line.title'),
87446 description: _t.html('modes.add_line.description'),
87447 preset: _mainPresetIndex.item('line'),
87449 }), modeAddArea(context, {
87450 title: _t.html('modes.add_area.title'),
87452 description: _t.html('modes.add_area.description'),
87453 preset: _mainPresetIndex.item('area'),
87457 function enabled() {
87458 return osmEditable();
87461 function osmEditable() {
87462 return context.editable();
87465 modes.forEach(function (mode) {
87466 context.keybinding().on(mode.key, function () {
87467 if (!enabled()) return;
87469 if (mode.id === context.mode().id) {
87470 context.enter(modeBrowse(context));
87472 context.enter(mode);
87477 tool.render = function (selection) {
87478 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
87480 var debouncedUpdate = debounce(update, 500, {
87485 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
87486 context.on('enter.modes', update);
87489 function update() {
87490 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
87494 buttons.exit().remove(); // enter
87496 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
87497 return d.id + ' add-button bar-button';
87498 }).on('click.mode-buttons', function (d3_event, d) {
87499 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
87501 var currMode = context.mode().id;
87502 if (/^draw/.test(currMode)) return;
87504 if (d.id === currMode) {
87505 context.enter(modeBrowse(context));
87509 }).call(uiTooltip().placement('bottom').title(function (d) {
87510 return d.description;
87511 }).keys(function (d) {
87513 }).scrollContainer(context.container().select('.top-toolbar')));
87514 buttonsEnter.each(function (d) {
87515 select(this).call(svgIcon('#iD-icon-' + d.button));
87517 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
87519 }); // if we are adding/removing the buttons, check if toolbar has overflowed
87521 if (buttons.enter().size() || buttons.exit().size()) {
87522 context.ui().checkOverflow('.top-toolbar', true);
87526 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
87528 }).classed('active', function (d) {
87529 return context.mode() && context.mode().button === d.button;
87537 function uiToolNotes(context) {
87540 label: _t.html('modes.add_note.label')
87542 var mode = modeAddNote(context);
87544 function enabled() {
87545 return notesEnabled() && notesEditable();
87548 function notesEnabled() {
87549 var noteLayer = context.layers().layer('notes');
87550 return noteLayer && noteLayer.enabled();
87553 function notesEditable() {
87554 var mode = context.mode();
87555 return context.map().notesEditable() && mode && mode.id !== 'save';
87558 context.keybinding().on(mode.key, function () {
87559 if (!enabled()) return;
87561 if (mode.id === context.mode().id) {
87562 context.enter(modeBrowse(context));
87564 context.enter(mode);
87568 tool.render = function (selection) {
87569 var debouncedUpdate = debounce(update, 500, {
87574 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
87575 context.on('enter.notes', update);
87578 function update() {
87579 var showNotes = notesEnabled();
87580 var data = showNotes ? [mode] : [];
87581 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
87585 buttons.exit().remove(); // enter
87587 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
87588 return d.id + ' add-button bar-button';
87589 }).on('click.notes', function (d3_event, d) {
87590 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
87592 var currMode = context.mode().id;
87593 if (/^draw/.test(currMode)) return;
87595 if (d.id === currMode) {
87596 context.enter(modeBrowse(context));
87600 }).call(uiTooltip().placement('bottom').title(function (d) {
87601 return d.description;
87602 }).keys(function (d) {
87604 }).scrollContainer(context.container().select('.top-toolbar')));
87605 buttonsEnter.each(function (d) {
87606 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
87607 }); // if we are adding/removing the buttons, check if toolbar has overflowed
87609 if (buttons.enter().size() || buttons.exit().size()) {
87610 context.ui().checkOverflow('.top-toolbar', true);
87614 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
87616 }).classed('active', function (d) {
87617 return context.mode() && context.mode().button === d.button;
87622 tool.uninstall = function () {
87623 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
87624 context.map().on('move.notes', null).on('drawn.notes', null);
87630 function uiToolSave(context) {
87633 label: _t.html('save.title')
87636 var tooltipBehavior = null;
87637 var history = context.history();
87638 var key = uiCmd('⌘S');
87639 var _numChanges = 0;
87641 function isSaving() {
87642 var mode = context.mode();
87643 return mode && mode.id === 'save';
87646 function isDisabled() {
87647 return _numChanges === 0 || isSaving();
87650 function save(d3_event) {
87651 d3_event.preventDefault();
87653 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
87654 context.enter(modeSave(context));
87658 function bgColor() {
87661 if (_numChanges === 0) {
87663 } else if (_numChanges <= 50) {
87664 step = _numChanges / 50;
87665 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
87667 step = Math.min((_numChanges - 50) / 50, 1.0);
87668 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
87672 function updateCount() {
87673 var val = history.difference().summary().length;
87674 if (val === _numChanges) return;
87677 if (tooltipBehavior) {
87678 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
87682 button.classed('disabled', isDisabled()).style('background', bgColor());
87683 button.select('span.count').html(_numChanges);
87687 tool.render = function (selection) {
87688 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
87689 var lastPointerUpType;
87690 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
87691 lastPointerUpType = d3_event.pointerType;
87692 }).on('click', function (d3_event) {
87695 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
87696 // there are no tooltips for touch interactions so flash feedback instead
87697 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
87700 lastPointerUpType = null;
87701 }).call(tooltipBehavior);
87702 button.call(svgIcon('#iD-icon-save'));
87703 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
87705 context.keybinding().on(key, save, true);
87706 context.history().on('change.save', updateCount);
87707 context.on('enter.save', function () {
87709 button.classed('disabled', isDisabled());
87712 button.call(tooltipBehavior.hide);
87718 tool.uninstall = function () {
87719 context.keybinding().off(key, true);
87720 context.history().on('change.save', null);
87721 context.on('enter.save', null);
87723 tooltipBehavior = null;
87729 function uiToolSidebarToggle(context) {
87731 id: 'sidebar_toggle',
87732 label: _t.html('toolbar.inspect')
87735 tool.render = function (selection) {
87736 selection.append('button').attr('class', 'bar-button').on('click', function () {
87737 context.ui().sidebar.toggle();
87738 }).call(uiTooltip().placement('bottom').title(_t.html('sidebar.tooltip')).keys([_t('sidebar.key')]).scrollContainer(context.container().select('.top-toolbar'))).call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
87744 function uiToolUndoRedo(context) {
87747 label: _t.html('toolbar.undo_redo')
87752 action: function action() {
87755 annotation: function annotation() {
87756 return context.history().undoAnnotation();
87758 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
87762 action: function action() {
87765 annotation: function annotation() {
87766 return context.history().redoAnnotation();
87768 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
87771 function editable() {
87772 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
87773 /* ignore min zoom */
87777 tool.render = function (selection) {
87778 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
87779 return d.annotation() ? _t.html(d.id + '.tooltip', {
87780 action: d.annotation()
87781 }) : _t.html(d.id + '.nothing');
87782 }).keys(function (d) {
87784 }).scrollContainer(context.container().select('.top-toolbar'));
87785 var lastPointerUpType;
87786 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
87787 return 'disabled ' + d.id + '-button bar-button';
87788 }).on('pointerup', function (d3_event) {
87789 // `pointerup` is always called before `click`
87790 lastPointerUpType = d3_event.pointerType;
87791 }).on('click', function (d3_event, d) {
87792 d3_event.preventDefault();
87793 var annotation = d.annotation();
87795 if (editable() && annotation) {
87799 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
87800 // there are no tooltips for touch interactions so flash feedback instead
87801 var text = annotation ? _t(d.id + '.tooltip', {
87803 }) : _t(d.id + '.nothing');
87804 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
87807 lastPointerUpType = null;
87808 }).call(tooltipBehavior);
87809 buttons.each(function (d) {
87810 select(this).call(svgIcon('#' + d.icon));
87812 context.keybinding().on(commands[0].cmd, function (d3_event) {
87813 d3_event.preventDefault();
87814 if (editable()) commands[0].action();
87815 }).on(commands[1].cmd, function (d3_event) {
87816 d3_event.preventDefault();
87817 if (editable()) commands[1].action();
87820 var debouncedUpdate = debounce(update, 500, {
87825 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
87826 context.history().on('change.undo_redo', function (difference) {
87827 if (difference) update();
87829 context.on('enter.undo_redo', update);
87831 function update() {
87832 buttons.classed('disabled', function (d) {
87833 return !editable() || !d.annotation();
87834 }).each(function () {
87835 var selection = select(this);
87837 if (!selection.select('.tooltip.in').empty()) {
87838 selection.call(tooltipBehavior.updateContent);
87844 tool.uninstall = function () {
87845 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
87846 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
87847 context.history().on('change.undo_redo', null);
87848 context.on('enter.undo_redo', null);
87854 function uiTopToolbar(context) {
87855 var sidebarToggle = uiToolSidebarToggle(context),
87856 modes = uiToolOldDrawModes(context),
87857 notes = uiToolNotes(context),
87858 undoRedo = uiToolUndoRedo(context),
87859 save = uiToolSave(context);
87861 function notesEnabled() {
87862 var noteLayer = context.layers().layer('notes');
87863 return noteLayer && noteLayer.enabled();
87866 function topToolbar(bar) {
87867 bar.on('wheel.topToolbar', function (d3_event) {
87868 if (!d3_event.deltaX) {
87869 // translate vertical scrolling into horizontal scrolling in case
87870 // the user doesn't have an input device that can scroll horizontally
87871 bar.node().scrollLeft += d3_event.deltaY;
87875 var debouncedUpdate = debounce(update, 500, {
87880 context.layers().on('change.topToolbar', debouncedUpdate);
87883 function update() {
87884 var tools = [sidebarToggle, 'spacer', modes];
87885 tools.push('spacer');
87887 if (notesEnabled()) {
87888 tools = tools.concat([notes, 'spacer']);
87891 tools = tools.concat([undoRedo, save]);
87892 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
87895 toolbarItems.exit().each(function (d) {
87900 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
87901 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
87902 if (d.klass) classes += ' ' + d.klass;
87905 var actionableItems = itemsEnter.filter(function (d) {
87906 return d !== 'spacer';
87908 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
87909 select(this).call(d.render, bar);
87911 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
87920 var sawVersion = null;
87921 var isNewVersion = false;
87922 var isNewUser = false;
87923 function uiVersion(context) {
87924 var currVersion = context.version;
87925 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
87927 if (sawVersion === null && matchedVersion !== null) {
87928 if (corePreferences('sawVersion')) {
87930 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
87933 isNewVersion = true;
87936 corePreferences('sawVersion', currVersion);
87937 sawVersion = currVersion;
87940 return function (selection) {
87941 selection.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD').html(currVersion); // only show new version indicator to users that have used iD before
87943 if (isNewVersion && !isNewUser) {
87944 selection.append('a').attr('class', 'badge').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.html('version.whats_new', {
87945 version: currVersion
87946 })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
87951 function uiZoom(context) {
87954 icon: 'iD-icon-plus',
87955 title: _t.html('zoom.in'),
87957 disabled: function disabled() {
87958 return !context.map().canZoomIn();
87960 disabledTitle: _t.html('zoom.disabled.in'),
87964 icon: 'iD-icon-minus',
87965 title: _t.html('zoom.out'),
87967 disabled: function disabled() {
87968 return !context.map().canZoomOut();
87970 disabledTitle: _t.html('zoom.disabled.out'),
87974 function zoomIn(d3_event) {
87975 if (d3_event.shiftKey) return;
87976 d3_event.preventDefault();
87977 context.map().zoomIn();
87980 function zoomOut(d3_event) {
87981 if (d3_event.shiftKey) return;
87982 d3_event.preventDefault();
87983 context.map().zoomOut();
87986 function zoomInFurther(d3_event) {
87987 if (d3_event.shiftKey) return;
87988 d3_event.preventDefault();
87989 context.map().zoomInFurther();
87992 function zoomOutFurther(d3_event) {
87993 if (d3_event.shiftKey) return;
87994 d3_event.preventDefault();
87995 context.map().zoomOutFurther();
87998 return function (selection) {
87999 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
88000 if (d.disabled()) {
88001 return d.disabledTitle;
88005 }).keys(function (d) {
88008 var lastPointerUpType;
88009 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
88011 }).on('pointerup.editor', function (d3_event) {
88012 lastPointerUpType = d3_event.pointerType;
88013 }).on('click.editor', function (d3_event, d) {
88014 if (!d.disabled()) {
88015 d.action(d3_event);
88016 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
88017 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
88020 lastPointerUpType = null;
88021 }).call(tooltipBehavior);
88022 buttons.each(function (d) {
88023 select(this).call(svgIcon('#' + d.icon, 'light'));
88025 utilKeybinding.plusKeys.forEach(function (key) {
88026 context.keybinding().on([key], zoomIn);
88027 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
88029 utilKeybinding.minusKeys.forEach(function (key) {
88030 context.keybinding().on([key], zoomOut);
88031 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
88034 function updateButtonStates() {
88035 buttons.classed('disabled', function (d) {
88036 return d.disabled();
88037 }).each(function () {
88038 var selection = select(this);
88040 if (!selection.select('.tooltip.in').empty()) {
88041 selection.call(tooltipBehavior.updateContent);
88046 updateButtonStates();
88047 context.map().on('move.uiZoom', updateButtonStates);
88051 function uiZoomToSelection(context) {
88052 function isDisabled() {
88053 var mode = context.mode();
88054 return !mode || !mode.zoomToSelected;
88057 var _lastPointerUpType;
88059 function pointerup(d3_event) {
88060 _lastPointerUpType = d3_event.pointerType;
88063 function click(d3_event) {
88064 d3_event.preventDefault();
88066 if (isDisabled()) {
88067 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
88068 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
88071 var mode = context.mode();
88073 if (mode && mode.zoomToSelected) {
88074 mode.zoomToSelected();
88078 _lastPointerUpType = null;
88081 return function (selection) {
88082 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
88083 if (isDisabled()) {
88084 return _t.html('inspector.zoom_to.no_selection');
88087 return _t.html('inspector.zoom_to.title');
88088 }).keys([_t('inspector.zoom_to.key')]);
88089 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
88091 function setEnabledState() {
88092 button.classed('disabled', isDisabled());
88094 if (!button.select('.tooltip.in').empty()) {
88095 button.call(tooltipBehavior.updateContent);
88099 context.on('enter.uiZoomToSelection', setEnabledState);
88104 function uiPane(id, context) {
88108 var _description = '';
88109 var _iconName = '';
88111 var _sections; // array of uiSection objects
88114 var _paneSelection = select(null);
88122 pane.label = function (val) {
88123 if (!arguments.length) return _label;
88128 pane.key = function (val) {
88129 if (!arguments.length) return _key;
88134 pane.description = function (val) {
88135 if (!arguments.length) return _description;
88136 _description = val;
88140 pane.iconName = function (val) {
88141 if (!arguments.length) return _iconName;
88146 pane.sections = function (val) {
88147 if (!arguments.length) return _sections;
88152 pane.selection = function () {
88153 return _paneSelection;
88156 function hidePane() {
88157 context.ui().togglePanes();
88160 pane.togglePane = function (d3_event) {
88161 if (d3_event) d3_event.preventDefault();
88163 _paneTooltip.hide();
88165 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
88168 pane.renderToggleButton = function (selection) {
88169 if (!_paneTooltip) {
88170 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
88173 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
88176 pane.renderContent = function (selection) {
88177 // override to fully customize content
88179 _sections.forEach(function (section) {
88180 selection.call(section.render);
88185 pane.renderPane = function (selection) {
88186 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
88188 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
88190 heading.append('h2').html(_label);
88191 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
88193 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
88196 context.keybinding().on(_key, pane.togglePane);
88203 function uiSectionBackgroundDisplayOptions(context) {
88204 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
88206 var _detected = utilDetect();
88208 var _storedOpacity = corePreferences('background-opacity');
88212 var _maxVal = _detected.cssfilters ? 3 : 1;
88214 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
88217 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
88223 function clamp(x, min, max) {
88224 return Math.max(min, Math.min(x, max));
88227 function updateValue(d, val) {
88228 val = clamp(val, _minVal, _maxVal);
88230 context.background()[d](val);
88232 if (d === 'brightness') {
88233 corePreferences('background-opacity', val);
88236 section.reRender();
88239 function renderDisclosureContent(selection) {
88240 var container = selection.selectAll('.display-options-container').data([0]);
88241 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
88243 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
88244 return 'display-control display-control-' + d;
88246 slidersEnter.append('h5').html(function (d) {
88247 return _t.html('background.' + d);
88248 }).append('span').attr('class', function (d) {
88249 return 'display-option-value display-option-value-' + d;
88251 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
88252 sildersControlEnter.append('input').attr('class', function (d) {
88253 return 'display-option-input display-option-input-' + d;
88254 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
88255 var val = select(this).property('value');
88257 if (!val && d3_event && d3_event.target) {
88258 val = d3_event.target.value;
88261 updateValue(d, val);
88263 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
88264 return 'display-option-reset display-option-reset-' + d;
88265 }).on('click', function (d3_event, d) {
88266 if (d3_event.button !== 0) return;
88268 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
88270 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
88271 d3_event.preventDefault();
88273 for (var i = 0; i < _sliders.length; i++) {
88274 updateValue(_sliders[i], 1);
88278 container = containerEnter.merge(container);
88279 container.selectAll('.display-option-input').property('value', function (d) {
88280 return _options[d];
88282 container.selectAll('.display-option-value').html(function (d) {
88283 return Math.floor(_options[d] * 100) + '%';
88285 container.selectAll('.display-option-reset').classed('disabled', function (d) {
88286 return _options[d] === 1;
88287 }); // first time only, set brightness if needed
88289 if (containerEnter.size() && _options.brightness !== 1) {
88290 context.background().brightness(_options.brightness);
88297 function uiSettingsCustomBackground() {
88298 var dispatch = dispatch$8('change');
88300 function render(selection) {
88301 // keep separate copies of original and current settings
88302 var _origSettings = {
88303 template: corePreferences('background-custom-template')
88305 var _currSettings = {
88306 template: corePreferences('background-custom-template')
88308 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
88309 var modal = uiConfirm(selection).okButton();
88310 modal.classed('settings-modal settings-custom-background', true);
88311 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
88312 var textSection = modal.select('.modal-section.message-text');
88313 var instructions = "".concat(_t.html('settings.custom_background.instructions.info'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.wms.tokens_label'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.proj'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.wkid'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.dimensions'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.wms.tokens.bbox'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.tms.tokens_label'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.xyz'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.flipped_y'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.switch'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.quadtile'), "\n") + "* ".concat(_t.html('settings.custom_background.instructions.tms.tokens.scale_factor'), "\n") + '\n' + "#### ".concat(_t.html('settings.custom_background.instructions.example'), "\n") + "`".concat(example, "`");
88314 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
88315 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
88317 var buttonSection = modal.select('.modal-section.buttons');
88318 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
88319 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
88320 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
88322 function isSaveDisabled() {
88324 } // restore the original template
88327 function clickCancel() {
88328 textSection.select('.field-template').property('value', _origSettings.template);
88329 corePreferences('background-custom-template', _origSettings.template);
88332 } // accept the current template
88335 function clickSave() {
88336 _currSettings.template = textSection.select('.field-template').property('value');
88337 corePreferences('background-custom-template', _currSettings.template);
88340 dispatch.call('change', this, _currSettings);
88344 return utilRebind(render, dispatch, 'on');
88347 function uiSectionBackgroundList(context) {
88348 var _backgroundList = select(null);
88350 var _customSource = context.background().findSource('custom');
88352 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
88354 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
88356 function previousBackgroundID() {
88357 return corePreferences('background-last-used-toggle');
88360 function renderDisclosureContent(selection) {
88361 // the background list
88362 var container = selection.selectAll('.layer-background-list').data([0]);
88363 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
88365 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
88366 var minimapLabelEnter = bgExtrasListEnter.append('li').attr('class', 'minimap-toggle-item').append('label').call(uiTooltip().title(_t.html('background.minimap.tooltip')).keys([_t('background.minimap.key')]).placement('top'));
88367 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88368 d3_event.preventDefault();
88369 uiMapInMap.toggle();
88371 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
88372 var panelLabelEnter = bgExtrasListEnter.append('li').attr('class', 'background-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('background.panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))]).placement('top'));
88373 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88374 d3_event.preventDefault();
88375 context.ui().info.toggle('background');
88377 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
88378 var locPanelLabelEnter = bgExtrasListEnter.append('li').attr('class', 'location-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('background.location_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))]).placement('top'));
88379 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
88380 d3_event.preventDefault();
88381 context.ui().info.toggle('location');
88383 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
88385 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').html(_t.html('background.imagery_problem_faq'));
88387 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
88388 chooseBackground(d);
88390 return !d.isHidden() && !d.overlay;
88394 function setTooltips(selection) {
88395 selection.each(function (d, i, nodes) {
88396 var item = select(this).select('label');
88397 var span = item.select('span');
88398 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
88399 var description = d.description();
88400 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
88401 item.call(uiTooltip().destroyAny);
88403 if (d.id === previousBackgroundID()) {
88404 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
88405 } else if (description || isOverflowing) {
88406 item.call(uiTooltip().placement(placement).title(description || d.label()));
88411 function drawListItems(layerList, type, change, filter) {
88412 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
88413 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
88415 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
88416 // arrow key navigation of radio values likes to work in the order
88417 // they were added, not the display document order.
88418 .data(sources, function (d, i) {
88419 return d.id + '---' + i;
88421 layerLinks.exit().remove();
88422 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
88423 return d.id === 'custom';
88424 }).classed('best', function (d) {
88427 var label = enter.append('label');
88428 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
88430 }).on('change', change);
88431 label.append('span').html(function (d) {
88434 enter.filter(function (d) {
88435 return d.id === 'custom';
88436 }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
88437 d3_event.preventDefault();
88439 }).call(svgIcon('#iD-icon-more'));
88440 enter.filter(function (d) {
88442 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
88443 layerList.call(updateLayerSelections);
88446 function updateLayerSelections(selection) {
88447 function active(d) {
88448 return context.background().showsLayer(d);
88451 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
88452 return d.id === previousBackgroundID();
88453 }).call(setTooltips).selectAll('input').property('checked', active);
88456 function chooseBackground(d) {
88457 if (d.id === 'custom' && !d.template()) {
88458 return editCustom();
88461 var previousBackground = context.background().baseLayerSource();
88462 corePreferences('background-last-used-toggle', previousBackground.id);
88463 corePreferences('background-last-used', d.id);
88464 context.background().baseLayerSource(d);
88467 function customChanged(d) {
88468 if (d && d.template) {
88469 _customSource.template(d.template);
88471 chooseBackground(_customSource);
88473 _customSource.template('');
88475 chooseBackground(context.background().findSource('none'));
88479 function editCustom() {
88480 context.container().call(_settingsCustomBackground);
88483 context.background().on('change.background_list', function () {
88484 _backgroundList.call(updateLayerSelections);
88486 context.map().on('move.background_list', debounce(function () {
88487 // layers in-view may have changed due to map move
88488 window.requestIdleCallback(section.reRender);
88493 function uiSectionBackgroundOffset(context) {
88494 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
88496 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
88498 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
88500 function updateValue() {
88501 var meters = geoOffsetToMeters(context.background().offset());
88502 var x = +meters[0].toFixed(2);
88503 var y = +meters[1].toFixed(2);
88504 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
88505 context.container().selectAll('.nudge-reset').classed('disabled', function () {
88506 return x === 0 && y === 0;
88510 function resetOffset() {
88511 context.background().offset([0, 0]);
88515 function nudge(d) {
88516 context.background().nudge(d, context.map().zoom());
88520 function inputOffset() {
88521 var input = select(this);
88522 var d = input.node().value;
88523 if (d === '') return resetOffset();
88524 d = d.replace(/;/g, ',').split(',').map(function (n) {
88525 // if n is NaN, it will always get mapped to false.
88526 return !isNaN(n) && n;
88529 if (d.length !== 2 || !d[0] || !d[1]) {
88530 input.classed('error', true);
88534 context.background().offset(geoMetersToOffset(d));
88538 function dragOffset(d3_event) {
88539 if (d3_event.button !== 0) return;
88540 var origin = [d3_event.clientX, d3_event.clientY];
88541 var pointerId = d3_event.pointerId || 'mouse';
88542 context.container().append('div').attr('class', 'nudge-surface');
88543 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
88545 if (_pointerPrefix === 'pointer') {
88546 select(window).on('pointercancel.drag-bg-offset', pointerup);
88549 function pointermove(d3_event) {
88550 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
88551 var latest = [d3_event.clientX, d3_event.clientY];
88552 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
88557 function pointerup(d3_event) {
88558 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
88559 if (d3_event.button !== 0) return;
88560 context.container().selectAll('.nudge-surface').remove();
88561 select(window).on('.drag-bg-offset', null);
88565 function renderDisclosureContent(selection) {
88566 var container = selection.selectAll('.nudge-container').data([0]);
88567 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
88568 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
88569 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
88570 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
88571 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
88572 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
88573 return d[0] + ' nudge';
88574 }).on('click', function (d3_event, d) {
88577 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
88578 d3_event.preventDefault();
88580 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
88584 context.background().on('change.backgroundOffset-update', updateValue);
88588 function uiSectionOverlayList(context) {
88589 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
88591 var _overlayList = select(null);
88593 function setTooltips(selection) {
88594 selection.each(function (d, i, nodes) {
88595 var item = select(this).select('label');
88596 var span = item.select('span');
88597 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
88598 var description = d.description();
88599 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
88600 item.call(uiTooltip().destroyAny);
88602 if (description || isOverflowing) {
88603 item.call(uiTooltip().placement(placement).title(description || d.name()));
88608 function updateLayerSelections(selection) {
88609 function active(d) {
88610 return context.background().showsLayer(d);
88613 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
88616 function chooseOverlay(d3_event, d) {
88617 d3_event.preventDefault();
88618 context.background().toggleOverlayLayer(d);
88620 _overlayList.call(updateLayerSelections);
88622 document.activeElement.blur();
88625 function drawListItems(layerList, type, change, filter) {
88626 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
88627 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
88630 layerLinks.exit().remove();
88631 var enter = layerLinks.enter().append('li');
88632 var label = enter.append('label');
88633 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
88634 label.append('span').html(function (d) {
88637 layerList.selectAll('li').sort(sortSources);
88638 layerList.call(updateLayerSelections);
88640 function sortSources(a, b) {
88641 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
88645 function renderDisclosureContent(selection) {
88646 var container = selection.selectAll('.layer-overlay-list').data([0]);
88647 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
88649 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
88650 return !d.isHidden() && d.overlay;
88654 context.map().on('move.overlay_list', debounce(function () {
88655 // layers in-view may have changed due to map move
88656 window.requestIdleCallback(section.reRender);
88661 function uiPaneBackground(context) {
88662 var backgroundPane = uiPane('background', context).key(_t('background.key')).label(_t.html('background.title')).description(_t.html('background.description')).iconName('iD-icon-layers').sections([uiSectionBackgroundList(context), uiSectionOverlayList(context), uiSectionBackgroundDisplayOptions(context), uiSectionBackgroundOffset(context)]);
88663 return backgroundPane;
88666 function uiPaneHelp(context) {
88667 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']], ['operations', ['intro', 'intro_2', 'straighten', 'orthogonalize', 'circularize', 'move', 'rotate', 'reflect', 'continue', 'reverse', 'disconnect', 'split', 'extract', 'merge', 'delete', 'downgrade', 'copy_paste']], ['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']]];
88669 'help.help.open_data_h': 3,
88670 'help.help.before_start_h': 3,
88671 'help.help.open_source_h': 3,
88672 'help.overview.navigation_h': 3,
88673 'help.overview.features_h': 3,
88674 'help.editing.select_h': 3,
88675 'help.editing.multiselect_h': 3,
88676 'help.editing.undo_redo_h': 3,
88677 'help.editing.save_h': 3,
88678 'help.editing.upload_h': 3,
88679 'help.editing.backups_h': 3,
88680 'help.editing.keyboard_h': 3,
88681 'help.feature_editor.type_h': 3,
88682 'help.feature_editor.fields_h': 3,
88683 'help.feature_editor.tags_h': 3,
88684 'help.points.add_point_h': 3,
88685 'help.points.move_point_h': 3,
88686 'help.points.delete_point_h': 3,
88687 'help.lines.add_line_h': 3,
88688 'help.lines.modify_line_h': 3,
88689 'help.lines.connect_line_h': 3,
88690 'help.lines.disconnect_line_h': 3,
88691 'help.lines.move_line_h': 3,
88692 'help.lines.delete_line_h': 3,
88693 'help.areas.point_or_area_h': 3,
88694 'help.areas.add_area_h': 3,
88695 'help.areas.square_area_h': 3,
88696 'help.areas.modify_area_h': 3,
88697 'help.areas.delete_area_h': 3,
88698 'help.relations.edit_relation_h': 3,
88699 'help.relations.maintain_relation_h': 3,
88700 'help.relations.relation_types_h': 2,
88701 'help.relations.multipolygon_h': 3,
88702 'help.relations.turn_restriction_h': 3,
88703 'help.relations.route_h': 3,
88704 'help.relations.boundary_h': 3,
88705 'help.notes.add_note_h': 3,
88706 'help.notes.update_note_h': 3,
88707 'help.notes.save_note_h': 3,
88708 'help.imagery.sources_h': 3,
88709 'help.imagery.offsets_h': 3,
88710 'help.streetlevel.using_h': 3,
88711 'help.gps.using_h': 3,
88712 'help.qa.tools_h': 3,
88713 'help.qa.issues_h': 3
88714 }; // For each section, squash all the texts into a single markdown document
88716 var docs = docKeys.map(function (key) {
88717 var helpkey = 'help.' + key[0];
88718 var helpPaneReplacements = {
88719 version: context.version
88721 var text = key[1].reduce(function (all, part) {
88722 var subkey = helpkey + '.' + part;
88723 var depth = headings[subkey]; // is this subkey a heading?
88725 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
88727 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
88730 title: _t.html(helpkey + '.title'),
88731 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
88732 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
88735 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
88737 helpPane.renderContent = function (content) {
88738 function clickHelp(d, i) {
88739 var rtl = _mainLocalizer.textDirection() === 'rtl';
88740 content.property('scrollTop', 0);
88741 helpPane.selection().select('.pane-heading h2').html(d.title);
88742 body.html(d.content);
88743 body.selectAll('a').attr('target', '_blank');
88744 menuItems.classed('selected', function (m) {
88745 return m.title === d.title;
88750 nav.call(drawNext).call(drawPrevious);
88752 nav.call(drawPrevious).call(drawNext);
88755 function drawNext(selection) {
88756 if (i < docs.length - 1) {
88757 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
88758 d3_event.preventDefault();
88759 clickHelp(docs[i + 1], i + 1);
88761 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
88765 function drawPrevious(selection) {
88767 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
88768 d3_event.preventDefault();
88769 clickHelp(docs[i - 1], i - 1);
88771 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
88776 function clickWalkthrough(d3_event) {
88777 d3_event.preventDefault();
88778 if (context.inIntro()) return;
88779 context.container().call(uiIntro(context));
88780 context.ui().togglePanes();
88783 function clickShortcuts(d3_event) {
88784 d3_event.preventDefault();
88785 context.container().call(context.ui().shortcuts, true);
88788 var toc = content.append('ul').attr('class', 'toc');
88789 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
88791 }).on('click', function (d3_event, d) {
88792 d3_event.preventDefault();
88793 clickHelp(d, docs.indexOf(d));
88795 var shortcuts = toc.append('li').attr('class', 'shortcuts').call(uiTooltip().title(_t.html('shortcuts.tooltip')).keys(['?']).placement('top')).append('a').attr('href', '#').on('click', clickShortcuts);
88796 shortcuts.append('div').html(_t.html('shortcuts.title'));
88797 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
88798 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
88799 walkthrough.append('div').html(_t.html('splash.walkthrough'));
88800 var helpContent = content.append('div').attr('class', 'left-content');
88801 var body = helpContent.append('div').attr('class', 'body');
88802 var nav = helpContent.append('div').attr('class', 'nav');
88803 clickHelp(docs[0], 0);
88809 function uiSectionValidationIssues(id, severity, context) {
88811 var section = uiSection(id, context).label(function () {
88812 if (!_issues) return '';
88813 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
88814 return _t('inspector.title_count', {
88815 title: _t.html('issues.' + severity + 's.list_title'),
88816 count: issueCountText
88818 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
88819 return _issues && _issues.length;
88822 function getOptions() {
88824 what: corePreferences('validate-what') || 'edited',
88825 where: corePreferences('validate-where') || 'all'
88827 } // get and cache the issues to display, unordered
88830 function reloadIssues() {
88831 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
88834 function renderDisclosureContent(selection) {
88835 var center = context.map().center();
88836 var graph = context.graph(); // sort issues by distance away from the center of the map
88838 var issues = _issues.map(function withDistance(issue) {
88839 var extent = issue.extent(graph);
88840 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
88841 return Object.assign(issue, {
88844 }).sort(function byDistance(a, b) {
88845 return a.dist - b.dist;
88846 }); // cut off at 1000
88849 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
88851 selection.call(drawIssuesList, issues);
88854 function drawIssuesList(selection, issues) {
88855 var list = selection.selectAll('.issues-list').data([0]);
88856 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
88857 var items = list.selectAll('li').data(issues, function (d) {
88861 items.exit().remove(); // Enter
88863 var itemsEnter = items.enter().append('li').attr('class', function (d) {
88864 return 'issue severity-' + d.severity;
88866 var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
88867 context.validator().focusIssue(d);
88868 }).on('mouseover', function (d3_event, d) {
88869 utilHighlightEntities(d.entityIds, true, context);
88870 }).on('mouseout', function (d3_event, d) {
88871 utilHighlightEntities(d.entityIds, false, context);
88873 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
88874 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
88875 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
88876 select(this).call(svgIcon(iconName));
88878 textEnter.append('span').attr('class', 'issue-message');
88882 .attr('class', 'issue-autofix')
88883 .each(function(d) {
88884 if (!d.autoFix) return;
88887 .attr('title', t('issues.fix_one.title'))
88888 .datum(d.autoFix) // set button datum to the autofix
88889 .attr('class', 'autofix action')
88890 .on('click', function(d3_event, d) {
88891 d3_event.preventDefault();
88892 d3_event.stopPropagation();
88893 var issuesEntityIDs = d.issue.entityIds;
88894 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
88895 context.perform.apply(context, d.autoArgs);
88896 context.validator().validate();
88898 .call(svgIcon('#iD-icon-wrench'));
88903 items = items.merge(itemsEnter).order();
88904 items.selectAll('.issue-message').html(function (d) {
88905 return d.message(context);
88909 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
88910 var autoFixAll = selection.selectAll('.autofix-all')
88911 .data(canAutoFix.length ? [0] : []);
88916 var autoFixAllEnter = autoFixAll.enter()
88917 .insert('div', '.issues-list')
88918 .attr('class', 'autofix-all');
88919 var linkEnter = autoFixAllEnter
88921 .attr('class', 'autofix-all-link')
88922 .attr('href', '#');
88925 .attr('class', 'autofix-all-link-text')
88926 .html(t.html('issues.fix_all.title'));
88929 .attr('class', 'autofix-all-link-icon')
88930 .call(svgIcon('#iD-icon-wrench'));
88931 if (severity === 'warning') {
88932 renderIgnoredIssuesReset(selection);
88935 autoFixAll = autoFixAll
88936 .merge(autoFixAllEnter);
88937 autoFixAll.selectAll('.autofix-all-link')
88938 .on('click', function() {
88939 context.pauseChangeDispatch();
88940 context.perform(actionNoop());
88941 canAutoFix.forEach(function(issue) {
88942 var args = issue.autoFix.autoArgs.slice(); // copy
88943 if (typeof args[args.length - 1] !== 'function') {
88946 args.push(t('issues.fix_all.annotation'));
88947 context.replace.apply(context, args);
88949 context.resumeChangeDispatch();
88950 context.validator().validate();
88955 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
88956 window.requestIdleCallback(function () {
88958 section.reRender();
88961 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
88962 window.requestIdleCallback(function () {
88963 if (getOptions().where === 'visible') {
88964 // must refetch issues if they are viewport-dependent
88966 } // always reload list to re-sort-by-distance
88969 section.reRender();
88975 function uiSectionValidationOptions(context) {
88976 var section = uiSection('issues-options', context).content(renderContent);
88978 function renderContent(selection) {
88979 var container = selection.selectAll('.issues-options-container').data([0]);
88980 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
88983 values: ['edited', 'all']
88986 values: ['visible', 'all']
88988 var options = container.selectAll('.issues-option').data(data, function (d) {
88991 var optionsEnter = options.enter().append('div').attr('class', function (d) {
88992 return 'issues-option issues-option-' + d.key;
88994 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
88995 return _t.html('issues.options.' + d.key + '.title');
88997 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
88998 return d.values.map(function (val) {
89004 }).enter().append('label');
89005 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
89006 return 'issues-option-' + d.key;
89007 }).attr('value', function (d) {
89009 }).property('checked', function (d) {
89010 return getOptions()[d.key] === d.value;
89011 }).on('change', function (d3_event, d) {
89012 updateOptionValue(d3_event, d.key, d.value);
89014 valuesEnter.append('span').html(function (d) {
89015 return _t.html('issues.options.' + d.key + '.' + d.value);
89019 function getOptions() {
89021 what: corePreferences('validate-what') || 'edited',
89023 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
89028 function updateOptionValue(d3_event, d, val) {
89029 if (!val && d3_event && d3_event.target) {
89030 val = d3_event.target.value;
89033 corePreferences('validate-' + d, val);
89034 context.validator().validate();
89040 function uiSectionValidationRules(context) {
89042 var MAXSQUARE = 20;
89043 var DEFAULTSQUARE = 5; // see also unsquare_way.js
89045 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
89047 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
89048 return key !== 'maprules';
89049 }).sort(function (key1, key2) {
89050 // alphabetize by localized title
89051 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
89054 function renderDisclosureContent(selection) {
89055 var container = selection.selectAll('.issues-rulelist-container').data([0]);
89056 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
89057 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
89058 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
89059 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
89060 d3_event.preventDefault();
89061 context.validator().disableRules(_ruleKeys);
89063 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
89064 d3_event.preventDefault();
89065 context.validator().disableRules([]);
89068 container = container.merge(containerEnter);
89069 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
89072 function drawListItems(selection, data, type, name, change, active) {
89073 var items = selection.selectAll('li').data(data); // Exit
89075 items.exit().remove(); // Enter
89077 var enter = items.enter().append('li');
89079 if (name === 'rule') {
89080 enter.call(uiTooltip().title(function (d) {
89081 return _t.html('issues.' + d + '.tip');
89082 }).placement('top'));
89085 var label = enter.append('label');
89086 label.append('input').attr('type', type).attr('name', name).on('change', change);
89087 label.append('span').html(function (d) {
89090 if (d === 'unsquare_way') {
89091 params.val = '<span class="square-degrees"></span>';
89094 return _t.html('issues.' + d + '.title', params);
89097 items = items.merge(enter);
89098 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
89100 var degStr = corePreferences('validate-square-degrees');
89102 if (degStr === null) {
89103 degStr = DEFAULTSQUARE.toString();
89106 var span = items.selectAll('.square-degrees');
89107 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
89109 input.enter().append('input').attr('type', 'number').attr('min', MINSQUARE.toString()).attr('max', MAXSQUARE.toString()).attr('step', '0.5').attr('class', 'square-degrees-input').call(utilNoAuto).on('click', function (d3_event) {
89110 d3_event.preventDefault();
89111 d3_event.stopPropagation();
89113 }).on('keyup', function (d3_event) {
89114 if (d3_event.keyCode === 13) {
89119 }).on('blur', changeSquare).merge(input).property('value', degStr);
89122 function changeSquare() {
89123 var input = select(this);
89124 var degStr = utilGetSetValue(input).trim();
89125 var degNum = parseFloat(degStr, 10);
89127 if (!isFinite(degNum)) {
89128 degNum = DEFAULTSQUARE;
89129 } else if (degNum > MAXSQUARE) {
89130 degNum = MAXSQUARE;
89131 } else if (degNum < MINSQUARE) {
89132 degNum = MINSQUARE;
89135 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
89137 degStr = degNum.toString();
89138 input.property('value', degStr);
89139 corePreferences('validate-square-degrees', degStr);
89140 context.validator().revalidateUnsquare();
89143 function isRuleEnabled(d) {
89144 return context.validator().isRuleEnabled(d);
89147 function toggleRule(d3_event, d) {
89148 context.validator().toggleRule(d);
89151 context.validator().on('validated.uiSectionValidationRules', function () {
89152 window.requestIdleCallback(section.reRender);
89157 function uiSectionValidationStatus(context) {
89158 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
89159 var issues = context.validator().getIssues(getOptions());
89160 return issues.length === 0;
89163 function getOptions() {
89165 what: corePreferences('validate-what') || 'edited',
89166 where: corePreferences('validate-where') || 'all'
89170 function renderContent(selection) {
89171 var box = selection.selectAll('.box').data([0]);
89172 var boxEnter = box.enter().append('div').attr('class', 'box');
89173 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
89174 var noIssuesMessage = boxEnter.append('span');
89175 noIssuesMessage.append('strong').attr('class', 'message');
89176 noIssuesMessage.append('br');
89177 noIssuesMessage.append('span').attr('class', 'details');
89178 renderIgnoredIssuesReset(selection);
89179 setNoIssuesText(selection);
89182 function renderIgnoredIssuesReset(selection) {
89183 var ignoredIssues = context.validator().getIssues({
89186 includeDisabledRules: true,
89187 includeIgnored: 'only'
89189 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
89191 resetIgnored.exit().remove(); // enter
89193 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
89194 resetIgnoredEnter.append('a').attr('href', '#'); // update
89196 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
89197 resetIgnored.select('a').html(_t('inspector.title_count', {
89198 title: _t.html('issues.reset_ignored'),
89199 count: ignoredIssues.length
89201 resetIgnored.on('click', function (d3_event) {
89202 d3_event.preventDefault();
89203 context.validator().resetIgnoredIssues();
89207 function setNoIssuesText(selection) {
89208 var opts = getOptions();
89210 function checkForHiddenIssues(cases) {
89211 for (var type in cases) {
89212 var hiddenOpts = cases[type];
89213 var hiddenIssues = context.validator().getIssues(hiddenOpts);
89215 if (hiddenIssues.length) {
89216 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
89217 count: hiddenIssues.length.toString()
89223 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
89228 if (opts.what === 'edited' && opts.where === 'visible') {
89229 messageType = 'edits_in_view';
89230 checkForHiddenIssues({
89242 includeDisabledRules: 'only'
89244 everything_else_elsewhere: {
89248 disabled_rules_elsewhere: {
89251 includeDisabledRules: 'only'
89256 includeIgnored: 'only'
89258 ignored_issues_elsewhere: {
89261 includeIgnored: 'only'
89264 } else if (opts.what === 'edited' && opts.where === 'all') {
89265 messageType = 'edits';
89266 checkForHiddenIssues({
89274 includeDisabledRules: 'only'
89279 includeIgnored: 'only'
89282 } else if (opts.what === 'all' && opts.where === 'visible') {
89283 messageType = 'everything_in_view';
89284 checkForHiddenIssues({
89292 includeDisabledRules: 'only'
89294 disabled_rules_elsewhere: {
89297 includeDisabledRules: 'only'
89302 includeIgnored: 'only'
89304 ignored_issues_elsewhere: {
89307 includeIgnored: 'only'
89310 } else if (opts.what === 'all' && opts.where === 'all') {
89311 messageType = 'everything';
89312 checkForHiddenIssues({
89316 includeDisabledRules: 'only'
89321 includeIgnored: 'only'
89326 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
89327 messageType = 'no_edits';
89330 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
89333 context.validator().on('validated.uiSectionValidationStatus', function () {
89334 window.requestIdleCallback(section.reRender);
89336 context.map().on('move.uiSectionValidationStatus', debounce(function () {
89337 window.requestIdleCallback(section.reRender);
89342 function uiPaneIssues(context) {
89343 var issuesPane = uiPane('issues', context).key(_t('issues.key')).label(_t.html('issues.title')).description(_t.html('issues.title')).iconName('iD-icon-alert').sections([uiSectionValidationOptions(context), uiSectionValidationStatus(context), uiSectionValidationIssues('issues-errors', 'error', context), uiSectionValidationIssues('issues-warnings', 'warning', context), uiSectionValidationRules(context)]);
89347 function uiSettingsCustomData(context) {
89348 var dispatch = dispatch$8('change');
89350 function render(selection) {
89351 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
89353 var _origSettings = {
89354 fileList: dataLayer && dataLayer.fileList() || null,
89355 url: corePreferences('settings-custom-data-url')
89357 var _currSettings = {
89358 fileList: dataLayer && dataLayer.fileList() || null,
89359 url: corePreferences('settings-custom-data-url')
89360 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
89362 var modal = uiConfirm(selection).okButton();
89363 modal.classed('settings-modal settings-custom-data', true);
89364 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
89365 var textSection = modal.select('.modal-section.message-text');
89366 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
89367 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
89368 .on('change', function (d3_event) {
89369 var files = d3_event.target.files;
89371 if (files && files.length) {
89372 _currSettings.url = '';
89373 textSection.select('.field-url').property('value', '');
89374 _currSettings.fileList = files;
89376 _currSettings.fileList = null;
89379 textSection.append('h4').html(_t.html('settings.custom_data.or'));
89380 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
89381 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
89383 var buttonSection = modal.select('.modal-section.buttons');
89384 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
89385 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
89386 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
89388 function isSaveDisabled() {
89390 } // restore the original url
89393 function clickCancel() {
89394 textSection.select('.field-url').property('value', _origSettings.url);
89395 corePreferences('settings-custom-data-url', _origSettings.url);
89398 } // accept the current url
89401 function clickSave() {
89402 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
89404 if (_currSettings.url) {
89405 _currSettings.fileList = null;
89408 if (_currSettings.fileList) {
89409 _currSettings.url = '';
89412 corePreferences('settings-custom-data-url', _currSettings.url);
89415 dispatch.call('change', this, _currSettings);
89419 return utilRebind(render, dispatch, 'on');
89422 function uiSectionDataLayers(context) {
89423 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
89424 var layers = context.layers();
89425 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
89427 function renderDisclosureContent(selection) {
89428 var container = selection.selectAll('.data-layer-container').data([0]);
89429 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
89430 .call(drawPanelItems);
89433 function showsLayer(which) {
89434 var layer = layers.layer(which);
89437 return layer.enabled();
89443 function setLayer(which, enabled) {
89444 // Don't allow layer changes while drawing - #6584
89445 var mode = context.mode();
89446 if (mode && /^draw/.test(mode.id)) return;
89447 var layer = layers.layer(which);
89450 layer.enabled(enabled);
89452 if (!enabled && (which === 'osm' || which === 'notes')) {
89453 context.enter(modeBrowse(context));
89458 function toggleLayer(which) {
89459 setLayer(which, !showsLayer(which));
89462 function drawOsmItems(selection) {
89463 var osmKeys = ['osm', 'notes'];
89464 var osmLayers = layers.all().filter(function (obj) {
89465 return osmKeys.indexOf(obj.id) !== -1;
89467 var ul = selection.selectAll('.layer-list-osm').data([0]);
89468 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
89469 var li = ul.selectAll('.list-item').data(osmLayers);
89470 li.exit().remove();
89471 var liEnter = li.enter().append('li').attr('class', function (d) {
89472 return 'list-item list-item-' + d.id;
89474 var labelEnter = liEnter.append('label').each(function (d) {
89475 if (d.id === 'osm') {
89476 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
89478 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
89481 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89484 labelEnter.append('span').html(function (d) {
89485 return _t.html('map_data.layers.' + d.id + '.title');
89488 li.merge(liEnter).classed('active', function (d) {
89489 return d.layer.enabled();
89490 }).selectAll('input').property('checked', function (d) {
89491 return d.layer.enabled();
89495 function drawQAItems(selection) {
89496 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
89497 var qaLayers = layers.all().filter(function (obj) {
89498 return qaKeys.indexOf(obj.id) !== -1;
89500 var ul = selection.selectAll('.layer-list-qa').data([0]);
89501 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
89502 var li = ul.selectAll('.list-item').data(qaLayers);
89503 li.exit().remove();
89504 var liEnter = li.enter().append('li').attr('class', function (d) {
89505 return 'list-item list-item-' + d.id;
89507 var labelEnter = liEnter.append('label').each(function (d) {
89508 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
89510 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89513 labelEnter.append('span').html(function (d) {
89514 return _t.html('map_data.layers.' + d.id + '.title');
89517 li.merge(liEnter).classed('active', function (d) {
89518 return d.layer.enabled();
89519 }).selectAll('input').property('checked', function (d) {
89520 return d.layer.enabled();
89522 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
89523 // https://github.com/osmus/detroit-mapping-challenge
89526 function drawVectorItems(selection) {
89527 var dataLayer = layers.layer('data');
89529 name: 'Detroit Neighborhoods/Parks',
89530 src: 'neighborhoods-parks',
89531 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
89532 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'
89534 name: 'Detroit Composite POIs',
89535 src: 'composite-poi',
89536 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
89537 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'
89539 name: 'Detroit All-The-Places POIs',
89540 src: 'alltheplaces-poi',
89541 tooltip: 'Public domain business location data created by web scrapers.',
89542 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'
89543 }]; // Only show this if the map is around Detroit..
89545 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
89546 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
89547 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
89548 container.exit().remove();
89549 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
89550 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
89551 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
89552 containerEnter.append('div').attr('class', 'vectortile-footer').append('a').attr('target', '_blank').call(svgIcon('#iD-icon-out-link', 'inline')).attr('href', 'https://github.com/osmus/detroit-mapping-challenge').append('span').html('About these layers');
89553 container = container.merge(containerEnter);
89554 var ul = container.selectAll('.layer-list-vectortile');
89555 var li = ul.selectAll('.list-item').data(vtData);
89556 li.exit().remove();
89557 var liEnter = li.enter().append('li').attr('class', function (d) {
89558 return 'list-item list-item-' + d.src;
89560 var labelEnter = liEnter.append('label').each(function (d) {
89561 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
89563 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
89564 labelEnter.append('span').html(function (d) {
89568 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
89570 function isVTLayerSelected(d) {
89571 return dataLayer && dataLayer.template() === d.template;
89574 function selectVTLayer(d3_event, d) {
89575 corePreferences('settings-custom-data-url', d.template);
89578 dataLayer.template(d.template, d.src);
89579 dataLayer.enabled(true);
89584 function drawCustomDataItems(selection) {
89585 var dataLayer = layers.layer('data');
89586 var hasData = dataLayer && dataLayer.hasData();
89587 var showsData = hasData && dataLayer.enabled();
89588 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
89590 ul.exit().remove(); // Enter
89592 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
89593 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
89594 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
89595 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
89596 toggleLayer('data');
89598 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
89599 liEnter.append('button').attr('class', 'open-data-options').call(uiTooltip().title(_t.html('settings.custom_data.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
89600 d3_event.preventDefault();
89602 }).call(svgIcon('#iD-icon-more'));
89603 liEnter.append('button').attr('class', 'zoom-to-data').call(uiTooltip().title(_t.html('map_data.layers.custom.zoom')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
89604 if (select(this).classed('disabled')) return;
89605 d3_event.preventDefault();
89606 d3_event.stopPropagation();
89607 dataLayer.fitZoom();
89608 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
89610 ul = ul.merge(ulEnter);
89611 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
89612 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
89615 function editCustom() {
89616 context.container().call(settingsCustomData);
89619 function customChanged(d) {
89620 var dataLayer = layers.layer('data');
89623 dataLayer.url(d.url);
89624 } else if (d && d.fileList) {
89625 dataLayer.fileList(d.fileList);
89629 function drawPanelItems(selection) {
89630 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
89631 var historyPanelLabelEnter = panelsListEnter.append('li').attr('class', 'history-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('map_data.history_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))]).placement('top'));
89632 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89633 d3_event.preventDefault();
89634 context.ui().info.toggle('history');
89636 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
89637 var measurementPanelLabelEnter = panelsListEnter.append('li').attr('class', 'measurement-panel-toggle-item').append('label').call(uiTooltip().title(_t.html('map_data.measurement_panel.tooltip')).keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))]).placement('top'));
89638 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89639 d3_event.preventDefault();
89640 context.ui().info.toggle('measurement');
89642 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
89645 context.layers().on('change.uiSectionDataLayers', section.reRender);
89646 context.map().on('move.uiSectionDataLayers', debounce(function () {
89647 // Detroit layers may have moved in or out of view
89648 window.requestIdleCallback(section.reRender);
89653 function uiSectionMapFeatures(context) {
89654 var _features = context.features().keys();
89656 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89658 function renderDisclosureContent(selection) {
89659 var container = selection.selectAll('.layer-feature-list-container').data([0]);
89660 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
89661 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
89662 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
89663 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
89664 d3_event.preventDefault();
89665 context.features().disableAll();
89667 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
89668 d3_event.preventDefault();
89669 context.features().enableAll();
89672 container = container.merge(containerEnter);
89673 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
89676 function drawListItems(selection, data, type, name, change, active) {
89677 var items = selection.selectAll('li').data(data); // Exit
89679 items.exit().remove(); // Enter
89681 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
89682 var tip = _t.html(name + '.' + d + '.tooltip');
89684 if (autoHiddenFeature(d)) {
89685 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
89686 tip += '<div>' + msg + '</div>';
89690 }).placement('top'));
89691 var label = enter.append('label');
89692 label.append('input').attr('type', type).attr('name', name).on('change', change);
89693 label.append('span').html(function (d) {
89694 return _t.html(name + '.' + d + '.description');
89697 items = items.merge(enter);
89698 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
89701 function autoHiddenFeature(d) {
89702 return context.features().autoHidden(d);
89705 function showsFeature(d) {
89706 return context.features().enabled(d);
89709 function clickFeature(d3_event, d) {
89710 context.features().toggle(d);
89713 function showsLayer(id) {
89714 var layer = context.layers().layer(id);
89715 return layer && layer.enabled();
89719 context.features().on('change.map_features', section.reRender);
89723 function uiSectionMapStyleOptions(context) {
89724 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89726 function renderDisclosureContent(selection) {
89727 var container = selection.selectAll('.layer-fill-list').data([0]);
89728 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
89729 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
89730 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
89731 return context.surface().classed('highlight-edited');
89735 function drawListItems(selection, data, type, name, change, active) {
89736 var items = selection.selectAll('li').data(data); // Exit
89738 items.exit().remove(); // Enter
89740 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
89741 return _t.html(name + '.' + d + '.tooltip');
89742 }).keys(function (d) {
89743 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
89744 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
89745 return key ? [key] : null;
89746 }).placement('top'));
89747 var label = enter.append('label');
89748 label.append('input').attr('type', type).attr('name', name).on('change', change);
89749 label.append('span').html(function (d) {
89750 return _t.html(name + '.' + d + '.description');
89753 items = items.merge(enter);
89754 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
89757 function isActiveFill(d) {
89758 return context.map().activeAreaFill() === d;
89761 function toggleHighlightEdited(d3_event) {
89762 d3_event.preventDefault();
89763 context.map().toggleHighlightEdited();
89766 function setFill(d3_event, d) {
89767 context.map().activeAreaFill(d);
89770 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
89774 function uiSectionPhotoOverlays(context) {
89775 var layers = context.layers();
89776 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
89778 function renderDisclosureContent(selection) {
89779 var container = selection.selectAll('.photo-overlay-container').data([0]);
89780 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
89783 function drawPhotoItems(selection) {
89784 var photoKeys = context.photos().overlayLayerIDs();
89785 var photoLayers = layers.all().filter(function (obj) {
89786 return photoKeys.indexOf(obj.id) !== -1;
89788 var data = photoLayers.filter(function (obj) {
89789 return obj.layer.supported();
89792 function layerSupported(d) {
89793 return d.layer && d.layer.supported();
89796 function layerEnabled(d) {
89797 return layerSupported(d) && d.layer.enabled();
89800 var ul = selection.selectAll('.layer-list-photos').data([0]);
89801 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
89802 var li = ul.selectAll('.list-item-photos').data(data);
89803 li.exit().remove();
89804 var liEnter = li.enter().append('li').attr('class', function (d) {
89805 var classes = 'list-item-photos list-item-' + d.id;
89807 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
89808 classes += ' indented';
89813 var labelEnter = liEnter.append('label').each(function (d) {
89815 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';
89816 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
89818 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89821 labelEnter.append('span').html(function (d) {
89823 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
89824 return _t.html(id.replace(/-/g, '_') + '.title');
89827 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
89830 function drawPhotoTypeItems(selection) {
89831 var data = context.photos().allPhotoTypes();
89833 function typeEnabled(d) {
89834 return context.photos().showsPhotoType(d);
89837 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
89838 ul.exit().remove();
89839 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
89840 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
89841 li.exit().remove();
89842 var liEnter = li.enter().append('li').attr('class', function (d) {
89843 return 'list-item-photo-types list-item-' + d;
89845 var labelEnter = liEnter.append('label').each(function (d) {
89846 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
89848 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
89849 context.photos().togglePhotoType(d);
89851 labelEnter.append('span').html(function (d) {
89852 return _t.html('photo_overlays.photo_type.' + d + '.title');
89855 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
89858 function drawDateFilter(selection) {
89859 var data = context.photos().dateFilters();
89861 function filterEnabled(d) {
89862 return context.photos().dateFilterValue(d);
89865 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
89866 ul.exit().remove();
89867 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
89868 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
89869 li.exit().remove();
89870 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
89871 var labelEnter = liEnter.append('label').each(function (d) {
89872 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
89874 labelEnter.append('span').html(function (d) {
89875 return _t.html('photo_overlays.date_filter.' + d + '.title');
89877 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
89878 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
89879 }).on('change', function (d3_event, d) {
89880 var value = utilGetSetValue(select(this)).trim();
89881 context.photos().setDateFilter(d, value, true); // reload the displayed dates
89883 li.selectAll('input').each(function (d) {
89884 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
89887 li = li.merge(liEnter).classed('active', filterEnabled);
89890 function drawUsernameFilter(selection) {
89891 function filterEnabled() {
89892 return context.photos().usernames();
89895 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
89896 ul.exit().remove();
89897 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
89898 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
89899 li.exit().remove();
89900 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
89901 var labelEnter = liEnter.append('label').each(function () {
89902 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
89904 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
89905 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
89906 var value = select(this).property('value');
89907 context.photos().setUsernameFilter(value, true);
89908 select(this).property('value', usernameValue);
89910 li.merge(liEnter).classed('active', filterEnabled);
89912 function usernameValue() {
89913 var usernames = context.photos().usernames();
89914 if (usernames) return usernames.join('; ');
89919 function toggleLayer(which) {
89920 setLayer(which, !showsLayer(which));
89923 function showsLayer(which) {
89924 var layer = layers.layer(which);
89927 return layer.enabled();
89933 function setLayer(which, enabled) {
89934 var layer = layers.layer(which);
89937 layer.enabled(enabled);
89941 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
89942 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
89946 function uiPaneMapData(context) {
89947 var mapDataPane = uiPane('map-data', context).key(_t('map_data.key')).label(_t.html('map_data.title')).description(_t.html('map_data.description')).iconName('iD-icon-data').sections([uiSectionDataLayers(context), uiSectionPhotoOverlays(context), uiSectionMapStyleOptions(context), uiSectionMapFeatures(context)]);
89948 return mapDataPane;
89951 function uiSectionPrivacy(context) {
89952 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
89954 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
89956 function renderDisclosureContent(selection) {
89958 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
89959 var thirdPartyIconsEnter = privacyOptionsListEnter.append('li').attr('class', 'privacy-third-party-icons-item').append('label').call(uiTooltip().title(_t.html('preferences.privacy.third_party_icons.tooltip')).placement('bottom'));
89960 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
89961 d3_event.preventDefault();
89962 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
89963 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
89966 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
89968 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').html(_t.html('preferences.privacy.privacy_link'));
89971 function update() {
89972 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
89979 function uiPanePreferences(context) {
89980 var preferencesPane = uiPane('preferences', context).key(_t('preferences.key')).label(_t.html('preferences.title')).description(_t.html('preferences.description')).iconName('fas-user-cog').sections([uiSectionPrivacy(context)]);
89981 return preferencesPane;
89984 function uiInit(context) {
89985 var _initCounter = 0;
89986 var _needWidth = {};
89988 var _lastPointerType;
89990 function render(container) {
89991 container.on('click.ui', function (d3_event) {
89992 // we're only concerned with the primary mouse button
89993 if (d3_event.button !== 0) return;
89994 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
89996 var isOkayTarget = d3_event.composedPath().some(function (node) {
89997 // we only care about element nodes
89998 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
89999 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
90000 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
90001 node.nodeName === 'A');
90003 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
90005 d3_event.preventDefault();
90007 var detected = utilDetect(); // only WebKit supports gesture events
90009 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
90010 // but we only need to do this on desktop Safari anyway. – #7694
90011 !detected.isMobileWebKit) {
90012 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
90013 // CSS property, but on desktop Safari we need to manually cancel the
90014 // default gesture events.
90015 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
90016 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
90017 d3_event.preventDefault();
90021 if ('PointerEvent' in window) {
90022 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
90023 var pointerType = d3_event.pointerType || 'mouse';
90025 if (_lastPointerType !== pointerType) {
90026 _lastPointerType = pointerType;
90027 container.attr('pointer', pointerType);
90031 _lastPointerType = 'mouse';
90032 container.attr('pointer', 'mouse');
90035 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
90037 container.call(uiFullScreen(context));
90038 var map = context.map();
90039 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
90041 map.on('hitMinZoom.ui', function () {
90042 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
90044 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
90045 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
90046 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
90048 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
90049 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
90050 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
90051 // pressing, even if it's not targeted. This conflicts with long-pressing
90052 // to show the edit menu. We add a selectable offscreen element as the first
90053 // child to trick Safari into not showing the selection UI.
90055 overMap.append('div').attr('class', 'select-trap').text('t');
90056 overMap.call(uiMapInMap(context)).call(uiNotice(context));
90057 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
90059 var controls = overMap.append('div').attr('class', 'map-controls');
90060 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
90061 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
90062 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
90063 // This should happen after map is initialized, as some require surface()
90065 var panes = overMap.append('div').attr('class', 'map-panes');
90066 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
90067 uiPanes.forEach(function (pane) {
90068 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
90069 panes.call(pane.renderPane);
90071 ui.info = uiInfo(context);
90072 overMap.call(ui.info);
90073 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
90074 .classed('hide', true).call(ui.photoviewer);
90075 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
90077 var about = content.append('div').attr('class', 'map-footer');
90078 about.append('div').attr('class', 'api-status').call(uiStatus(context));
90079 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
90080 footer.append('div').attr('class', 'flash-wrap footer-hide');
90081 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
90082 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
90083 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
90084 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
90085 var apiConnections = context.apiConnections();
90087 if (apiConnections && apiConnections.length > 1) {
90088 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
90091 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
90092 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
90093 var issueLinks = aboutList.append('li');
90094 issueLinks.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/issues').call(svgIcon('#iD-icon-bug', 'light')).call(uiTooltip().title(_t.html('report_a_bug')).placement('top'));
90095 issueLinks.append('a').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating').call(svgIcon('#iD-icon-translate', 'light')).call(uiTooltip().title(_t.html('help_translate')).placement('top'));
90096 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
90098 if (!context.embed()) {
90099 aboutList.call(uiAccount(context));
90100 } // Setup map dimensions and move map to initial center/zoom.
90101 // This should happen after .main-content and toolbars exist.
90105 map.redrawEnable(true);
90106 ui.hash = behaviorHash(context);
90109 if (!ui.hash.hadHash) {
90110 map.centerZoom([0, 0], 2);
90114 window.onbeforeunload = function () {
90115 return context.save();
90118 window.onunload = function () {
90119 context.history().unlock();
90122 select(window).on('resize.editor', function () {
90125 var panPixels = 80;
90126 context.keybinding().on('⌫', function (d3_event) {
90127 d3_event.preventDefault();
90128 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
90129 .on('←', pan([panPixels, 0])).on('↑', pan([0, panPixels])).on('→', pan([-panPixels, 0])).on('↓', pan([0, -panPixels])).on(uiCmd('⌥←'), pan([map.dimensions()[0], 0])).on(uiCmd('⌥↑'), pan([0, map.dimensions()[1]])).on(uiCmd('⌥→'), pan([-map.dimensions()[0], 0])).on(uiCmd('⌥↓'), pan([0, -map.dimensions()[1]])).on(uiCmd('⌘' + _t('background.key')), function quickSwitch(d3_event) {
90131 d3_event.stopImmediatePropagation();
90132 d3_event.preventDefault();
90135 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
90137 if (previousBackground) {
90138 var currentBackground = context.background().baseLayerSource();
90139 corePreferences('background-last-used-toggle', currentBackground.id);
90140 corePreferences('background-last-used', previousBackground.id);
90141 context.background().baseLayerSource(previousBackground);
90143 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
90144 d3_event.preventDefault();
90145 d3_event.stopPropagation();
90146 context.map().toggleWireframe();
90147 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
90148 d3_event.preventDefault();
90149 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
90151 var mode = context.mode();
90152 if (mode && /^draw/.test(mode.id)) return;
90153 var layer = context.layers().layer('osm');
90156 layer.enabled(!layer.enabled());
90158 if (!layer.enabled()) {
90159 context.enter(modeBrowse(context));
90162 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
90163 d3_event.preventDefault();
90164 context.map().toggleHighlightEdited();
90166 context.on('enter.editor', function (entered) {
90167 container.classed('mode-' + entered.id, true);
90168 }).on('exit.editor', function (exited) {
90169 container.classed('mode-' + exited.id, false);
90171 context.enter(modeBrowse(context));
90173 if (!_initCounter++) {
90174 if (!ui.hash.startWalkthrough) {
90175 context.container().call(uiSplash(context)).call(uiRestore(context));
90178 context.container().call(ui.shortcuts);
90181 var osm = context.connection();
90182 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
90185 osm.on('authLoading.ui', function () {
90186 context.container().call(auth);
90187 }).on('authDone.ui', function () {
90194 if (ui.hash.startWalkthrough) {
90195 ui.hash.startWalkthrough = false;
90196 context.container().call(uiIntro(context));
90200 return function (d3_event) {
90201 if (d3_event.shiftKey) return;
90202 if (context.container().select('.combobox').size()) return;
90203 d3_event.preventDefault();
90204 context.map().pan(d, 100);
90211 var _loadPromise; // renders the iD interface into the container node
90214 ui.ensureLoaded = function () {
90215 if (_loadPromise) return _loadPromise;
90216 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
90217 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
90218 if (!context.container().empty()) render(context.container());
90219 })["catch"](function (err) {
90220 return console.error(err);
90221 }); // eslint-disable-line
90222 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
90223 // for example to switch the locale while iD is running.
90226 ui.restart = function () {
90227 context.keybinding().clear();
90228 _loadPromise = null;
90229 context.container().selectAll('*').remove();
90233 ui.lastPointerType = function () {
90234 return _lastPointerType;
90237 ui.svgDefs = svgDefs(context);
90238 ui.flash = uiFlash(context);
90239 ui.sidebar = uiSidebar(context);
90240 ui.photoviewer = uiPhotoviewer(context);
90241 ui.shortcuts = uiShortcuts(context);
90243 ui.onResize = function (withPan) {
90244 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
90245 // This will call `getBoundingClientRect` and trigger reflow,
90246 // but the values will be cached for later use.
90248 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
90249 utilGetDimensions(context.container().select('.sidebar'), true);
90251 if (withPan !== undefined) {
90252 map.redrawEnable(false);
90254 map.redrawEnable(true);
90257 map.dimensions(mapDimensions);
90258 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
90260 ui.checkOverflow('.top-toolbar');
90261 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
90263 var resizeWindowEvent = document.createEvent('Event');
90264 resizeWindowEvent.initEvent('resizeWindow', true, true);
90265 document.dispatchEvent(resizeWindowEvent);
90266 }; // Call checkOverflow when resizing or whenever the contents change.
90269 ui.checkOverflow = function (selector, reset) {
90271 delete _needWidth[selector];
90274 var selection = context.container().select(selector);
90275 if (selection.empty()) return;
90276 var scrollWidth = selection.property('scrollWidth');
90277 var clientWidth = selection.property('clientWidth');
90278 var needed = _needWidth[selector] || scrollWidth;
90280 if (scrollWidth > clientWidth) {
90281 // overflow happening
90282 selection.classed('narrow', true);
90284 if (!_needWidth[selector]) {
90285 _needWidth[selector] = scrollWidth;
90287 } else if (scrollWidth >= needed) {
90288 selection.classed('narrow', false);
90292 ui.togglePanes = function (showPane) {
90293 var hidePanes = context.container().selectAll('.map-pane.shown');
90294 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
90295 hidePanes.classed('shown', false).classed('hide', true);
90296 context.container().selectAll('.map-pane-control button').classed('active', false);
90299 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
90300 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
90301 showPane.classed('shown', true).classed('hide', false);
90303 if (hidePanes.empty()) {
90304 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
90306 showPane.style(side, '0px');
90309 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
90310 select(this).classed('shown', false).classed('hide', true);
90315 var _editMenu = uiEditMenu(context);
90317 ui.editMenu = function () {
90321 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
90322 // remove any displayed menu
90323 ui.closeEditMenu();
90324 if (!operations && context.mode().operations) operations = context.mode().operations();
90325 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
90327 if (!context.map().editableDataEnabled()) return;
90328 var surfaceNode = context.surface().node();
90330 if (surfaceNode.focus) {
90331 // FF doesn't support it
90332 // focus the surface or else clicking off the menu may not trigger modeBrowse
90333 surfaceNode.focus();
90336 operations.forEach(function (operation) {
90337 if (operation.point) operation.point(anchorPoint);
90340 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
90343 context.map().supersurface.call(_editMenu);
90346 ui.closeEditMenu = function () {
90347 // remove any existing menu no matter how it was added
90348 context.map().supersurface.select('.edit-menu').remove();
90351 var _saveLoading = select(null);
90353 context.uploader().on('saveStarted.ui', function () {
90354 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
90355 context.container().call(_saveLoading); // block input during upload
90356 }).on('saveEnded.ui', function () {
90357 _saveLoading.close();
90359 _saveLoading = select(null);
90364 function coreContext() {
90367 var dispatch = dispatch$8('enter', 'exit', 'change');
90368 var context = utilRebind({}, dispatch, 'on');
90370 var _deferred = new Set();
90372 context.version = '2.20.1';
90373 context.privacyVersion = '20201202'; // iD will alter the hash so cache the parameters intended to setup the session
90375 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
90376 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
90378 // An osmChangeset object. Not loaded until needed.
90380 context.changeset = null;
90381 var _defaultChangesetComment = context.initialHashParams.comment;
90382 var _defaultChangesetSource = context.initialHashParams.source;
90383 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
90385 context.defaultChangesetComment = function (val) {
90386 if (!arguments.length) return _defaultChangesetComment;
90387 _defaultChangesetComment = val;
90391 context.defaultChangesetSource = function (val) {
90392 if (!arguments.length) return _defaultChangesetSource;
90393 _defaultChangesetSource = val;
90397 context.defaultChangesetHashtags = function (val) {
90398 if (!arguments.length) return _defaultChangesetHashtags;
90399 _defaultChangesetHashtags = val;
90402 /* Document title */
90404 /* (typically shown as the label for the browser window/tab) */
90405 // If true, iD will update the title based on what the user is doing
90408 var _setsDocumentTitle = true;
90410 context.setsDocumentTitle = function (val) {
90411 if (!arguments.length) return _setsDocumentTitle;
90412 _setsDocumentTitle = val;
90414 }; // The part of the title that is always the same
90417 var _documentTitleBase = document.title;
90419 context.documentTitleBase = function (val) {
90420 if (!arguments.length) return _documentTitleBase;
90421 _documentTitleBase = val;
90424 /* User interface and keybinding */
90429 context.ui = function () {
90433 context.lastPointerType = function () {
90434 return _ui.lastPointerType();
90437 var _keybinding = utilKeybinding('context');
90439 context.keybinding = function () {
90440 return _keybinding;
90443 select(document).call(_keybinding);
90444 /* Straight accessors. Avoid using these if you can. */
90445 // Instantiate the connection here because it doesn't require passing in
90446 // `context` and it's needed for pre-init calls like `preauth`
90448 var _connection = services.osm;
90456 context.connection = function () {
90457 return _connection;
90460 context.history = function () {
90464 context.validator = function () {
90468 context.uploader = function () {
90474 context.preauth = function (options) {
90476 _connection["switch"](options);
90481 /* connection options for source switcher (optional) */
90484 var _apiConnections;
90486 context.apiConnections = function (val) {
90487 if (!arguments.length) return _apiConnections;
90488 _apiConnections = val;
90490 }; // A string or array or locale codes to prefer over the browser's settings
90493 context.locale = function (locale) {
90494 if (!arguments.length) return _mainLocalizer.localeCode();
90495 _mainLocalizer.preferredLocaleCodes(locale);
90499 function afterLoad(cid, callback) {
90500 return function (err, result) {
90502 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
90503 if (err.status === 400 || err.status === 401 || err.status === 403) {
90505 _connection.logout();
90509 if (typeof callback === 'function') {
90514 } else if (_connection && _connection.getConnectionId() !== cid) {
90515 if (typeof callback === 'function') {
90517 message: 'Connection Switched',
90524 _history.merge(result.data, result.extent);
90526 if (typeof callback === 'function') {
90527 callback(err, result);
90535 context.loadTiles = function (projection, callback) {
90536 var handle = window.requestIdleCallback(function () {
90537 _deferred["delete"](handle);
90539 if (_connection && context.editableDataEnabled()) {
90540 var cid = _connection.getConnectionId();
90542 _connection.loadTiles(projection, afterLoad(cid, callback));
90546 _deferred.add(handle);
90549 context.loadTileAtLoc = function (loc, callback) {
90550 var handle = window.requestIdleCallback(function () {
90551 _deferred["delete"](handle);
90553 if (_connection && context.editableDataEnabled()) {
90554 var cid = _connection.getConnectionId();
90556 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
90560 _deferred.add(handle);
90561 }; // Download the full entity and its parent relations. The callback may be called multiple times.
90564 context.loadEntity = function (entityID, callback) {
90566 var cid = _connection.getConnectionId();
90568 _connection.loadEntity(entityID, afterLoad(cid, callback)); // We need to fetch the parent relations separately.
90571 _connection.loadEntityRelations(entityID, afterLoad(cid, callback));
90575 context.zoomToEntity = function (entityID, zoomTo) {
90576 // be sure to load the entity even if we're not going to zoom to it
90577 context.loadEntity(entityID, function (err, result) {
90580 if (zoomTo !== false) {
90581 var entity = result.data.find(function (e) {
90582 return e.id === entityID;
90586 _map.zoomTo(entity);
90591 _map.on('drawn.zoomToEntity', function () {
90592 if (!context.hasEntity(entityID)) return;
90594 _map.on('drawn.zoomToEntity', null);
90596 context.on('enter.zoomToEntity', null);
90597 context.enter(modeSelect(context, [entityID]));
90600 context.on('enter.zoomToEntity', function () {
90601 if (_mode.id !== 'browse') {
90602 _map.on('drawn.zoomToEntity', null);
90604 context.on('enter.zoomToEntity', null);
90609 var _minEditableZoom = 16;
90611 context.minEditableZoom = function (val) {
90612 if (!arguments.length) return _minEditableZoom;
90613 _minEditableZoom = val;
90616 _connection.tileZoom(val);
90620 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
90623 context.maxCharsForTagKey = function () {
90627 context.maxCharsForTagValue = function () {
90631 context.maxCharsForRelationRole = function () {
90635 function cleanOsmString(val, maxChars) {
90636 // be lenient with input
90637 if (val === undefined || val === null) {
90640 val = val.toString();
90641 } // remove whitespace
90644 val = val.trim(); // use the canonical form of the string
90646 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
90648 return utilUnicodeCharsTruncated(val, maxChars);
90651 context.cleanTagKey = function (val) {
90652 return cleanOsmString(val, context.maxCharsForTagKey());
90655 context.cleanTagValue = function (val) {
90656 return cleanOsmString(val, context.maxCharsForTagValue());
90659 context.cleanRelationRole = function (val) {
90660 return cleanOsmString(val, context.maxCharsForRelationRole());
90665 var _inIntro = false;
90667 context.inIntro = function (val) {
90668 if (!arguments.length) return _inIntro;
90671 }; // Immediately save the user's history to localstorage, if possible
90672 // This is called someteimes, but also on the `window.onbeforeunload` handler
90675 context.save = function () {
90676 // no history save, no message onbeforeunload
90677 if (_inIntro || context.container().select('.modal').size()) return;
90680 if (_mode && _mode.id === 'save') {
90681 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
90683 if (services.osm && services.osm.isChangesetInflight()) {
90684 _history.clearSaved();
90689 canSave = context.selectedIDs().every(function (id) {
90690 var entity = context.hasEntity(id);
90691 return entity && !entity.isDegenerate();
90699 if (_history.hasChanges()) {
90700 return _t('save.unsaved_changes');
90702 }; // Debounce save, since it's a synchronous localStorage write,
90703 // and history changes can happen frequently (e.g. when dragging).
90706 context.debouncedSave = debounce(context.save, 350);
90708 function withDebouncedSave(fn) {
90709 return function () {
90710 var result = fn.apply(_history, arguments);
90711 context.debouncedSave();
90718 context.hasEntity = function (id) {
90719 return _history.graph().hasEntity(id);
90722 context.entity = function (id) {
90723 return _history.graph().entity(id);
90730 context.mode = function () {
90734 context.enter = function (newMode) {
90738 dispatch.call('exit', _this, _mode);
90745 dispatch.call('enter', _this, _mode);
90748 context.selectedIDs = function () {
90749 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
90752 context.activeID = function () {
90753 return _mode && _mode.activeID && _mode.activeID();
90756 var _selectedNoteID;
90758 context.selectedNoteID = function (noteID) {
90759 if (!arguments.length) return _selectedNoteID;
90760 _selectedNoteID = noteID;
90762 }; // NOTE: Don't change the name of this until UI v3 is merged
90765 var _selectedErrorID;
90767 context.selectedErrorID = function (errorID) {
90768 if (!arguments.length) return _selectedErrorID;
90769 _selectedErrorID = errorID;
90775 context.install = function (behavior) {
90776 return context.surface().call(behavior);
90779 context.uninstall = function (behavior) {
90780 return context.surface().call(behavior.off);
90787 context.copyGraph = function () {
90793 context.copyIDs = function (val) {
90794 if (!arguments.length) return _copyIDs;
90796 _copyGraph = _history.graph();
90802 context.copyLonLat = function (val) {
90803 if (!arguments.length) return _copyLonLat;
90812 context.background = function () {
90813 return _background;
90820 context.features = function () {
90824 context.hasHiddenConnections = function (id) {
90825 var graph = _history.graph();
90827 var entity = graph.entity(id);
90828 return _features.hasHiddenConnections(entity, graph);
90835 context.photos = function () {
90843 context.map = function () {
90847 context.layers = function () {
90848 return _map.layers();
90851 context.surface = function () {
90852 return _map.surface;
90855 context.editableDataEnabled = function () {
90856 return _map.editableDataEnabled();
90859 context.surfaceRect = function () {
90860 return _map.surface.node().getBoundingClientRect();
90863 context.editable = function () {
90864 // don't allow editing during save
90865 var mode = context.mode();
90866 if (!mode || mode.id === 'save') return false;
90867 return _map.editableDataEnabled();
90872 var _debugFlags = {
90876 // label collision bounding boxes
90878 // imagery bounding polygons
90881 downloaded: false // downloaded data from osm
90885 context.debugFlags = function () {
90886 return _debugFlags;
90889 context.getDebug = function (flag) {
90890 return flag && _debugFlags[flag];
90893 context.setDebug = function (flag, val) {
90894 if (arguments.length === 1) val = true;
90895 _debugFlags[flag] = val;
90896 dispatch.call('change');
90902 var _container = select(null);
90904 context.container = function (val) {
90905 if (!arguments.length) return _container;
90908 _container.classed('ideditor', true);
90913 context.containerNode = function (val) {
90914 if (!arguments.length) return context.container().node();
90915 context.container(select(val));
90921 context.embed = function (val) {
90922 if (!arguments.length) return _embed;
90929 var _assetPath = '';
90931 context.assetPath = function (val) {
90932 if (!arguments.length) return _assetPath;
90934 _mainFileFetcher.assetPath(val);
90938 var _assetMap = {};
90940 context.assetMap = function (val) {
90941 if (!arguments.length) return _assetMap;
90943 _mainFileFetcher.assetMap(val);
90947 context.asset = function (val) {
90948 if (/^http(s)?:\/\//i.test(val)) return val;
90949 var filename = _assetPath + val;
90950 return _assetMap[filename] || filename;
90953 context.imagePath = function (val) {
90954 return context.asset("img/".concat(val));
90956 /* reset (aka flush) */
90959 context.reset = context.flush = function () {
90960 context.debouncedSave.cancel();
90961 Array.from(_deferred).forEach(function (handle) {
90962 window.cancelIdleCallback(handle);
90964 _deferred["delete"](handle);
90966 Object.values(services).forEach(function (service) {
90967 if (service && typeof service.reset === 'function') {
90968 service.reset(context);
90971 context.changeset = null;
90973 _validator.reset();
90979 _uploader.reset(); // don't leave stale state in the inspector
90982 context.container().select('.inspector-wrap *').remove();
90988 context.projection = geoRawMercator();
90989 context.curtainProjection = geoRawMercator();
90992 context.init = function () {
90993 instantiateInternal();
90994 initializeDependents();
90995 return context; // Load variables and properties. No property of `context` should be accessed
90996 // until this is complete since load statuses are indeterminate. The order
90997 // of instantiation shouldn't matter.
90999 function instantiateInternal() {
91000 _history = coreHistory(context);
91001 context.graph = _history.graph;
91002 context.pauseChangeDispatch = _history.pauseChangeDispatch;
91003 context.resumeChangeDispatch = _history.resumeChangeDispatch;
91004 context.perform = withDebouncedSave(_history.perform);
91005 context.replace = withDebouncedSave(_history.replace);
91006 context.pop = withDebouncedSave(_history.pop);
91007 context.overwrite = withDebouncedSave(_history.overwrite);
91008 context.undo = withDebouncedSave(_history.undo);
91009 context.redo = withDebouncedSave(_history.redo);
91010 _validator = coreValidator(context);
91011 _uploader = coreUploader(context);
91012 _background = rendererBackground(context);
91013 _features = rendererFeatures(context);
91014 _map = rendererMap(context);
91015 _photos = rendererPhotos(context);
91016 _ui = uiInit(context);
91017 } // Set up objects that might need to access properties of `context`. The order
91018 // might matter if dependents make calls to each other. Be wary of async calls.
91021 function initializeDependents() {
91022 if (context.initialHashParams.presets) {
91023 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
91026 if (context.initialHashParams.locale) {
91027 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
91028 } // kick off some async work
91031 _mainLocalizer.ensureLoaded();
91033 _background.ensureLoaded();
91035 _mainPresetIndex.ensureLoaded();
91036 Object.values(services).forEach(function (service) {
91037 if (service && typeof service.init === 'function') {
91048 if (services.maprules && context.initialHashParams.maprules) {
91049 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
91050 services.maprules.init();
91051 mapcss.forEach(function (mapcssSelector) {
91052 return services.maprules.addRule(mapcssSelector);
91054 })["catch"](function () {
91057 } // if the container isn't available, e.g. when testing, don't load the UI
91060 if (!context.container().empty()) {
91061 _ui.ensureLoaded().then(function () {
91071 // NSI contains the most correct tagging for many commonly mapped features.
91072 // See https://github.com/osmlab/name-suggestion-index and https://nsi.guide
91075 var _nsiStatus = 'loading'; // 'loading', 'ok', 'failed'
91077 var _nsi = {}; // Sometimes we can upgrade a feature tagged like `building=yes` to a better tag.
91079 var buildingPreset = {
91080 'building/commercial': true,
91081 'building/government': true,
91082 'building/hotel': true,
91083 'building/retail': true,
91084 'building/office': true,
91085 'building/supermarket': true,
91086 'building/yes': true
91087 }; // Exceptions to the namelike regexes.
91088 // Usually a tag suffix contains a language code like `name:en`, `name:ru`
91089 // but we want to exclude things like `operator:type`, `name:etymology`, etc..
91091 var notNames = /:(colou?r|type|forward|backward|left|right|etymology|pronunciation|wikipedia)$/i; // Exceptions to the branchlike regexes
91093 var notBranches = /(coop|express|wireless|factory|outlet)/i; // PRIVATE FUNCTIONS
91094 // `setNsiSources()`
91095 // Adds the sources to iD's filemap so we can start downloading data.
91098 function setNsiSources() {
91099 var nsiVersion = packageJSON.devDependencies['name-suggestion-index'];
91100 var v = vparse(nsiVersion);
91101 var vMinor = "".concat(v.major, ".").concat(v.minor);
91103 'nsi_data': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/nsi.min.json"),
91104 'nsi_dissolved': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/dissolved.min.json"),
91105 'nsi_features': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/featureCollection.min.json"),
91106 'nsi_generics': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/genericWords.min.json"),
91107 'nsi_presets': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/presets/nsi-id-presets.min.json"),
91108 'nsi_replacements': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/replacements.min.json"),
91109 'nsi_trees': "https://cdn.jsdelivr.net/npm/name-suggestion-index@".concat(vMinor, "/dist/trees.min.json")
91111 var fileMap = _mainFileFetcher.fileMap();
91113 for (var k in sources) {
91114 fileMap[k] = sources[k];
91116 } // `loadNsiPresets()`
91117 // Returns a Promise fulfilled when the presets have been downloaded and merged into iD.
91121 function loadNsiPresets() {
91122 return Promise.all([_mainFileFetcher.get('nsi_presets'), _mainFileFetcher.get('nsi_features')]).then(function (vals) {
91123 // Add `suggestion=true` to all the nsi presets
91124 // The preset json schema doesn't include it, but the iD code still uses it
91125 Object.values(vals[0].presets).forEach(function (preset) {
91126 return preset.suggestion = true;
91128 _mainPresetIndex.merge({
91129 presets: vals[0].presets,
91130 featureCollection: vals[1]
91133 } // `loadNsiData()`
91134 // Returns a Promise fulfilled when the other data have been downloaded and processed
91138 function loadNsiData() {
91139 return Promise.all([_mainFileFetcher.get('nsi_data'), _mainFileFetcher.get('nsi_dissolved'), _mainFileFetcher.get('nsi_replacements'), _mainFileFetcher.get('nsi_trees')]).then(function (vals) {
91142 // the raw name-suggestion-index data
91143 dissolved: vals[1].dissolved,
91144 // list of dissolved items
91145 replacements: vals[2].replacements,
91146 // trivial old->new qid replacements
91147 trees: vals[3].trees,
91148 // metadata about trees, main tags
91150 // Map (k -> Map (v -> t) )
91152 // Map (wd/wp tag values -> qids)
91153 ids: new Map() // Map (id -> NSI item)
91156 _nsi.matcher = new Matcher();
91158 _nsi.matcher.buildMatchIndex(_nsi.data);
91160 _nsi.matcher.buildLocationIndex(_nsi.data, _mainLocations.loco());
91162 Object.keys(_nsi.data).forEach(function (tkv) {
91163 var category = _nsi.data[tkv];
91164 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
91168 var v = parts[2]; // Build a reverse index of keys -> values -> trees present in the name-suggestion-index
91169 // Collect primary keys (e.g. "amenity", "craft", "shop", "man_made", "route", etc)
91171 // "restaurant": "brands"
91174 var vmap = _nsi.kvt.get(k);
91179 _nsi.kvt.set(k, vmap);
91183 var tree = _nsi.trees[t]; // e.g. "brands", "operators"
91185 var mainTag = tree.mainTag; // e.g. "brand:wikidata", "operator:wikidata", etc
91187 var items = category.items || [];
91188 items.forEach(function (item) {
91189 // Remember some useful things for later, cache NSI id -> item
91191 item.mainTag = mainTag;
91193 _nsi.ids.set(item.id, item); // Cache Wikidata/Wikipedia values -> qid, for #6416
91196 var wd = item.tags[mainTag];
91197 var wp = item.tags[mainTag.replace('wikidata', 'wikipedia')];
91198 if (wd) _nsi.qids.set(wd, wd);
91199 if (wp && wd) _nsi.qids.set(wp, wd);
91204 // Gather all the k/v pairs that we will run through the NSI matcher.
91205 // An OSM tags object can contain anything, but only a few tags will be interesting to NSI.
91207 // This function will return the interesting tag pairs like:
91208 // "amenity/restaurant", "man_made/flagpole"
91209 // and fallbacks like
91211 // excluding things like
91212 // "tiger:reviewed", "surface", "ref", etc.
91215 // `tags`: `Object` containing the feature's OSM tags
91217 // `Object` containing kv pairs to test:
91219 // 'primary': Set(),
91220 // 'alternate': Set()
91225 function gatherKVs(tags) {
91226 var primary = new Set();
91227 var alternate = new Set();
91228 Object.keys(tags).forEach(function (osmkey) {
91229 var osmvalue = tags[osmkey];
91230 if (!osmvalue) return; // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91232 if (osmkey === 'route_master') osmkey = 'route';
91234 var vmap = _nsi.kvt.get(osmkey);
91236 if (!vmap) return; // not an interesting key
91238 if (vmap.get(osmvalue)) {
91239 // Matched a category in NSI
91240 primary.add("".concat(osmkey, "/").concat(osmvalue)); // interesting key/value
91241 } else if (osmvalue === 'yes') {
91242 alternate.add("".concat(osmkey, "/").concat(osmvalue)); // fallback key/yes
91244 }); // Can we try a generic building fallback match? - See #6122, #7197
91245 // Only try this if we do a preset match and find nothing else remarkable about that building.
91246 // For example, a way with `building=yes` + `name=Westfield` may be a Westfield department store.
91247 // But a way with `building=yes` + `name=Westfield` + `public_transport=station` is a train station for a town named "Westfield"
91249 var preset = _mainPresetIndex.matchTags(tags, 'area');
91251 if (buildingPreset[preset.id]) {
91252 alternate.add('building/yes');
91257 alternate: alternate
91259 } // `identifyTree()`
91260 // NSI has a concept of trees: "brands", "operators", "flags", "transit".
91261 // The tree determines things like which tags are namelike, and which tags hold important wikidata.
91262 // This takes an Object of tags and tries to identify what tree to use.
91265 // `tags`: `Object` containing the feature's OSM tags
91267 // `string` the name of the tree if known
91268 // or 'unknown' if it could match several trees (e.g. amenity/yes)
91269 // or null if no match
91273 function identifyTree(tags) {
91275 var t; // Check all tags
91277 Object.keys(tags).forEach(function (osmkey) {
91278 if (t) return; // found already
91280 var osmvalue = tags[osmkey];
91281 if (!osmvalue) return; // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91283 if (osmkey === 'route_master') osmkey = 'route';
91285 var vmap = _nsi.kvt.get(osmkey);
91287 if (!vmap) return; // this key is not in nsi
91289 if (osmvalue === 'yes') {
91290 unknown = 'unknown';
91292 t = vmap.get(osmvalue);
91295 return t || unknown || null;
91296 } // `gatherNames()`
91297 // Gather all the namelike values that we will run through the NSI matcher.
91298 // It will gather values primarily from tags `name`, `name:ru`, `flag:name`
91299 // and fallback to alternate tags like `brand`, `brand:ru`, `alt_name`
91302 // `tags`: `Object` containing the feature's OSM tags
91304 // `Object` containing namelike values to test:
91306 // 'primary': Set(),
91307 // 'fallbacks': Set()
91312 function gatherNames(tags) {
91314 primary: new Set(),
91315 alternate: new Set()
91317 var primary = new Set();
91318 var alternate = new Set();
91319 var foundSemi = false;
91320 var testNameFragments = false;
91321 var patterns; // Patterns for matching OSM keys that might contain namelike values.
91322 // These roughly correspond to the "trees" concept in name-suggestion-index,
91324 var t = identifyTree(tags);
91325 if (!t) return empty;
91327 if (t === 'transit') {
91329 primary: /^network$/i,
91330 alternate: /^(operator|operator:\w+|network:\w+|\w+_name|\w+_name:\w+)$/i
91332 } else if (t === 'flags') {
91334 primary: /^(flag:name|flag:name:\w+)$/i,
91335 alternate: /^(flag|flag:\w+|subject|subject:\w+)$/i // note: no `country`, we special-case it below
91338 } else if (t === 'brands') {
91339 testNameFragments = true;
91341 primary: /^(name|name:\w+)$/i,
91342 alternate: /^(brand|brand:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
91344 } else if (t === 'operators') {
91345 testNameFragments = true;
91347 primary: /^(name|name:\w+|operator|operator:\w+)$/i,
91348 alternate: /^(brand|brand:\w+|\w+_name|\w+_name:\w+)/i
91351 // unknown/multiple
91352 testNameFragments = true;
91354 primary: /^(name|name:\w+)$/i,
91355 alternate: /^(brand|brand:\w+|network|network:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i
91357 } // Test `name` fragments, longest to shortest, to fit them into a "Name Branch" pattern.
91358 // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
91361 if (tags.name && testNameFragments) {
91362 var nameParts = tags.name.split(/[\s\-\/,.]/);
91364 for (var split = nameParts.length; split > 0; split--) {
91365 var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
91369 } // Check all tags
91372 Object.keys(tags).forEach(function (osmkey) {
91373 var osmvalue = tags[osmkey];
91374 if (!osmvalue) return;
91376 if (isNamelike(osmkey, 'primary')) {
91377 if (/;/.test(osmvalue)) {
91380 primary.add(osmvalue);
91381 alternate["delete"](osmvalue);
91383 } else if (!primary.has(osmvalue) && isNamelike(osmkey, 'alternate')) {
91384 if (/;/.test(osmvalue)) {
91387 alternate.add(osmvalue);
91390 }); // For flags only, fallback to `country` tag only if no other namelike values were found.
91391 // See https://github.com/openstreetmap/iD/pull/8305#issuecomment-769174070
91393 if (tags.man_made === 'flagpole' && !primary.size && !alternate.size && !!tags.country) {
91394 var osmvalue = tags.country;
91396 if (/;/.test(osmvalue)) {
91399 alternate.add(osmvalue);
91401 } // If any namelike value contained a semicolon, return empty set and don't try matching anything.
91409 alternate: alternate
91413 function isNamelike(osmkey, which) {
91414 if (osmkey === 'old_name') return false;
91415 return patterns[which].test(osmkey) && !notNames.test(osmkey);
91417 } // `gatherTuples()`
91418 // Generate all combinations of [key,value,name] that we want to test.
91419 // This prioritizes them so that the primary name and k/v pairs go first
91422 // `tryKVs`: `Object` containing primary and alternate k/v pairs to test
91423 // `tryNames`: `Object` containing primary and alternate names to test
91425 // `Array`: tuple objects ordered by priority
91429 function gatherTuples(tryKVs, tryNames) {
91431 ['primary', 'alternate'].forEach(function (whichName) {
91432 // test names longest to shortest
91433 var arr = Array.from(tryNames[whichName]).sort(function (a, b) {
91434 return b.length - a.length;
91436 arr.forEach(function (n) {
91437 ['primary', 'alternate'].forEach(function (whichKV) {
91438 tryKVs[whichKV].forEach(function (kv) {
91439 var parts = kv.split('/', 2);
91452 } // `_upgradeTags()`
91453 // Try to match a feature to a canonical record in name-suggestion-index
91454 // and upgrade the tags to match.
91457 // `tags`: `Object` containing the feature's OSM tags
91458 // `loc`: Location where this feature exists, as a [lon, lat]
91460 // `Object`: The tags the the feature should have, or `null` if no changes needed
91464 function _upgradeTags(tags, loc) {
91465 var newTags = Object.assign({}, tags); // shallow copy
91467 var changed = false; // Before anything, perform trivial Wikipedia/Wikidata replacements
91469 Object.keys(newTags).forEach(function (osmkey) {
91470 var matchTag = osmkey.match(/^(\w+:)?wikidata$/);
91473 // Look at '*:wikidata' tags
91474 var prefix = matchTag[1] || '';
91475 var wd = newTags[osmkey];
91476 var replace = _nsi.replacements[wd]; // If it matches a QID in the replacement list...
91478 if (replace && replace.wikidata !== undefined) {
91479 // replace or delete `*:wikidata` tag
91482 if (replace.wikidata) {
91483 newTags[osmkey] = replace.wikidata;
91485 delete newTags[osmkey];
91489 if (replace && replace.wikipedia !== undefined) {
91490 // replace or delete `*:wikipedia` tag
91492 var wpkey = "".concat(prefix, "wikipedia");
91494 if (replace.wikipedia) {
91495 newTags[wpkey] = replace.wikipedia;
91497 delete newTags[wpkey];
91501 }); // Match a 'route_master' as if it were a 'route' - name-suggestion-index#5184
91503 var isRouteMaster = tags.type === 'route_master'; // Gather key/value tag pairs to try to match
91505 var tryKVs = gatherKVs(tags);
91506 if (!tryKVs.primary.size && !tryKVs.alternate.size) return changed ? newTags : null; // Gather namelike tag values to try to match
91508 var tryNames = gatherNames(tags); // Do `wikidata=*` or `wikipedia=*` tags identify this entity as a chain? - See #6416
91509 // If so, these tags can be swapped to e.g. `brand:wikidata`/`brand:wikipedia`.
91511 var foundQID = _nsi.qids.get(tags.wikidata) || _nsi.qids.get(tags.wikipedia);
91513 if (foundQID) tryNames.primary.add(foundQID); // matcher will recognize the Wikidata QID as name too
91515 if (!tryNames.primary.size && !tryNames.alternate.size) return changed ? newTags : null; // Order the [key,value,name] tuples - test primary before alternate
91517 var tuples = gatherTuples(tryKVs, tryNames);
91519 var _loop = function _loop(i) {
91520 var tuple = tuples[i];
91522 var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n, loc); // Attempt to match an item in NSI
91525 if (!hits || !hits.length) return "continue"; // no match, try next tuple
91527 if (hits[0].match !== 'primary' && hits[0].match !== 'alternate') return "break"; // a generic match, stop looking
91528 // A match may contain multiple results, the first one is likely the best one for this location
91529 // e.g. `['pfk-a54c14', 'kfc-1ff19c', 'kfc-658eea']`
91531 var itemID = void 0,
91534 for (var j = 0; j < hits.length; j++) {
91536 itemID = hit.itemID;
91537 if (_nsi.dissolved[itemID]) continue; // Don't upgrade to a dissolved item
91539 item = _nsi.ids.get(itemID);
91540 if (!item) continue;
91541 var mainTag = item.mainTag; // e.g. `brand:wikidata`
91543 var itemQID = item.tags[mainTag]; // e.g. `brand:wikidata` qid
91545 var notQID = newTags["not:".concat(mainTag)]; // e.g. `not:brand:wikidata` qid
91547 if ( // Exceptions, skip this hit
91548 !itemQID || itemQID === notQID || // No `*:wikidata` or matched a `not:*:wikidata`
91549 newTags.office && !item.tags.office // feature may be a corporate office for a brand? - #6416
91552 continue; // continue looking
91554 break; // use `item`
91556 } // Can't use any of these hits, try next tuple..
91559 if (!item) return "continue"; // At this point we have matched a canonical item and can suggest tag upgrades..
91561 var tkv = item.tkv;
91562 var parts = tkv.split('/', 3); // tkv = "tree/key/value"
91566 var category = _nsi.data[tkv];
91567 var properties = category.properties || {}; // Preserve some tags that we specifically don't want NSI to overwrite. ('^name', sometimes)
91569 var preserveTags = item.preserveTags || properties.preserveTags || []; // These tags can be toplevel tags -or- attributes - so we generally want to preserve existing values - #8615
91570 // We'll only _replace_ the tag value if this tag is the toplevel/defining tag for the matched item (`k`)
91572 ['building', 'emergency', 'internet_access', 'takeaway'].forEach(function (osmkey) {
91573 if (k !== osmkey) preserveTags.push("^".concat(osmkey, "$"));
91575 var regexes = preserveTags.map(function (s) {
91576 return new RegExp(s, 'i');
91579 Object.keys(newTags).forEach(function (osmkey) {
91580 if (regexes.some(function (regex) {
91581 return regex.test(osmkey);
91583 keepTags[osmkey] = newTags[osmkey];
91585 }); // Remove any primary tags ("amenity", "craft", "shop", "man_made", "route", etc) that have a
91586 // value like `amenity=yes` or `shop=yes` (exceptions have already been added to `keepTags` above)
91588 _nsi.kvt.forEach(function (vmap, k) {
91589 if (newTags[k] === 'yes') delete newTags[k];
91590 }); // Replace mistagged `wikidata`/`wikipedia` with e.g. `brand:wikidata`/`brand:wikipedia`
91594 delete newTags.wikipedia;
91595 delete newTags.wikidata;
91596 } // Do the tag upgrade
91599 Object.assign(newTags, item.tags, keepTags); // Swap `route` back to `route_master` - name-suggestion-index#5184
91601 if (isRouteMaster) {
91602 newTags.route_master = newTags.route;
91603 delete newTags.route;
91604 } // Special `branch` splitting rules - IF..
91605 // - NSI is suggesting to replace `name`, AND
91606 // - `branch` doesn't already contain something, AND
91607 // - original name has not moved to an alternate name (e.g. "Dunkin' Donuts" -> "Dunkin'"), AND
91608 // - original name is "some name" + "some stuff", THEN
91609 // consider splitting `name` into `name`/`branch`..
91612 var origName = tags.name;
91613 var newName = newTags.name;
91615 if (newName && origName && newName !== origName && !newTags.branch) {
91616 var newNames = gatherNames(newTags);
91617 var newSet = new Set([].concat(_toConsumableArray(newNames.primary), _toConsumableArray(newNames.alternate)));
91618 var isMoved = newSet.has(origName); // another tag holds the original name now
91621 // Test name fragments, longest to shortest, to fit them into a "Name Branch" pattern.
91622 // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
91623 var nameParts = origName.split(/[\s\-\/,.]/);
91625 for (var split = nameParts.length; split > 0; split--) {
91626 var name = nameParts.slice(0, split).join(' '); // e.g. "TUI ReiseCenter"
91628 var branch = nameParts.slice(split).join(' '); // e.g. "Neuss Innenstadt"
91630 var nameHits = _nsi.matcher.match(k, v, name, loc);
91632 if (!nameHits || !nameHits.length) continue; // no match, try next name fragment
91634 if (nameHits.some(function (hit) {
91635 return hit.itemID === itemID;
91637 // matched the name fragment to the same itemID above
91639 if (notBranches.test(branch)) {
91640 // "branch" was detected but is noise ("factory outlet", etc)
91641 newTags.name = origName; // Leave `name` alone, this part of the name may be significant..
91643 var branchHits = _nsi.matcher.match(k, v, branch, loc);
91645 if (branchHits && branchHits.length) {
91646 // if "branch" matched something else in NSI..
91647 if (branchHits[0].match === 'primary' || branchHits[0].match === 'alternate') {
91648 // if another brand! (e.g. "KFC - Taco Bell"?)
91651 }; // bail out - can't suggest tags in this case
91652 } // else a generic (e.g. "gas", "cafe") - ignore
91655 // "branch" is not noise and not something in NSI
91656 newTags.branch = branch; // Stick it in the `branch` tag..
91672 for (var i = 0; i < tuples.length; i++) {
91673 var _ret = _loop(i);
91675 if (_ret === "continue") continue;
91676 if (_ret === "break") break;
91677 if (_typeof(_ret) === "object") return _ret.v;
91680 return changed ? newTags : null;
91681 } // `_isGenericName()`
91682 // Is the `name` tag generic?
91685 // `tags`: `Object` containing the feature's OSM tags
91687 // `true` if it is generic, `false` if not
91691 function _isGenericName(tags) {
91693 if (!n) return false; // tryNames just contains the `name` tag value and nothing else
91696 primary: new Set([n]),
91697 alternate: new Set()
91698 }; // Gather key/value tag pairs to try to match
91700 var tryKVs = gatherKVs(tags);
91701 if (!tryKVs.primary.size && !tryKVs.alternate.size) return false; // Order the [key,value,name] tuples - test primary before alternate
91703 var tuples = gatherTuples(tryKVs, tryNames);
91705 for (var i = 0; i < tuples.length; i++) {
91706 var tuple = tuples[i];
91708 var hits = _nsi.matcher.match(tuple.k, tuple.v, tuple.n); // Attempt to match an item in NSI
91709 // If we get a `excludeGeneric` hit, this is a generic name.
91712 if (hits && hits.length && hits[0].match === 'excludeGeneric') return true;
91716 } // PUBLIC INTERFACE
91721 // On init, start preparing the name-suggestion-index
91723 init: function init() {
91724 // Note: service.init is called immediately after the presetManager has started loading its data.
91725 // We expect to chain onto an unfulfilled promise here.
91727 _mainPresetIndex.ensureLoaded().then(function () {
91728 return loadNsiPresets();
91729 }).then(function () {
91731 }) // wait briefly for locationSets to enter the locationManager queue
91732 .then(function () {
91733 return _mainLocations.mergeLocationSets([]);
91734 }) // wait for locationSets to resolve
91735 .then(function () {
91736 return loadNsiData();
91737 }).then(function () {
91738 return _nsiStatus = 'ok';
91739 })["catch"](function () {
91740 return _nsiStatus = 'failed';
91743 function delay(msec) {
91744 return new Promise(function (resolve) {
91745 window.setTimeout(resolve, msec);
91750 // Reset is called when user saves data to OSM (does nothing here)
91752 reset: function reset() {},
91754 // To let other code know how it's going...
91757 // `String`: 'loading', 'ok', 'failed'
91759 status: function status() {
91762 // `isGenericName()`
91763 // Is the `name` tag generic?
91766 // `tags`: `Object` containing the feature's OSM tags
91768 // `true` if it is generic, `false` if not
91770 isGenericName: function isGenericName(tags) {
91771 return _isGenericName(tags);
91774 // Suggest tag upgrades.
91775 // This function will not modify the input tags, it makes a copy.
91778 // `tags`: `Object` containing the feature's OSM tags
91779 // `loc`: Location where this feature exists, as a [lon, lat]
91781 // `Object`: The tags the the feature should have, or `null` if no change
91783 upgradeTags: function upgradeTags(tags, loc) {
91784 return _upgradeTags(tags, loc);
91787 // Direct access to the NSI cache, useful for testing or breaking things
91790 // `Object`: the internal NSI cache
91792 cache: function cache() {
91797 var apibase$1 = 'https://openstreetcam.org';
91798 var maxResults$1 = 1000;
91799 var tileZoom$1 = 14;
91800 var tiler$3 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
91801 var dispatch$3 = dispatch$8('loadedImages');
91802 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
91806 var _oscSelectedImage;
91808 var _loadViewerPromise$1;
91810 function abortRequest$3(controller) {
91811 controller.abort();
91814 function maxPageAtZoom(z) {
91815 if (z < 15) return 2;
91816 if (z === 15) return 5;
91817 if (z === 16) return 10;
91818 if (z === 17) return 20;
91819 if (z === 18) return 40;
91820 if (z > 18) return 80;
91823 function loadTiles$1(which, url, projection) {
91824 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
91825 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
91827 var cache = _oscCache[which];
91828 Object.keys(cache.inflight).forEach(function (k) {
91829 var wanted = tiles.find(function (tile) {
91830 return k.indexOf(tile.id + ',') === 0;
91834 abortRequest$3(cache.inflight[k]);
91835 delete cache.inflight[k];
91838 tiles.forEach(function (tile) {
91839 loadNextTilePage$1(which, currZoom, url, tile);
91843 function loadNextTilePage$1(which, currZoom, url, tile) {
91844 var cache = _oscCache[which];
91845 var bbox = tile.extent.bbox();
91846 var maxPages = maxPageAtZoom(currZoom);
91847 var nextPage = cache.nextPage[tile.id] || 1;
91848 var params = utilQsString({
91851 // client_id: clientId,
91852 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
91853 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
91855 if (nextPage > maxPages) return;
91856 var id = tile.id + ',' + String(nextPage);
91857 if (cache.loaded[id] || cache.inflight[id]) return;
91858 var controller = new AbortController();
91859 cache.inflight[id] = controller;
91862 signal: controller.signal,
91865 'Content-Type': 'application/x-www-form-urlencoded'
91868 d3_json(url, options).then(function (data) {
91869 cache.loaded[id] = true;
91870 delete cache.inflight[id];
91872 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
91873 throw new Error('No Data');
91876 var features = data.currentPageItems.map(function (item) {
91877 var loc = [+item.lng, +item.lat];
91880 if (which === 'images') {
91885 captured_at: item.shot_date || item.date_added,
91886 captured_by: item.username,
91887 imagePath: item.lth_name,
91888 sequence_id: item.sequence_id,
91889 sequence_index: +item.sequence_index
91890 }; // cache sequence info
91892 var seq = _oscCache.sequences[d.sequence_id];
91899 _oscCache.sequences[d.sequence_id] = seq;
91902 seq.images[d.sequence_index] = d;
91903 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
91914 cache.rtree.load(features);
91916 if (data.currentPageItems.length === maxResults$1) {
91917 // more pages to load
91918 cache.nextPage[tile.id] = nextPage + 1;
91919 loadNextTilePage$1(which, currZoom, url, tile);
91921 cache.nextPage[tile.id] = Infinity; // no more pages to load
91924 if (which === 'images') {
91925 dispatch$3.call('loadedImages');
91927 })["catch"](function () {
91928 cache.loaded[id] = true;
91929 delete cache.inflight[id];
91931 } // partition viewport into higher zoom tiles
91934 function partitionViewport$1(projection) {
91935 var z = geoScaleToZoom(projection.scale());
91936 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
91938 var tiler = utilTiler().zoomExtent([z2, z2]);
91939 return tiler.getTiles(projection).map(function (tile) {
91940 return tile.extent;
91942 } // no more than `limit` results per partition.
91945 function searchLimited$1(limit, projection, rtree) {
91946 limit = limit || 5;
91947 return partitionViewport$1(projection).reduce(function (result, extent) {
91948 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
91951 return found.length ? result.concat(found) : result;
91955 var serviceOpenstreetcam = {
91956 init: function init() {
91961 this.event = utilRebind(this, dispatch$3, 'on');
91963 reset: function reset() {
91965 Object.values(_oscCache.images.inflight).forEach(abortRequest$3);
91973 rtree: new RBush(),
91978 _oscSelectedImage = null;
91980 images: function images(projection) {
91982 return searchLimited$1(limit, projection, _oscCache.images.rtree);
91984 sequences: function sequences(projection) {
91985 var viewport = projection.clipExtent();
91986 var min = [viewport[0][0], viewport[1][1]];
91987 var max = [viewport[1][0], viewport[0][1]];
91988 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
91989 var sequenceKeys = {}; // all sequences for images in viewport
91991 _oscCache.images.rtree.search(bbox).forEach(function (d) {
91992 sequenceKeys[d.data.sequence_id] = true;
91993 }); // make linestrings from those sequences
91996 var lineStrings = [];
91997 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
91998 var seq = _oscCache.sequences[sequenceKey];
91999 var images = seq && seq.images;
92003 type: 'LineString',
92004 coordinates: images.map(function (d) {
92006 }).filter(Boolean),
92008 captured_at: images[0] ? images[0].captured_at : null,
92009 captured_by: images[0] ? images[0].captured_by : null,
92015 return lineStrings;
92017 cachedImage: function cachedImage(imageKey) {
92018 return _oscCache.images.forImageKey[imageKey];
92020 loadImages: function loadImages(projection) {
92021 var url = apibase$1 + '/1.0/list/nearby-photos/';
92022 loadTiles$1('images', url, projection);
92024 ensureViewerLoaded: function ensureViewerLoaded(context) {
92025 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
92027 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
92029 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
92030 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
92031 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
92032 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
92033 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
92034 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
92035 controlsEnter.append('button').on('click.forward', step(1)).html('►');
92036 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
92038 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
92039 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
92042 function zoomPan(d3_event) {
92043 var t = d3_event.transform;
92044 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
92047 function rotate(deg) {
92048 return function () {
92049 if (!_oscSelectedImage) return;
92050 var sequenceKey = _oscSelectedImage.sequence_id;
92051 var sequence = _oscCache.sequences[sequenceKey];
92052 if (!sequence) return;
92053 var r = sequence.rotation || 0;
92055 if (r > 180) r -= 360;
92056 if (r < -180) r += 360;
92057 sequence.rotation = r;
92058 var wrap = context.container().select('.photoviewer .osc-wrapper');
92059 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
92060 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
92064 function step(stepBy) {
92065 return function () {
92066 if (!_oscSelectedImage) return;
92067 var sequenceKey = _oscSelectedImage.sequence_id;
92068 var sequence = _oscCache.sequences[sequenceKey];
92069 if (!sequence) return;
92070 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
92071 var nextImage = sequence.images[nextIndex];
92072 if (!nextImage) return;
92073 context.map().centerEase(nextImage.loc);
92074 that.selectImage(context, nextImage.key);
92076 } // don't need any async loading so resolve immediately
92079 _loadViewerPromise$1 = Promise.resolve();
92080 return _loadViewerPromise$1;
92082 showViewer: function showViewer(context) {
92083 var viewer = context.container().select('.photoviewer').classed('hide', false);
92084 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
92087 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
92088 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
92093 hideViewer: function hideViewer(context) {
92094 _oscSelectedImage = null;
92095 this.updateUrlImage(null);
92096 var viewer = context.container().select('.photoviewer');
92097 if (!viewer.empty()) viewer.datum(null);
92098 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
92099 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
92100 return this.setStyles(context, null, true);
92102 selectImage: function selectImage(context, imageKey) {
92103 var d = this.cachedImage(imageKey);
92104 _oscSelectedImage = d;
92105 this.updateUrlImage(imageKey);
92106 var viewer = context.container().select('.photoviewer');
92107 if (!viewer.empty()) viewer.datum(d);
92108 this.setStyles(context, null, true);
92109 context.container().selectAll('.icon-sign').classed('currentView', false);
92110 if (!d) return this;
92111 var wrap = context.container().select('.photoviewer .osc-wrapper');
92112 var imageWrap = wrap.selectAll('.osc-image-wrap');
92113 var attribution = wrap.selectAll('.photo-attribution').html('');
92114 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
92115 imageWrap.selectAll('.osc-image').remove();
92118 var sequence = _oscCache.sequences[d.sequence_id];
92119 var r = sequence && sequence.rotation || 0;
92120 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$1 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
92122 if (d.captured_by) {
92123 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
92124 attribution.append('span').html('|');
92127 if (d.captured_at) {
92128 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
92129 attribution.append('span').html('|');
92132 attribution.append('a').attr('class', 'image-link').attr('target', '_blank').attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index).html('openstreetcam.org');
92137 function localeDateString(s) {
92138 if (!s) return null;
92144 var d = new Date(s);
92145 if (isNaN(d.getTime())) return null;
92146 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
92149 getSelectedImage: function getSelectedImage() {
92150 return _oscSelectedImage;
92152 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
92153 return d && d.sequence_id;
92155 // Updates the currently highlighted sequence and selected bubble.
92156 // Reset is only necessary when interacting with the viewport because
92157 // this implicitly changes the currently selected bubble/sequence
92158 setStyles: function setStyles(context, hovered, reset) {
92160 // reset all layers
92161 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
92162 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
92165 var hoveredImageKey = hovered && hovered.key;
92166 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
92167 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
92168 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
92171 var viewer = context.container().select('.photoviewer');
92172 var selected = viewer.empty() ? undefined : viewer.datum();
92173 var selectedImageKey = selected && selected.key;
92174 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
92175 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
92176 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
92178 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
92180 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
92181 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
92182 return highlightedImageKeys.indexOf(d.key) !== -1;
92183 }).classed('hovered', function (d) {
92184 return d.key === hoveredImageKey;
92185 }).classed('currentView', function (d) {
92186 return d.key === selectedImageKey;
92188 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
92189 return d.properties.key === hoveredSequenceKey;
92190 }).classed('currentView', function (d) {
92191 return d.properties.key === selectedSequenceKey;
92192 }); // update viewfields if needed
92194 context.container().selectAll('.layer-openstreetcam .viewfield-group .viewfield').attr('d', viewfieldPath);
92196 function viewfieldPath() {
92197 var d = this.parentNode.__data__;
92199 if (d.pano && d.key !== selectedImageKey) {
92200 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
92202 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
92208 updateUrlImage: function updateUrlImage(imageKey) {
92209 if (!window.mocha) {
92210 var hash = utilStringQs(window.location.hash);
92213 hash.photo = 'openstreetcam/' + imageKey;
92218 window.location.replace('#' + utilQsString(hash, true));
92221 cache: function cache() {
92226 var hashes = createCommonjsModule(function (module, exports) {
92230 function utf8Encode(str) {
92237 if (str && str.length) {
92240 while ((i += 1) < l) {
92241 /* Decode utf-16 surrogate pairs */
92242 x = str.charCodeAt(i);
92243 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
92245 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
92246 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
92249 /* Encode output as utf-8 */
92253 output += String.fromCharCode(x);
92254 } else if (x <= 0x7FF) {
92255 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
92256 } else if (x <= 0xFFFF) {
92257 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
92258 } else if (x <= 0x1FFFFF) {
92259 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
92267 function utf8Decode(str) {
92275 i = ac = c1 = c2 = c3 = 0;
92277 if (str && str.length) {
92282 c1 = str.charCodeAt(i);
92286 arr[ac] = String.fromCharCode(c1);
92288 } else if (c1 > 191 && c1 < 224) {
92289 c2 = str.charCodeAt(i + 1);
92290 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
92293 c2 = str.charCodeAt(i + 1);
92294 c3 = str.charCodeAt(i + 2);
92295 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
92301 return arr.join('');
92304 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
92305 * to work around bugs in some JS interpreters.
92309 function safe_add(x, y) {
92310 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
92311 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
92312 return msw << 16 | lsw & 0xFFFF;
92315 * Bitwise rotate a 32-bit number to the left.
92319 function bit_rol(num, cnt) {
92320 return num << cnt | num >>> 32 - cnt;
92323 * Convert a raw string to a hex string
92327 function rstr2hex(input, hexcase) {
92328 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
92334 for (; i < l; i += 1) {
92335 x = input.charCodeAt(i);
92336 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
92342 * Convert an array of big-endian words to a string
92346 function binb2rstr(input) {
92348 l = input.length * 32,
92351 for (i = 0; i < l; i += 8) {
92352 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
92358 * Convert an array of little-endian words to a string
92362 function binl2rstr(input) {
92364 l = input.length * 32,
92367 for (i = 0; i < l; i += 8) {
92368 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
92374 * Convert a raw string to an array of little-endian words
92375 * Characters >255 have their high-byte silently ignored.
92379 function rstr2binl(input) {
92381 l = input.length * 8,
92382 output = Array(input.length >> 2),
92383 lo = output.length;
92385 for (i = 0; i < lo; i += 1) {
92389 for (i = 0; i < l; i += 8) {
92390 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
92396 * Convert a raw string to an array of big-endian words
92397 * Characters >255 have their high-byte silently ignored.
92401 function rstr2binb(input) {
92403 l = input.length * 8,
92404 output = Array(input.length >> 2),
92405 lo = output.length;
92407 for (i = 0; i < lo; i += 1) {
92411 for (i = 0; i < l; i += 8) {
92412 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
92418 * Convert a raw string to an arbitrary string encoding
92422 function rstr2any(input, encoding) {
92423 var divisor = encoding.length,
92424 remainders = Array(),
92433 /* Convert to an array of 16-bit big-endian values, forming the dividend */
92435 dividend = Array(Math.ceil(input.length / 2));
92436 ld = dividend.length;
92438 for (i = 0; i < ld; i += 1) {
92439 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
92442 * Repeatedly perform a long division. The binary array forms the dividend,
92443 * the length of the encoding is the divisor. Once computed, the quotient
92444 * forms the dividend for the next step. We stop when the dividend is zerHashes.
92445 * All remainders are stored for later use.
92449 while (dividend.length > 0) {
92450 quotient = Array();
92453 for (i = 0; i < dividend.length; i += 1) {
92454 x = (x << 16) + dividend[i];
92455 q = Math.floor(x / divisor);
92458 if (quotient.length > 0 || q > 0) {
92459 quotient[quotient.length] = q;
92463 remainders[remainders.length] = x;
92464 dividend = quotient;
92466 /* Convert the remainders to the output string */
92471 for (i = remainders.length - 1; i >= 0; i--) {
92472 output += encoding.charAt(remainders[i]);
92474 /* Append leading zero equivalents */
92477 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
92479 for (i = output.length; i < full_length; i += 1) {
92480 output = encoding[0] + output;
92486 * Convert a raw string to a base-64 string
92490 function rstr2b64(input, b64pad) {
92491 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
92493 len = input.length,
92497 b64pad = b64pad || '=';
92499 for (i = 0; i < len; i += 3) {
92500 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
92502 for (j = 0; j < 4; j += 1) {
92503 if (i * 8 + j * 6 > input.length * 8) {
92506 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
92516 * @property {String} version
92526 Base64: function Base64() {
92527 // private properties
92528 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
92530 // URL encoding support @todo
92531 utf8 = true; // by default enable UTF-8 support encoding
92532 // public method for encoding
92534 this.encode = function (input) {
92539 len = input.length;
92541 input = utf8 ? utf8Encode(input) : input;
92543 for (i = 0; i < len; i += 3) {
92544 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
92546 for (j = 0; j < 4; j += 1) {
92547 if (i * 8 + j * 6 > len * 8) {
92550 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
92556 }; // public method for decoding
92559 this.decode = function (input) {
92560 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
92579 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
92583 // unpack four hexets into three octets using index points in b64
92584 h1 = tab.indexOf(input.charAt(i += 1));
92585 h2 = tab.indexOf(input.charAt(i += 1));
92586 h3 = tab.indexOf(input.charAt(i += 1));
92587 h4 = tab.indexOf(input.charAt(i += 1));
92588 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
92589 o1 = bits >> 16 & 0xff;
92590 o2 = bits >> 8 & 0xff;
92595 arr[ac] = String.fromCharCode(o1);
92596 } else if (h4 === 64) {
92597 arr[ac] = String.fromCharCode(o1, o2);
92599 arr[ac] = String.fromCharCode(o1, o2, o3);
92601 } while (i < input.length);
92603 dec = arr.join('');
92604 dec = utf8 ? utf8Decode(dec) : dec;
92606 }; // set custom pad string
92609 this.setPad = function (str) {
92612 }; // set custom tab string characters
92615 this.setTab = function (str) {
92620 this.setUTF8 = function (bool) {
92621 if (typeof bool === 'boolean') {
92630 * CRC-32 calculation
92634 * @param {String} str Input String
92637 CRC32: function CRC32(str) {
92644 str = utf8Encode(str);
92645 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('');
92648 for (i = 0, iTop = str.length; i < iTop; i += 1) {
92649 y = (crc ^ str.charCodeAt(i)) & 0xFF;
92650 x = '0x' + table.substr(y * 9, 8);
92651 crc = crc >>> 8 ^ x;
92652 } // always return a positive number (that's what >>> 0 does)
92655 return (crc ^ -1) >>> 0;
92662 * @param {Object} [config]
92664 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
92665 * Digest Algorithm, as defined in RFC 1321.
92666 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
92667 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
92668 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
92670 MD5: function MD5(options) {
92672 * Private config properties. You may need to tweak these to be compatible with
92673 * the server-side, but the defaults work in most cases.
92674 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
92676 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
92677 // hexadecimal output case format. false - lowercase; true - uppercase
92678 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
92679 // base-64 pad character. Defaults to '=' for strict RFC compliance
92680 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
92681 // privileged (public) methods
92683 this.hex = function (s) {
92684 return rstr2hex(rstr(s), hexcase);
92687 this.b64 = function (s) {
92688 return rstr2b64(rstr(s), b64pad);
92691 this.any = function (s, e) {
92692 return rstr2any(rstr(s), e);
92695 this.raw = function (s) {
92699 this.hex_hmac = function (k, d) {
92700 return rstr2hex(rstr_hmac(k, d), hexcase);
92703 this.b64_hmac = function (k, d) {
92704 return rstr2b64(rstr_hmac(k, d), b64pad);
92707 this.any_hmac = function (k, d, e) {
92708 return rstr2any(rstr_hmac(k, d), e);
92711 * Perform a simple self-test to see if the VM is working
92712 * @return {String} Hexadecimal hash sample
92716 this.vm_test = function () {
92717 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
92720 * Enable/disable uppercase hexadecimal returned string
92722 * @return {Object} this
92726 this.setUpperCase = function (a) {
92727 if (typeof a === 'boolean') {
92734 * Defines a base64 pad string
92735 * @param {String} Pad
92736 * @return {Object} this
92740 this.setPad = function (a) {
92741 b64pad = a || b64pad;
92745 * Defines a base64 pad string
92747 * @return {Object} [this]
92751 this.setUTF8 = function (a) {
92752 if (typeof a === 'boolean') {
92757 }; // private methods
92760 * Calculate the MD5 of a raw string
92765 s = utf8 ? utf8Encode(s) : s;
92766 return binl2rstr(binl(rstr2binl(s), s.length * 8));
92769 * Calculate the HMAC-MD5, of a key and some data (raw strings)
92773 function rstr_hmac(key, data) {
92774 var bkey, ipad, opad, hash, i;
92775 key = utf8 ? utf8Encode(key) : key;
92776 data = utf8 ? utf8Encode(data) : data;
92777 bkey = rstr2binl(key);
92779 if (bkey.length > 16) {
92780 bkey = binl(bkey, key.length * 8);
92783 ipad = Array(16), opad = Array(16);
92785 for (i = 0; i < 16; i += 1) {
92786 ipad[i] = bkey[i] ^ 0x36363636;
92787 opad[i] = bkey[i] ^ 0x5C5C5C5C;
92790 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
92791 return binl2rstr(binl(opad.concat(hash), 512 + 128));
92794 * Calculate the MD5 of an array of little-endian words, and a bit length.
92798 function binl(x, len) {
92808 /* append padding */
92810 x[len >> 5] |= 0x80 << len % 32;
92811 x[(len + 64 >>> 9 << 4) + 14] = len;
92813 for (i = 0; i < x.length; i += 16) {
92818 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
92819 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
92820 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
92821 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
92822 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
92823 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
92824 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
92825 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
92826 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
92827 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
92828 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
92829 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
92830 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
92831 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
92832 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
92833 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
92834 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
92835 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
92836 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
92837 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
92838 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
92839 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
92840 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
92841 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
92842 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
92843 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
92844 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
92845 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
92846 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
92847 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
92848 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
92849 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
92850 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
92851 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
92852 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
92853 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
92854 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
92855 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
92856 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
92857 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
92858 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
92859 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
92860 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
92861 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
92862 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
92863 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
92864 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
92865 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
92866 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
92867 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
92868 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
92869 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
92870 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
92871 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
92872 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
92873 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
92874 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
92875 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
92876 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
92877 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
92878 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
92879 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
92880 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
92881 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
92882 a = safe_add(a, olda);
92883 b = safe_add(b, oldb);
92884 c = safe_add(c, oldc);
92885 d = safe_add(d, oldd);
92888 return Array(a, b, c, d);
92891 * These functions implement the four basic operations the algorithm uses.
92895 function md5_cmn(q, a, b, x, s, t) {
92896 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
92899 function md5_ff(a, b, c, d, x, s, t) {
92900 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
92903 function md5_gg(a, b, c, d, x, s, t) {
92904 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
92907 function md5_hh(a, b, c, d, x, s, t) {
92908 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
92911 function md5_ii(a, b, c, d, x, s, t) {
92912 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
92918 * @class Hashes.SHA1
92919 * @param {Object} [config]
92922 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
92923 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
92924 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
92925 * See http://pajhome.org.uk/crypt/md5 for details.
92927 SHA1: function SHA1(options) {
92929 * Private config properties. You may need to tweak these to be compatible with
92930 * the server-side, but the defaults work in most cases.
92931 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
92933 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
92934 // hexadecimal output case format. false - lowercase; true - uppercase
92935 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
92936 // base-64 pad character. Defaults to '=' for strict RFC compliance
92937 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
92940 this.hex = function (s) {
92941 return rstr2hex(rstr(s), hexcase);
92944 this.b64 = function (s) {
92945 return rstr2b64(rstr(s), b64pad);
92948 this.any = function (s, e) {
92949 return rstr2any(rstr(s), e);
92952 this.raw = function (s) {
92956 this.hex_hmac = function (k, d) {
92957 return rstr2hex(rstr_hmac(k, d));
92960 this.b64_hmac = function (k, d) {
92961 return rstr2b64(rstr_hmac(k, d), b64pad);
92964 this.any_hmac = function (k, d, e) {
92965 return rstr2any(rstr_hmac(k, d), e);
92968 * Perform a simple self-test to see if the VM is working
92969 * @return {String} Hexadecimal hash sample
92974 this.vm_test = function () {
92975 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
92978 * @description Enable/disable uppercase hexadecimal returned string
92980 * @return {Object} this
92985 this.setUpperCase = function (a) {
92986 if (typeof a === 'boolean') {
92993 * @description Defines a base64 pad string
92994 * @param {string} Pad
92995 * @return {Object} this
93000 this.setPad = function (a) {
93001 b64pad = a || b64pad;
93005 * @description Defines a base64 pad string
93007 * @return {Object} this
93012 this.setUTF8 = function (a) {
93013 if (typeof a === 'boolean') {
93018 }; // private methods
93021 * Calculate the SHA-512 of a raw string
93026 s = utf8 ? utf8Encode(s) : s;
93027 return binb2rstr(binb(rstr2binb(s), s.length * 8));
93030 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
93034 function rstr_hmac(key, data) {
93035 var bkey, ipad, opad, i, hash;
93036 key = utf8 ? utf8Encode(key) : key;
93037 data = utf8 ? utf8Encode(data) : data;
93038 bkey = rstr2binb(key);
93040 if (bkey.length > 16) {
93041 bkey = binb(bkey, key.length * 8);
93044 ipad = Array(16), opad = Array(16);
93046 for (i = 0; i < 16; i += 1) {
93047 ipad[i] = bkey[i] ^ 0x36363636;
93048 opad[i] = bkey[i] ^ 0x5C5C5C5C;
93051 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
93052 return binb2rstr(binb(opad.concat(hash), 512 + 160));
93055 * Calculate the SHA-1 of an array of big-endian words, and a bit length
93059 function binb(x, len) {
93074 /* append padding */
93076 x[len >> 5] |= 0x80 << 24 - len % 32;
93077 x[(len + 64 >> 9 << 4) + 15] = len;
93079 for (i = 0; i < x.length; i += 16) {
93086 for (j = 0; j < 80; j += 1) {
93090 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
93093 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)));
93096 c = bit_rol(b, 30);
93101 a = safe_add(a, olda);
93102 b = safe_add(b, oldb);
93103 c = safe_add(c, oldc);
93104 d = safe_add(d, oldd);
93105 e = safe_add(e, olde);
93108 return Array(a, b, c, d, e);
93111 * Perform the appropriate triplet combination function for the current
93116 function sha1_ft(t, b, c, d) {
93118 return b & c | ~b & d;
93126 return b & c | b & d | c & d;
93132 * Determine the appropriate additive constant for the current iteration
93136 function sha1_kt(t) {
93137 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
93142 * @class Hashes.SHA256
93145 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
93146 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
93147 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93148 * See http://pajhome.org.uk/crypt/md5 for details.
93149 * Also http://anmar.eu.org/projects/jssha2/
93151 SHA256: function SHA256(options) {
93153 * Private properties configuration variables. You may need to tweak these to be compatible with
93154 * the server-side, but the defaults work in most cases.
93155 * @see this.setUpperCase() method
93156 * @see this.setPad() method
93158 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93159 var // hexadecimal output case format. false - lowercase; true - uppercase */
93160 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
93162 /* base-64 pad character. Default '=' for strict RFC compliance */
93163 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93165 /* enable/disable utf8 encoding */
93167 /* privileged (public) methods */
93169 this.hex = function (s) {
93170 return rstr2hex(rstr(s, utf8));
93173 this.b64 = function (s) {
93174 return rstr2b64(rstr(s, utf8), b64pad);
93177 this.any = function (s, e) {
93178 return rstr2any(rstr(s, utf8), e);
93181 this.raw = function (s) {
93182 return rstr(s, utf8);
93185 this.hex_hmac = function (k, d) {
93186 return rstr2hex(rstr_hmac(k, d));
93189 this.b64_hmac = function (k, d) {
93190 return rstr2b64(rstr_hmac(k, d), b64pad);
93193 this.any_hmac = function (k, d, e) {
93194 return rstr2any(rstr_hmac(k, d), e);
93197 * Perform a simple self-test to see if the VM is working
93198 * @return {String} Hexadecimal hash sample
93203 this.vm_test = function () {
93204 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93207 * Enable/disable uppercase hexadecimal returned string
93209 * @return {Object} this
93214 this.setUpperCase = function (a) {
93219 * @description Defines a base64 pad string
93220 * @param {string} Pad
93221 * @return {Object} this
93226 this.setPad = function (a) {
93227 b64pad = a || b64pad;
93231 * Defines a base64 pad string
93233 * @return {Object} this
93238 this.setUTF8 = function (a) {
93239 if (typeof a === 'boolean') {
93244 }; // private methods
93247 * Calculate the SHA-512 of a raw string
93251 function rstr(s, utf8) {
93252 s = utf8 ? utf8Encode(s) : s;
93253 return binb2rstr(binb(rstr2binb(s), s.length * 8));
93256 * Calculate the HMAC-sha256 of a key and some data (raw strings)
93260 function rstr_hmac(key, data) {
93261 key = utf8 ? utf8Encode(key) : key;
93262 data = utf8 ? utf8Encode(data) : data;
93265 bkey = rstr2binb(key),
93269 if (bkey.length > 16) {
93270 bkey = binb(bkey, key.length * 8);
93273 for (; i < 16; i += 1) {
93274 ipad[i] = bkey[i] ^ 0x36363636;
93275 opad[i] = bkey[i] ^ 0x5C5C5C5C;
93278 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
93279 return binb2rstr(binb(opad.concat(hash), 512 + 256));
93282 * Main sha256 function, with its support functions
93286 function sha256_S(X, n) {
93287 return X >>> n | X << 32 - n;
93290 function sha256_R(X, n) {
93294 function sha256_Ch(x, y, z) {
93295 return x & y ^ ~x & z;
93298 function sha256_Maj(x, y, z) {
93299 return x & y ^ x & z ^ y & z;
93302 function sha256_Sigma0256(x) {
93303 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
93306 function sha256_Sigma1256(x) {
93307 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
93310 function sha256_Gamma0256(x) {
93311 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
93314 function sha256_Gamma1256(x) {
93315 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
93318 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];
93320 function binb(m, l) {
93321 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
93322 var W = new Array(64);
93323 var a, b, c, d, e, f, g, h;
93325 /* append padding */
93327 m[l >> 5] |= 0x80 << 24 - l % 32;
93328 m[(l + 64 >> 9 << 4) + 15] = l;
93330 for (i = 0; i < m.length; i += 16) {
93340 for (j = 0; j < 64; j += 1) {
93344 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
93347 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
93348 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
93352 e = safe_add(d, T1);
93356 a = safe_add(T1, T2);
93359 HASH[0] = safe_add(a, HASH[0]);
93360 HASH[1] = safe_add(b, HASH[1]);
93361 HASH[2] = safe_add(c, HASH[2]);
93362 HASH[3] = safe_add(d, HASH[3]);
93363 HASH[4] = safe_add(e, HASH[4]);
93364 HASH[5] = safe_add(f, HASH[5]);
93365 HASH[6] = safe_add(g, HASH[6]);
93366 HASH[7] = safe_add(h, HASH[7]);
93374 * @class Hashes.SHA512
93377 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
93378 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
93379 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93380 * See http://pajhome.org.uk/crypt/md5 for details.
93382 SHA512: function SHA512(options) {
93384 * Private properties configuration variables. You may need to tweak these to be compatible with
93385 * the server-side, but the defaults work in most cases.
93386 * @see this.setUpperCase() method
93387 * @see this.setPad() method
93389 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93391 var /* hexadecimal output case format. false - lowercase; true - uppercase */
93392 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
93394 /* base-64 pad character. Default '=' for strict RFC compliance */
93395 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93397 /* enable/disable utf8 encoding */
93399 /* privileged (public) methods */
93401 this.hex = function (s) {
93402 return rstr2hex(rstr(s));
93405 this.b64 = function (s) {
93406 return rstr2b64(rstr(s), b64pad);
93409 this.any = function (s, e) {
93410 return rstr2any(rstr(s), e);
93413 this.raw = function (s) {
93417 this.hex_hmac = function (k, d) {
93418 return rstr2hex(rstr_hmac(k, d));
93421 this.b64_hmac = function (k, d) {
93422 return rstr2b64(rstr_hmac(k, d), b64pad);
93425 this.any_hmac = function (k, d, e) {
93426 return rstr2any(rstr_hmac(k, d), e);
93429 * Perform a simple self-test to see if the VM is working
93430 * @return {String} Hexadecimal hash sample
93435 this.vm_test = function () {
93436 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93439 * @description Enable/disable uppercase hexadecimal returned string
93441 * @return {Object} this
93446 this.setUpperCase = function (a) {
93451 * @description Defines a base64 pad string
93452 * @param {string} Pad
93453 * @return {Object} this
93458 this.setPad = function (a) {
93459 b64pad = a || b64pad;
93463 * @description Defines a base64 pad string
93465 * @return {Object} this
93470 this.setUTF8 = function (a) {
93471 if (typeof a === 'boolean') {
93477 /* private methods */
93480 * Calculate the SHA-512 of a raw string
93485 s = utf8 ? utf8Encode(s) : s;
93486 return binb2rstr(binb(rstr2binb(s), s.length * 8));
93489 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
93493 function rstr_hmac(key, data) {
93494 key = utf8 ? utf8Encode(key) : key;
93495 data = utf8 ? utf8Encode(data) : data;
93498 bkey = rstr2binb(key),
93502 if (bkey.length > 32) {
93503 bkey = binb(bkey, key.length * 8);
93506 for (; i < 32; i += 1) {
93507 ipad[i] = bkey[i] ^ 0x36363636;
93508 opad[i] = bkey[i] ^ 0x5C5C5C5C;
93511 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
93512 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
93515 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
93519 function binb(x, len) {
93524 hash = new Array(16),
93525 //Initial hash values
93526 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)],
93527 T1 = new int64(0, 0),
93528 T2 = new int64(0, 0),
93529 a = new int64(0, 0),
93530 b = new int64(0, 0),
93531 c = new int64(0, 0),
93532 d = new int64(0, 0),
93533 e = new int64(0, 0),
93534 f = new int64(0, 0),
93535 g = new int64(0, 0),
93536 h = new int64(0, 0),
93537 //Temporary variables not specified by the document
93538 s0 = new int64(0, 0),
93539 s1 = new int64(0, 0),
93540 Ch = new int64(0, 0),
93541 Maj = new int64(0, 0),
93542 r1 = new int64(0, 0),
93543 r2 = new int64(0, 0),
93544 r3 = new int64(0, 0);
93546 if (sha512_k === undefined) {
93548 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)];
93551 for (i = 0; i < 80; i += 1) {
93552 W[i] = new int64(0, 0);
93553 } // append padding to the source string. The format is described in the FIPS.
93556 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
93557 x[(len + 128 >> 10 << 5) + 31] = len;
93560 for (i = 0; i < l; i += 32) {
93561 //32 dwords is the block size
93562 int64copy(a, H[0]);
93563 int64copy(b, H[1]);
93564 int64copy(c, H[2]);
93565 int64copy(d, H[3]);
93566 int64copy(e, H[4]);
93567 int64copy(f, H[5]);
93568 int64copy(g, H[6]);
93569 int64copy(h, H[7]);
93571 for (j = 0; j < 16; j += 1) {
93572 W[j].h = x[i + 2 * j];
93573 W[j].l = x[i + 2 * j + 1];
93576 for (j = 16; j < 80; j += 1) {
93578 int64rrot(r1, W[j - 2], 19);
93579 int64revrrot(r2, W[j - 2], 29);
93580 int64shr(r3, W[j - 2], 6);
93581 s1.l = r1.l ^ r2.l ^ r3.l;
93582 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
93584 int64rrot(r1, W[j - 15], 1);
93585 int64rrot(r2, W[j - 15], 8);
93586 int64shr(r3, W[j - 15], 7);
93587 s0.l = r1.l ^ r2.l ^ r3.l;
93588 s0.h = r1.h ^ r2.h ^ r3.h;
93589 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
93592 for (j = 0; j < 80; j += 1) {
93594 Ch.l = e.l & f.l ^ ~e.l & g.l;
93595 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
93597 int64rrot(r1, e, 14);
93598 int64rrot(r2, e, 18);
93599 int64revrrot(r3, e, 9);
93600 s1.l = r1.l ^ r2.l ^ r3.l;
93601 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
93603 int64rrot(r1, a, 28);
93604 int64revrrot(r2, a, 2);
93605 int64revrrot(r3, a, 7);
93606 s0.l = r1.l ^ r2.l ^ r3.l;
93607 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
93609 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
93610 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
93611 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
93612 int64add(T2, s0, Maj);
93616 int64add(e, d, T1);
93620 int64add(a, T1, T2);
93623 int64add(H[0], H[0], a);
93624 int64add(H[1], H[1], b);
93625 int64add(H[2], H[2], c);
93626 int64add(H[3], H[3], d);
93627 int64add(H[4], H[4], e);
93628 int64add(H[5], H[5], f);
93629 int64add(H[6], H[6], g);
93630 int64add(H[7], H[7], h);
93631 } //represent the hash as an array of 32-bit dwords
93634 for (i = 0; i < 8; i += 1) {
93635 hash[2 * i] = H[i].h;
93636 hash[2 * i + 1] = H[i].l;
93640 } //A constructor for 64-bit numbers
93643 function int64(h, l) {
93645 this.l = l; //this.toString = int64toString;
93646 } //Copies src into dst, assuming both are 64-bit numbers
93649 function int64copy(dst, src) {
93652 } //Right-rotates a 64-bit number by shift
93653 //Won't handle cases of shift>=32
93654 //The function revrrot() is for that
93657 function int64rrot(dst, x, shift) {
93658 dst.l = x.l >>> shift | x.h << 32 - shift;
93659 dst.h = x.h >>> shift | x.l << 32 - shift;
93660 } //Reverses the dwords of the source and then rotates right by shift.
93661 //This is equivalent to rotation by 32+shift
93664 function int64revrrot(dst, x, shift) {
93665 dst.l = x.h >>> shift | x.l << 32 - shift;
93666 dst.h = x.l >>> shift | x.h << 32 - shift;
93667 } //Bitwise-shifts right a 64-bit number by shift
93668 //Won't handle shift>=32, but it's never needed in SHA512
93671 function int64shr(dst, x, shift) {
93672 dst.l = x.l >>> shift | x.h << 32 - shift;
93673 dst.h = x.h >>> shift;
93674 } //Adds two 64-bit numbers
93675 //Like the original implementation, does not rely on 32-bit operations
93678 function int64add(dst, x, y) {
93679 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
93680 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
93681 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
93682 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
93683 dst.l = w0 & 0xffff | w1 << 16;
93684 dst.h = w2 & 0xffff | w3 << 16;
93685 } //Same, except with 4 addends. Works faster than adding them one by one.
93688 function int64add4(dst, a, b, c, d) {
93689 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
93690 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
93691 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
93692 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
93693 dst.l = w0 & 0xffff | w1 << 16;
93694 dst.h = w2 & 0xffff | w3 << 16;
93695 } //Same, except with 5 addends
93698 function int64add5(dst, a, b, c, d, e) {
93699 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
93700 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
93701 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
93702 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
93703 dst.l = w0 & 0xffff | w1 << 16;
93704 dst.h = w2 & 0xffff | w3 << 16;
93709 * @class Hashes.RMD160
93711 * @param {Object} [config]
93713 * A JavaScript implementation of the RIPEMD-160 Algorithm
93714 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
93715 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
93716 * See http://pajhome.org.uk/crypt/md5 for details.
93717 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
93719 RMD160: function RMD160(options) {
93721 * Private properties configuration variables. You may need to tweak these to be compatible with
93722 * the server-side, but the defaults work in most cases.
93723 * @see this.setUpperCase() method
93724 * @see this.setPad() method
93726 options && typeof options.uppercase === 'boolean' ? options.uppercase : false;
93728 var /* hexadecimal output case format. false - lowercase; true - uppercase */
93729 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
93731 /* base-64 pad character. Default '=' for strict RFC compliance */
93732 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
93734 /* enable/disable utf8 encoding */
93735 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],
93736 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],
93737 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],
93738 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];
93739 /* privileged (public) methods */
93741 this.hex = function (s) {
93742 return rstr2hex(rstr(s));
93745 this.b64 = function (s) {
93746 return rstr2b64(rstr(s), b64pad);
93749 this.any = function (s, e) {
93750 return rstr2any(rstr(s), e);
93753 this.raw = function (s) {
93757 this.hex_hmac = function (k, d) {
93758 return rstr2hex(rstr_hmac(k, d));
93761 this.b64_hmac = function (k, d) {
93762 return rstr2b64(rstr_hmac(k, d), b64pad);
93765 this.any_hmac = function (k, d, e) {
93766 return rstr2any(rstr_hmac(k, d), e);
93769 * Perform a simple self-test to see if the VM is working
93770 * @return {String} Hexadecimal hash sample
93775 this.vm_test = function () {
93776 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
93779 * @description Enable/disable uppercase hexadecimal returned string
93781 * @return {Object} this
93786 this.setUpperCase = function (a) {
93791 * @description Defines a base64 pad string
93792 * @param {string} Pad
93793 * @return {Object} this
93798 this.setPad = function (a) {
93799 if (typeof a !== 'undefined') {
93806 * @description Defines a base64 pad string
93808 * @return {Object} this
93813 this.setUTF8 = function (a) {
93814 if (typeof a === 'boolean') {
93820 /* private methods */
93823 * Calculate the rmd160 of a raw string
93828 s = utf8 ? utf8Encode(s) : s;
93829 return binl2rstr(binl(rstr2binl(s), s.length * 8));
93832 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
93836 function rstr_hmac(key, data) {
93837 key = utf8 ? utf8Encode(key) : key;
93838 data = utf8 ? utf8Encode(data) : data;
93841 bkey = rstr2binl(key),
93845 if (bkey.length > 16) {
93846 bkey = binl(bkey, key.length * 8);
93849 for (i = 0; i < 16; i += 1) {
93850 ipad[i] = bkey[i] ^ 0x36363636;
93851 opad[i] = bkey[i] ^ 0x5C5C5C5C;
93854 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
93855 return binl2rstr(binl(opad.concat(hash), 512 + 160));
93858 * Convert an array of little-endian words to a string
93862 function binl2rstr(input) {
93865 l = input.length * 32;
93867 for (i = 0; i < l; i += 8) {
93868 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
93874 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
93878 function binl(x, len) {
93898 /* append padding */
93900 x[len >> 5] |= 0x80 << len % 32;
93901 x[(len + 64 >>> 9 << 4) + 14] = len;
93904 for (i = 0; i < l; i += 16) {
93911 for (j = 0; j <= 79; j += 1) {
93912 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
93913 T = safe_add(T, x[i + rmd160_r1[j]]);
93914 T = safe_add(T, rmd160_K1(j));
93915 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
93918 D1 = bit_rol(C1, 10);
93921 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
93922 T = safe_add(T, x[i + rmd160_r2[j]]);
93923 T = safe_add(T, rmd160_K2(j));
93924 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
93927 D2 = bit_rol(C2, 10);
93932 T = safe_add(h1, safe_add(C1, D2));
93933 h1 = safe_add(h2, safe_add(D1, E2));
93934 h2 = safe_add(h3, safe_add(E1, A2));
93935 h3 = safe_add(h4, safe_add(A1, B2));
93936 h4 = safe_add(h0, safe_add(B1, C2));
93940 return [h0, h1, h2, h3, h4];
93941 } // specific algorithm methods
93944 function rmd160_f(j, x, y, z) {
93945 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';
93948 function rmd160_K1(j) {
93949 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';
93952 function rmd160_K2(j) {
93953 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';
93956 }; // exposes Hashes
93958 (function (window, undefined$1) {
93959 var freeExports = false;
93962 freeExports = exports;
93964 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
93965 window = commonjsGlobal;
93969 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
93970 // define as an anonymous module, so, through path mapping, it can be aliased
93971 undefined$1(function () {
93974 } else if (freeExports) {
93975 // in Node.js or RingoJS v0.8.0+
93976 if (module && module.exports === freeExports) {
93977 module.exports = Hashes;
93978 } // in Narwhal or RingoJS v0.7.0-
93980 freeExports.Hashes = Hashes;
93983 // in a browser or Rhino
93984 window.Hashes = Hashes;
93991 var sha1 = new hashes.SHA1(); // # xtend
93993 var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
93998 for (var i = 0; i < arguments.length; i++) {
93999 var source = arguments[i];
94001 for (var key in source) {
94002 if (hasOwnProperty$1.call(source, key)) {
94003 target[key] = source[key];
94013 ohauth.qsString = function (obj) {
94014 return Object.keys(obj).sort().map(function (key) {
94015 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
94019 ohauth.stringQs = function (str) {
94020 return str.split('&').filter(function (pair) {
94021 return pair !== '';
94022 }).reduce(function (obj, pair) {
94023 var parts = pair.split('=');
94024 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
94029 ohauth.rawxhr = function (method, url, data, headers, callback) {
94030 var xhr = new XMLHttpRequest(),
94031 twoHundred = /^20\d$/;
94033 xhr.onreadystatechange = function () {
94034 if (4 === xhr.readyState && 0 !== xhr.status) {
94035 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
94039 xhr.onerror = function (e) {
94040 return callback(e, null);
94043 xhr.open(method, url, true);
94045 for (var h in headers) {
94046 xhr.setRequestHeader(h, headers[h]);
94053 ohauth.xhr = function (method, url, auth, data, options, callback) {
94054 var headers = options && options.header || {
94055 'Content-Type': 'application/x-www-form-urlencoded'
94057 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
94058 return ohauth.rawxhr(method, url, data, headers, callback);
94061 ohauth.nonce = function () {
94062 for (var o = ''; o.length < 6;) {
94063 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
94069 ohauth.authHeader = function (obj) {
94070 return Object.keys(obj).sort().map(function (key) {
94071 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
94075 ohauth.timestamp = function () {
94076 return ~~(+new Date() / 1000);
94079 ohauth.percentEncode = function (s) {
94080 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
94083 ohauth.baseString = function (method, url, params) {
94084 if (params.oauth_signature) delete params.oauth_signature;
94085 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
94088 ohauth.signature = function (oauth_secret, token_secret, baseString) {
94089 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
94092 * Takes an options object for configuration (consumer_key,
94093 * consumer_secret, version, signature_method, token, token_secret)
94094 * and returns a function that generates the Authorization header
94097 * The returned function takes these parameters:
94098 * - method: GET/POST/...
94099 * - uri: full URI with protocol, port, path and query string
94100 * - extra_params: any extra parameters (that are passed in the POST data),
94101 * can be an object or a from-urlencoded string.
94103 * Returned function returns full OAuth header with "OAuth" string in it.
94107 ohauth.headerGenerator = function (options) {
94108 options = options || {};
94109 var consumer_key = options.consumer_key || '',
94110 consumer_secret = options.consumer_secret || '',
94111 signature_method = options.signature_method || 'HMAC-SHA1',
94112 version = options.version || '1.0',
94113 token = options.token || '',
94114 token_secret = options.token_secret || '';
94115 return function (method, uri, extra_params) {
94116 method = method.toUpperCase();
94118 if (typeof extra_params === 'string' && extra_params.length > 0) {
94119 extra_params = ohauth.stringQs(extra_params);
94122 var uri_parts = uri.split('?', 2),
94123 base_uri = uri_parts[0];
94124 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
94125 var oauth_params = {
94126 oauth_consumer_key: consumer_key,
94127 oauth_signature_method: signature_method,
94128 oauth_version: version,
94129 oauth_timestamp: ohauth.timestamp(),
94130 oauth_nonce: ohauth.nonce()
94132 if (token) oauth_params.oauth_token = token;
94133 var all_params = xtend({}, oauth_params, query_params, extra_params),
94134 base_str = ohauth.baseString(method, base_uri, all_params);
94135 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
94136 return 'OAuth ' + ohauth.authHeader(oauth_params);
94140 var ohauth_1 = ohauth;
94142 var resolveUrl = createCommonjsModule(function (module, exports) {
94143 // Copyright 2014 Simon Lydell
94144 // X11 (“MIT”) Licensed. (See LICENSE.)
94145 void function (root, factory) {
94147 module.exports = factory();
94149 }(commonjsGlobal, function () {
94150 function resolveUrl()
94153 var numUrls = arguments.length;
94155 if (numUrls === 0) {
94156 throw new Error("resolveUrl requires at least one argument; got none.");
94159 var base = document.createElement("base");
94160 base.href = arguments[0];
94162 if (numUrls === 1) {
94166 var head = document.getElementsByTagName("head")[0];
94167 head.insertBefore(base, head.firstChild);
94168 var a = document.createElement("a");
94171 for (var index = 1; index < numUrls; index++) {
94172 a.href = arguments[index];
94174 base.href = resolved;
94177 head.removeChild(base);
94185 var assign = make_assign();
94186 var create$1 = make_create();
94187 var trim$1 = make_trim();
94188 var Global$5 = typeof window !== 'undefined' ? window : commonjsGlobal;
94199 isFunction: isFunction$1,
94200 isObject: isObject$1,
94204 function make_assign() {
94205 if (Object.assign) {
94206 return Object.assign;
94208 return function shimAssign(obj, props1, props2, etc) {
94209 for (var i = 1; i < arguments.length; i++) {
94210 each$7(Object(arguments[i]), function (val, key) {
94220 function make_create() {
94221 if (Object.create) {
94222 return function create(obj, assignProps1, assignProps2, etc) {
94223 var assignArgsList = slice$1(arguments, 1);
94224 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
94227 var F = function F() {}; // eslint-disable-line no-inner-declarations
94230 return function create(obj, assignProps1, assignProps2, etc) {
94231 var assignArgsList = slice$1(arguments, 1);
94233 return assign.apply(this, [new F()].concat(assignArgsList));
94238 function make_trim() {
94239 if (String.prototype.trim) {
94240 return function trim(str) {
94241 return String.prototype.trim.call(str);
94244 return function trim(str) {
94245 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
94250 function bind$1(obj, fn) {
94251 return function () {
94252 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
94256 function slice$1(arr, index) {
94257 return Array.prototype.slice.call(arr, index || 0);
94260 function each$7(obj, fn) {
94261 pluck$1(obj, function (val, key) {
94267 function map(obj, fn) {
94268 var res = isList$1(obj) ? [] : {};
94269 pluck$1(obj, function (v, k) {
94276 function pluck$1(obj, fn) {
94277 if (isList$1(obj)) {
94278 for (var i = 0; i < obj.length; i++) {
94279 if (fn(obj[i], i)) {
94284 for (var key in obj) {
94285 if (obj.hasOwnProperty(key)) {
94286 if (fn(obj[key], key)) {
94294 function isList$1(val) {
94295 return val != null && typeof val != 'function' && typeof val.length == 'number';
94298 function isFunction$1(val) {
94299 return val && {}.toString.call(val) === '[object Function]';
94302 function isObject$1(val) {
94303 return val && {}.toString.call(val) === '[object Object]';
94306 var slice = util.slice;
94307 var pluck = util.pluck;
94308 var each$6 = util.each;
94309 var bind = util.bind;
94310 var create = util.create;
94311 var isList = util.isList;
94312 var isFunction = util.isFunction;
94313 var isObject = util.isObject;
94314 var storeEngine = {
94315 createStore: _createStore
94320 // get returns the value of the given key. If that value
94321 // is undefined, it returns optionalDefaultValue instead.
94322 get: function get(key, optionalDefaultValue) {
94323 var data = this.storage.read(this._namespacePrefix + key);
94324 return this._deserialize(data, optionalDefaultValue);
94326 // set will store the given value at key and returns value.
94327 // Calling set with value === undefined is equivalent to calling remove.
94328 set: function set(key, value) {
94329 if (value === undefined) {
94330 return this.remove(key);
94333 this.storage.write(this._namespacePrefix + key, this._serialize(value));
94336 // remove deletes the key and value stored at the given key.
94337 remove: function remove(key) {
94338 this.storage.remove(this._namespacePrefix + key);
94340 // each will call the given callback once for each key-value pair
94342 each: function each(callback) {
94344 this.storage.each(function (val, namespacedKey) {
94345 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
94348 // clearAll will remove all the stored key-value pairs in this store.
94349 clearAll: function clearAll() {
94350 this.storage.clearAll();
94352 // additional functionality that can't live in plugins
94353 // ---------------------------------------------------
94354 // hasNamespace returns true if this store instance has the given namespace.
94355 hasNamespace: function hasNamespace(namespace) {
94356 return this._namespacePrefix == '__storejs_' + namespace + '_';
94358 // createStore creates a store.js instance with the first
94359 // functioning storage in the list of storage candidates,
94360 // and applies the the given mixins to the instance.
94361 createStore: function createStore() {
94362 return _createStore.apply(this, arguments);
94364 addPlugin: function addPlugin(plugin) {
94365 this._addPlugin(plugin);
94367 namespace: function namespace(_namespace) {
94368 return _createStore(this.storage, this.plugins, _namespace);
94373 var _console = typeof console == 'undefined' ? null : console;
94379 var fn = _console.warn ? _console.warn : _console.log;
94380 fn.apply(_console, arguments);
94383 function _createStore(storages, plugins, namespace) {
94388 if (storages && !isList(storages)) {
94389 storages = [storages];
94392 if (plugins && !isList(plugins)) {
94393 plugins = [plugins];
94396 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
94397 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
94398 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
94400 if (!legalNamespaces.test(namespace)) {
94401 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
94404 var _privateStoreProps = {
94405 _namespacePrefix: namespacePrefix,
94406 _namespaceRegexp: namespaceRegexp,
94407 _testStorage: function _testStorage(storage) {
94409 var testStr = '__storejs__test__';
94410 storage.write(testStr, testStr);
94411 var ok = storage.read(testStr) === testStr;
94412 storage.remove(testStr);
94418 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
94419 var oldFn = this[propName];
94421 this[propName] = function pluginFn() {
94422 var args = slice(arguments, 0);
94423 var self = this; // super_fn calls the old function which was overwritten by
94426 function super_fn() {
94431 each$6(arguments, function (arg, i) {
94434 return oldFn.apply(self, args);
94435 } // Give mixing function access to super_fn by prefixing all mixin function
94436 // arguments with super_fn.
94439 var newFnArgs = [super_fn].concat(args);
94440 return pluginFnProp.apply(self, newFnArgs);
94443 _serialize: function _serialize(obj) {
94444 return JSON.stringify(obj);
94446 _deserialize: function _deserialize(strVal, defaultVal) {
94449 } // It is possible that a raw string value has been previously stored
94450 // in a storage without using store.js, meaning it will be a raw
94451 // string value instead of a JSON serialized string. By defaulting
94452 // to the raw string value in case of a JSON parse error, we allow
94453 // for past stored values to be forwards-compatible with store.js
94459 val = JSON.parse(strVal);
94464 return val !== undefined ? val : defaultVal;
94466 _addStorage: function _addStorage(storage) {
94467 if (this.enabled) {
94471 if (this._testStorage(storage)) {
94472 this.storage = storage;
94473 this.enabled = true;
94476 _addPlugin: function _addPlugin(plugin) {
94477 var self = this; // If the plugin is an array, then add all plugins in the array.
94478 // This allows for a plugin to depend on other plugins.
94480 if (isList(plugin)) {
94481 each$6(plugin, function (plugin) {
94482 self._addPlugin(plugin);
94485 } // Keep track of all plugins we've seen so far, so that we
94486 // don't add any of them twice.
94489 var seenPlugin = pluck(this.plugins, function (seenPlugin) {
94490 return plugin === seenPlugin;
94497 this.plugins.push(plugin); // Check that the plugin is properly formed
94499 if (!isFunction(plugin)) {
94500 throw new Error('Plugins must be function values that return objects');
94503 var pluginProperties = plugin.call(this);
94505 if (!isObject(pluginProperties)) {
94506 throw new Error('Plugins must return an object of function properties');
94507 } // Add the plugin function properties to this store instance.
94510 each$6(pluginProperties, function (pluginFnProp, propName) {
94511 if (!isFunction(pluginFnProp)) {
94512 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
94515 self._assignPluginFnProp(pluginFnProp, propName);
94518 // Put deprecated properties in the private API, so as to not expose it to accidential
94519 // discovery through inspection of the store object.
94520 // Deprecated: addStorage
94521 addStorage: function addStorage(storage) {
94522 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
94524 this._addStorage(storage);
94527 var store = create(_privateStoreProps, storeAPI, {
94531 each$6(store, function (prop, propName) {
94532 if (isFunction(prop)) {
94533 store.raw[propName] = bind(store, prop);
94536 each$6(storages, function (storage) {
94537 store._addStorage(storage);
94539 each$6(plugins, function (plugin) {
94540 store._addPlugin(plugin);
94545 var Global$4 = util.Global;
94546 var localStorage_1 = {
94547 name: 'localStorage',
94552 clearAll: clearAll$5
94555 function localStorage$1() {
94556 return Global$4.localStorage;
94559 function read$5(key) {
94560 return localStorage$1().getItem(key);
94563 function write$5(key, data) {
94564 return localStorage$1().setItem(key, data);
94567 function each$5(fn) {
94568 for (var i = localStorage$1().length - 1; i >= 0; i--) {
94569 var key = localStorage$1().key(i);
94570 fn(read$5(key), key);
94574 function remove$5(key) {
94575 return localStorage$1().removeItem(key);
94578 function clearAll$5() {
94579 return localStorage$1().clear();
94582 // versions 6 and 7, where no localStorage, etc
94585 var Global$3 = util.Global;
94586 var oldFFGlobalStorage = {
94587 name: 'oldFF-globalStorage',
94592 clearAll: clearAll$4
94594 var globalStorage = Global$3.globalStorage;
94596 function read$4(key) {
94597 return globalStorage[key];
94600 function write$4(key, data) {
94601 globalStorage[key] = data;
94604 function each$4(fn) {
94605 for (var i = globalStorage.length - 1; i >= 0; i--) {
94606 var key = globalStorage.key(i);
94607 fn(globalStorage[key], key);
94611 function remove$4(key) {
94612 return globalStorage.removeItem(key);
94615 function clearAll$4() {
94616 each$4(function (key, _) {
94617 delete globalStorage[key];
94621 // versions 6 and 7, where no localStorage, sessionStorage, etc
94624 var Global$2 = util.Global;
94625 var oldIEUserDataStorage = {
94626 name: 'oldIE-userDataStorage',
94631 clearAll: clearAll$3
94633 var storageName = 'storejs';
94634 var doc$1 = Global$2.document;
94636 var _withStorageEl = _makeIEStorageElFunction();
94638 var disable = (Global$2.navigator ? Global$2.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
94640 function write$3(unfixedKey, data) {
94645 var fixedKey = fixKey(unfixedKey);
94647 _withStorageEl(function (storageEl) {
94648 storageEl.setAttribute(fixedKey, data);
94649 storageEl.save(storageName);
94653 function read$3(unfixedKey) {
94658 var fixedKey = fixKey(unfixedKey);
94661 _withStorageEl(function (storageEl) {
94662 res = storageEl.getAttribute(fixedKey);
94668 function each$3(callback) {
94669 _withStorageEl(function (storageEl) {
94670 var attributes = storageEl.XMLDocument.documentElement.attributes;
94672 for (var i = attributes.length - 1; i >= 0; i--) {
94673 var attr = attributes[i];
94674 callback(storageEl.getAttribute(attr.name), attr.name);
94679 function remove$3(unfixedKey) {
94680 var fixedKey = fixKey(unfixedKey);
94682 _withStorageEl(function (storageEl) {
94683 storageEl.removeAttribute(fixedKey);
94684 storageEl.save(storageName);
94688 function clearAll$3() {
94689 _withStorageEl(function (storageEl) {
94690 var attributes = storageEl.XMLDocument.documentElement.attributes;
94691 storageEl.load(storageName);
94693 for (var i = attributes.length - 1; i >= 0; i--) {
94694 storageEl.removeAttribute(attributes[i].name);
94697 storageEl.save(storageName);
94701 // In IE7, keys cannot start with a digit or contain certain chars.
94702 // See https://github.com/marcuswestin/store.js/issues/40
94703 // See https://github.com/marcuswestin/store.js/issues/83
94706 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
94708 function fixKey(key) {
94709 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
94712 function _makeIEStorageElFunction() {
94713 if (!doc$1 || !doc$1.documentElement || !doc$1.documentElement.addBehavior) {
94717 var scriptTag = 'script',
94720 storageEl; // Since #userData storage applies only to specific paths, we need to
94721 // somehow link our data to a specific path. We choose /favicon.ico
94722 // as a pretty safe option, since all browsers already make a request to
94723 // this URL anyway and being a 404 will not hurt us here. We wrap an
94724 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
94725 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
94726 // since the iframe access rules appear to allow direct access and
94727 // manipulation of the document element, even for a 404 page. This
94728 // document can be used instead of the current document (which would
94729 // have been limited to the current path) to perform #userData storage.
94732 /* global ActiveXObject */
94733 storageContainer = new ActiveXObject('htmlfile');
94734 storageContainer.open();
94735 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
94736 storageContainer.close();
94737 storageOwner = storageContainer.w.frames[0].document;
94738 storageEl = storageOwner.createElement('div');
94740 // somehow ActiveXObject instantiation failed (perhaps some special
94741 // security settings or otherwse), fall back to per-path storage
94742 storageEl = doc$1.createElement('div');
94743 storageOwner = doc$1.body;
94746 return function (storeFunction) {
94747 var args = [].slice.call(arguments, 0);
94748 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
94749 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
94751 storageOwner.appendChild(storageEl);
94752 storageEl.addBehavior('#default#userData');
94753 storageEl.load(storageName);
94754 storeFunction.apply(this, args);
94755 storageOwner.removeChild(storageEl);
94760 // doesn't work but cookies do. This implementation is adopted from
94761 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
94763 var Global$1 = util.Global;
94764 var trim = util.trim;
94765 var cookieStorage = {
94766 name: 'cookieStorage',
94771 clearAll: clearAll$2
94773 var doc = Global$1.document;
94775 function read$2(key) {
94776 if (!key || !_has(key)) {
94780 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
94781 return unescape(doc.cookie.replace(new RegExp(regexpStr), "$1"));
94784 function each$2(callback) {
94785 var cookies = doc.cookie.split(/; ?/g);
94787 for (var i = cookies.length - 1; i >= 0; i--) {
94788 if (!trim(cookies[i])) {
94792 var kvp = cookies[i].split('=');
94793 var key = unescape(kvp[0]);
94794 var val = unescape(kvp[1]);
94795 callback(val, key);
94799 function write$2(key, data) {
94804 doc.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
94807 function remove$2(key) {
94808 if (!key || !_has(key)) {
94812 doc.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
94815 function clearAll$2() {
94816 each$2(function (_, key) {
94821 function _has(key) {
94822 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc.cookie);
94825 var Global = util.Global;
94826 var sessionStorage_1 = {
94827 name: 'sessionStorage',
94832 clearAll: clearAll$1
94835 function sessionStorage() {
94836 return Global.sessionStorage;
94839 function read$1(key) {
94840 return sessionStorage().getItem(key);
94843 function write$1(key, data) {
94844 return sessionStorage().setItem(key, data);
94847 function each$1(fn) {
94848 for (var i = sessionStorage().length - 1; i >= 0; i--) {
94849 var key = sessionStorage().key(i);
94850 fn(read$1(key), key);
94854 function remove$1(key) {
94855 return sessionStorage().removeItem(key);
94858 function clearAll$1() {
94859 return sessionStorage().clear();
94862 // memoryStorage is a useful last fallback to ensure that the store
94863 // is functions (meaning store.get(), store.set(), etc will all function).
94864 // However, stored values will not persist when the browser navigates to
94865 // a new page or reloads the current page.
94866 var memoryStorage_1 = {
94867 name: 'memoryStorage',
94874 var memoryStorage = {};
94876 function read(key) {
94877 return memoryStorage[key];
94880 function write(key, data) {
94881 memoryStorage[key] = data;
94884 function each(callback) {
94885 for (var key in memoryStorage) {
94886 if (memoryStorage.hasOwnProperty(key)) {
94887 callback(memoryStorage[key], key);
94892 function remove(key) {
94893 delete memoryStorage[key];
94896 function clearAll(key) {
94897 memoryStorage = {};
94900 var all = [// Listed in order of usage preference
94901 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
94903 /* eslint-disable */
94907 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
94908 // See http://www.JSON.org/js.html
94909 // This code should be minified before deployment.
94910 // See http://javascript.crockford.com/jsmin.html
94911 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
94913 // This file creates a global JSON object containing two methods: stringify
94914 // and parse. This file provides the ES5 JSON capability to ES3 systems.
94915 // If a project might run on IE8 or earlier, then this file should be included.
94916 // This file does nothing on ES5 systems.
94917 // JSON.stringify(value, replacer, space)
94918 // value any JavaScript value, usually an object or array.
94919 // replacer an optional parameter that determines how object
94920 // values are stringified for objects. It can be a
94921 // function or an array of strings.
94922 // space an optional parameter that specifies the indentation
94923 // of nested structures. If it is omitted, the text will
94924 // be packed without extra whitespace. If it is a number,
94925 // it will specify the number of spaces to indent at each
94926 // level. If it is a string (such as "\t" or " "),
94927 // it contains the characters used to indent at each level.
94928 // This method produces a JSON text from a JavaScript value.
94929 // When an object value is found, if the object contains a toJSON
94930 // method, its toJSON method will be called and the result will be
94931 // stringified. A toJSON method does not serialize: it returns the
94932 // value represented by the name/value pair that should be serialized,
94933 // or undefined if nothing should be serialized. The toJSON method
94934 // will be passed the key associated with the value, and this will be
94935 // bound to the value.
94936 // For example, this would serialize Dates as ISO strings.
94937 // Date.prototype.toJSON = function (key) {
94939 // // Format integers to have at least two digits.
94944 // return this.getUTCFullYear() + "-" +
94945 // f(this.getUTCMonth() + 1) + "-" +
94946 // f(this.getUTCDate()) + "T" +
94947 // f(this.getUTCHours()) + ":" +
94948 // f(this.getUTCMinutes()) + ":" +
94949 // f(this.getUTCSeconds()) + "Z";
94951 // You can provide an optional replacer method. It will be passed the
94952 // key and value of each member, with this bound to the containing
94953 // object. The value that is returned from your method will be
94954 // serialized. If your method returns undefined, then the member will
94955 // be excluded from the serialization.
94956 // If the replacer parameter is an array of strings, then it will be
94957 // used to select the members to be serialized. It filters the results
94958 // such that only members with keys listed in the replacer array are
94960 // Values that do not have JSON representations, such as undefined or
94961 // functions, will not be serialized. Such values in objects will be
94962 // dropped; in arrays they will be replaced with null. You can use
94963 // a replacer function to replace those with JSON values.
94964 // JSON.stringify(undefined) returns undefined.
94965 // The optional space parameter produces a stringification of the
94966 // value that is filled with line breaks and indentation to make it
94968 // If the space parameter is a non-empty string, then that string will
94969 // be used for indentation. If the space parameter is a number, then
94970 // the indentation will be that many spaces.
94972 // text = JSON.stringify(["e", {pluribus: "unum"}]);
94973 // // text is '["e",{"pluribus":"unum"}]'
94974 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
94975 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
94976 // text = JSON.stringify([new Date()], function (key, value) {
94977 // return this[key] instanceof Date
94978 // ? "Date(" + this[key] + ")"
94981 // // text is '["Date(---current time---)"]'
94982 // JSON.parse(text, reviver)
94983 // This method parses a JSON text to produce an object or array.
94984 // It can throw a SyntaxError exception.
94985 // The optional reviver parameter is a function that can filter and
94986 // transform the results. It receives each of the keys and values,
94987 // and its return value is used instead of the original value.
94988 // If it returns what it received, then the structure is not modified.
94989 // If it returns undefined then the member is deleted.
94991 // // Parse the text. Values that look like ISO date strings will
94992 // // be converted to Date objects.
94993 // myData = JSON.parse(text, function (key, value) {
94995 // if (typeof value === "string") {
94997 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
94999 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
95005 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
95007 // if (typeof value === "string" &&
95008 // value.slice(0, 5) === "Date(" &&
95009 // value.slice(-1) === ")") {
95010 // d = new Date(value.slice(5, -1));
95017 // This is a reference implementation. You are free to copy, modify, or
95025 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
95026 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
95027 lastIndex, length, parse, prototype, push, replace, slice, stringify,
95028 test, toJSON, toString, valueOf
95030 // Create a JSON object only if one does not already exist. We create the
95031 // methods in a closure to avoid creating global variables.
95032 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
95038 var rx_one = /^[\],:{}\s]*$/;
95039 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
95040 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
95041 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
95042 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
95043 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
95046 // Format integers to have at least two digits.
95047 return n < 10 ? "0" + n : n;
95050 function this_value() {
95051 return this.valueOf();
95054 if (typeof Date.prototype.toJSON !== "function") {
95055 Date.prototype.toJSON = function () {
95056 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;
95059 Boolean.prototype.toJSON = this_value;
95060 Number.prototype.toJSON = this_value;
95061 String.prototype.toJSON = this_value;
95069 function quote(string) {
95070 // If the string contains no control characters, no quote characters, and no
95071 // backslash characters, then we can safely slap some quotes around it.
95072 // Otherwise we must also replace the offending characters with safe escape
95074 rx_escapable.lastIndex = 0;
95075 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
95077 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
95078 }) + "\"" : "\"" + string + "\"";
95081 function str(key, holder) {
95082 // Produce a string from holder[key].
95083 var i; // The loop counter.
95085 var k; // The member key.
95087 var v; // The member value.
95092 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
95094 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
95095 value = value.toJSON(key);
95096 } // If we were called with a replacer function, then call the replacer to
95097 // obtain a replacement value.
95100 if (typeof rep === "function") {
95101 value = rep.call(holder, key, value);
95102 } // What happens next depends on the value's type.
95105 switch (_typeof(value)) {
95107 return quote(value);
95110 // JSON numbers must be finite. Encode non-finite numbers as null.
95111 return isFinite(value) ? String(value) : "null";
95115 // If the value is a boolean or null, convert it to a string. Note:
95116 // typeof null does not produce "null". The case is included here in
95117 // the remote chance that this gets fixed someday.
95118 return String(value);
95119 // If the type is "object", we might be dealing with an object or an array or
95123 // Due to a specification blunder in ECMAScript, typeof null is "object",
95124 // so watch out for that case.
95127 } // Make an array to hold the partial results of stringifying this object value.
95131 partial = []; // Is the value an array?
95133 if (Object.prototype.toString.apply(value) === "[object Array]") {
95134 // The value is an array. Stringify every element. Use null as a placeholder
95135 // for non-JSON values.
95136 length = value.length;
95138 for (i = 0; i < length; i += 1) {
95139 partial[i] = str(i, value) || "null";
95140 } // Join all of the elements together, separated with commas, and wrap them in
95144 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
95147 } // If the replacer is an array, use it to select the members to be stringified.
95150 if (rep && _typeof(rep) === "object") {
95151 length = rep.length;
95153 for (i = 0; i < length; i += 1) {
95154 if (typeof rep[i] === "string") {
95159 partial.push(quote(k) + (gap ? ": " : ":") + v);
95164 // Otherwise, iterate through all of the keys in the object.
95166 if (Object.prototype.hasOwnProperty.call(value, k)) {
95170 partial.push(quote(k) + (gap ? ": " : ":") + v);
95174 } // Join all of the member texts together, separated with commas,
95175 // and wrap them in braces.
95178 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
95182 } // If the JSON object does not yet have a stringify method, give it one.
95185 if (typeof JSON.stringify !== "function") {
95187 // table of character substitutions
95197 JSON.stringify = function (value, replacer, space) {
95198 // The stringify method takes a value and an optional replacer, and an optional
95199 // space parameter, and returns a JSON text. The replacer can be a function
95200 // that can replace values, or an array of strings that will select the keys.
95201 // A default replacer method can be provided. Use of the space parameter can
95202 // produce text that is more easily readable.
95205 indent = ""; // If the space parameter is a number, make an indent string containing that
95208 if (typeof space === "number") {
95209 for (i = 0; i < space; i += 1) {
95211 } // If the space parameter is a string, it will be used as the indent string.
95213 } else if (typeof space === "string") {
95215 } // If there is a replacer, it must be a function or an array.
95216 // Otherwise, throw an error.
95221 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
95222 throw new Error("JSON.stringify");
95223 } // Make a fake root object containing our value under the key of "".
95224 // Return the result of stringifying the value.
95231 } // If the JSON object does not yet have a parse method, give it one.
95234 if (typeof JSON.parse !== "function") {
95235 JSON.parse = function (text, reviver) {
95236 // The parse method takes a text and an optional reviver function, and returns
95237 // a JavaScript value if the text is a valid JSON text.
95240 function walk(holder, key) {
95241 // The walk method is used to recursively walk the resulting structure so
95242 // that modifications can be made.
95245 var value = holder[key];
95247 if (value && _typeof(value) === "object") {
95249 if (Object.prototype.hasOwnProperty.call(value, k)) {
95250 v = walk(value, k);
95252 if (v !== undefined) {
95261 return reviver.call(holder, key, value);
95262 } // Parsing happens in four stages. In the first stage, we replace certain
95263 // Unicode characters with escape sequences. JavaScript handles many characters
95264 // incorrectly, either silently deleting them, or treating them as line endings.
95267 text = String(text);
95268 rx_dangerous.lastIndex = 0;
95270 if (rx_dangerous.test(text)) {
95271 text = text.replace(rx_dangerous, function (a) {
95272 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
95274 } // In the second stage, we run the text against regular expressions that look
95275 // for non-JSON patterns. We are especially concerned with "()" and "new"
95276 // because they can cause invocation, and "=" because it can cause mutation.
95277 // But just to be safe, we want to reject all unexpected forms.
95278 // We split the second stage into 4 regexp operations in order to work around
95279 // crippling inefficiencies in IE's and Safari's regexp engines. First we
95280 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
95281 // replace all simple value tokens with "]" characters. Third, we delete all
95282 // open brackets that follow a colon or comma or that begin the text. Finally,
95283 // we look to see that the remaining characters are only whitespace or "]" or
95284 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
95287 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
95288 // In the third stage we use the eval function to compile the text into a
95289 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
95290 // in JavaScript: it can begin a block or an object literal. We wrap the text
95291 // in parens to eliminate the ambiguity.
95292 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
95293 // each name/value pair to a reviver function for possible transformation.
95295 return typeof reviver === "function" ? walk({
95298 } // If the text is not JSON parseable, then a SyntaxError is thrown.
95301 throw new SyntaxError("JSON.parse");
95306 var json2 = json2Plugin;
95308 function json2Plugin() {
95312 var plugins = [json2];
95313 var store_legacy = storeEngine.createStore(all, plugins);
95315 var immutable = extend;
95316 var hasOwnProperty = Object.prototype.hasOwnProperty;
95318 function extend() {
95321 for (var i = 0; i < arguments.length; i++) {
95322 var source = arguments[i];
95324 for (var key in source) {
95325 if (hasOwnProperty.call(source, key)) {
95326 target[key] = source[key];
95335 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
95336 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
95337 // does not support custom headers, which this uses everywhere.
95340 var osmAuth = function osmAuth(o) {
95341 var oauth = {}; // authenticated users will also have a request token secret, but it's
95342 // not used in transactions with the server
95344 oauth.authenticated = function () {
95345 return !!(token('oauth_token') && token('oauth_token_secret'));
95348 oauth.logout = function () {
95349 token('oauth_token', '');
95350 token('oauth_token_secret', '');
95351 token('oauth_request_token_secret', '');
95353 }; // TODO: detect lack of click event
95356 oauth.authenticate = function (callback) {
95357 if (oauth.authenticated()) return callback();
95358 oauth.logout(); // ## Getting a request token
95360 var params = timenonce(getAuth(o)),
95361 url = o.url + '/oauth/request_token';
95362 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
95364 if (!o.singlepage) {
95365 // Create a 600x550 popup window in the center of the screen
95368 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
95369 return x.join('=');
95371 popup = window.open('about:blank', 'oauth_window', settings);
95372 oauth.popupWindow = popup;
95375 var error = new Error('Popup was blocked');
95376 error.status = 'popup-blocked';
95379 } // Request a request token. When this is complete, the popup
95380 // window is redirected to OSM's authorization page.
95383 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
95386 function reqTokenDone(err, xhr) {
95388 if (err) return callback(err);
95389 var resp = ohauth_1.stringQs(xhr.response);
95390 token('oauth_request_token_secret', resp.oauth_token_secret);
95391 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
95392 oauth_token: resp.oauth_token,
95393 oauth_callback: resolveUrl(o.landing)
95396 if (o.singlepage) {
95397 location.href = authorize_url;
95399 popup.location = authorize_url;
95401 } // Called by a function in a landing page, in the popup window. The
95402 // window closes itself.
95405 window.authComplete = function (token) {
95406 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
95407 get_access_token(oauth_token.oauth_token);
95408 delete window.authComplete;
95409 }; // ## Getting an request token
95411 // At this point we have an `oauth_token`, brought in from a function
95412 // call on a landing page popup.
95415 function get_access_token(oauth_token) {
95416 var url = o.url + '/oauth/access_token',
95417 params = timenonce(getAuth(o)),
95418 request_token_secret = token('oauth_request_token_secret');
95419 params.oauth_token = oauth_token;
95420 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
95422 // The final token required for authentication. At this point
95423 // we have a `request token secret`
95425 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
95429 function accessTokenDone(err, xhr) {
95431 if (err) return callback(err);
95432 var access_token = ohauth_1.stringQs(xhr.response);
95433 token('oauth_token', access_token.oauth_token);
95434 token('oauth_token_secret', access_token.oauth_token_secret);
95435 callback(null, oauth);
95439 oauth.bringPopupWindowToFront = function () {
95440 var brougtPopupToFront = false;
95443 // This may cause a cross-origin error:
95444 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
95445 if (oauth.popupWindow && !oauth.popupWindow.closed) {
95446 oauth.popupWindow.focus();
95447 brougtPopupToFront = true;
95449 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
95452 return brougtPopupToFront;
95455 oauth.bootstrapToken = function (oauth_token, callback) {
95456 // ## Getting an request token
95457 // At this point we have an `oauth_token`, brought in from a function
95458 // call on a landing page popup.
95459 function get_access_token(oauth_token) {
95460 var url = o.url + '/oauth/access_token',
95461 params = timenonce(getAuth(o)),
95462 request_token_secret = token('oauth_request_token_secret');
95463 params.oauth_token = oauth_token;
95464 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
95465 // The final token required for authentication. At this point
95466 // we have a `request token secret`
95468 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
95472 function accessTokenDone(err, xhr) {
95474 if (err) return callback(err);
95475 var access_token = ohauth_1.stringQs(xhr.response);
95476 token('oauth_token', access_token.oauth_token);
95477 token('oauth_token_secret', access_token.oauth_token_secret);
95478 callback(null, oauth);
95481 get_access_token(oauth_token);
95484 // A single XMLHttpRequest wrapper that does authenticated calls if the
95485 // user has logged in.
95488 oauth.xhr = function (options, callback) {
95489 if (!oauth.authenticated()) {
95491 return oauth.authenticate(run);
95493 callback('not authenticated', null);
95501 var params = timenonce(getAuth(o)),
95502 oauth_token_secret = token('oauth_token_secret'),
95503 url = options.prefix !== false ? o.url + options.path : options.path,
95504 url_parts = url.replace(/#.*$/, '').split('?', 2),
95505 base_url = url_parts[0],
95506 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
95508 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
95509 params = immutable(params, ohauth_1.stringQs(options.content));
95512 params.oauth_token = token('oauth_token');
95513 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))));
95514 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
95517 function done(err, xhr) {
95518 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
95520 }; // pre-authorize this object, if we can just get a token and token_secret
95524 oauth.preauth = function (c) {
95526 if (c.oauth_token) token('oauth_token', c.oauth_token);
95527 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
95531 oauth.options = function (_) {
95532 if (!arguments.length) return o;
95534 o.url = o.url || 'https://www.openstreetmap.org';
95535 o.landing = o.landing || 'land.html';
95536 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
95537 // by default, no-ops
95539 o.loading = o.loading || function () {};
95541 o.done = o.done || function () {};
95543 return oauth.preauth(o);
95544 }; // 'stamp' an authentication object from `getAuth()`
95545 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
95549 function timenonce(o) {
95550 o.oauth_timestamp = ohauth_1.timestamp();
95551 o.oauth_nonce = ohauth_1.nonce();
95553 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
95554 // can be used with multiple APIs and the keys in `localStorage`
95560 if (store_legacy.enabled) {
95561 token = function token(x, y) {
95562 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
95567 token = function token(x, y) {
95568 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
95570 } // Get an authentication object. If you just add and remove properties
95571 // from a single object, you'll need to use `delete` to make sure that
95572 // it doesn't contain undesired properties for authentication
95575 function getAuth(o) {
95577 oauth_consumer_key: o.oauth_consumer_key,
95578 oauth_signature_method: 'HMAC-SHA1'
95580 } // potentially pre-authorize
95587 var tiler$2 = utilTiler();
95588 var dispatch$2 = dispatch$8('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
95589 var urlroot = 'https://www.openstreetmap.org';
95590 var oauth = osmAuth({
95592 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
95593 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
95594 loading: authLoading,
95596 }); // hardcode default block of Google Maps
95598 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
95620 var _cachedApiStatus;
95622 var _changeset = {};
95624 var _deferred = new Set();
95626 var _connectionID = 1;
95627 var _tileZoom = 16;
95628 var _noteZoom = 12;
95630 var _rateLimitError;
95632 var _userChangesets;
95636 var _off; // set a default but also load this from the API status
95639 var _maxWayNodes = 2000;
95641 function authLoading() {
95642 dispatch$2.call('authLoading');
95645 function authDone() {
95646 dispatch$2.call('authDone');
95649 function abortRequest$2(controllerOrXHR) {
95650 if (controllerOrXHR) {
95651 controllerOrXHR.abort();
95655 function hasInflightRequests(cache) {
95656 return Object.keys(cache.inflight).length;
95659 function abortUnwantedRequests(cache, visibleTiles) {
95660 Object.keys(cache.inflight).forEach(function (k) {
95661 if (cache.toLoad[k]) return;
95662 if (visibleTiles.find(function (tile) {
95663 return k === tile.id;
95665 abortRequest$2(cache.inflight[k]);
95666 delete cache.inflight[k];
95670 function getLoc(attrs) {
95671 var lon = attrs.lon && attrs.lon.value;
95672 var lat = attrs.lat && attrs.lat.value;
95673 return [parseFloat(lon), parseFloat(lat)];
95676 function getNodes(obj) {
95677 var elems = obj.getElementsByTagName('nd');
95678 var nodes = new Array(elems.length);
95680 for (var i = 0, l = elems.length; i < l; i++) {
95681 nodes[i] = 'n' + elems[i].attributes.ref.value;
95687 function getNodesJSON(obj) {
95688 var elems = obj.nodes;
95689 var nodes = new Array(elems.length);
95691 for (var i = 0, l = elems.length; i < l; i++) {
95692 nodes[i] = 'n' + elems[i];
95698 function getTags(obj) {
95699 var elems = obj.getElementsByTagName('tag');
95702 for (var i = 0, l = elems.length; i < l; i++) {
95703 var attrs = elems[i].attributes;
95704 tags[attrs.k.value] = attrs.v.value;
95710 function getMembers(obj) {
95711 var elems = obj.getElementsByTagName('member');
95712 var members = new Array(elems.length);
95714 for (var i = 0, l = elems.length; i < l; i++) {
95715 var attrs = elems[i].attributes;
95717 id: attrs.type.value[0] + attrs.ref.value,
95718 type: attrs.type.value,
95719 role: attrs.role.value
95726 function getMembersJSON(obj) {
95727 var elems = obj.members;
95728 var members = new Array(elems.length);
95730 for (var i = 0, l = elems.length; i < l; i++) {
95731 var attrs = elems[i];
95733 id: attrs.type[0] + attrs.ref,
95742 function getVisible(attrs) {
95743 return !attrs.visible || attrs.visible.value !== 'false';
95746 function parseComments(comments) {
95747 var parsedComments = []; // for each comment
95749 for (var i = 0; i < comments.length; i++) {
95750 var comment = comments[i];
95752 if (comment.nodeName === 'comment') {
95753 var childNodes = comment.childNodes;
95754 var parsedComment = {};
95756 for (var j = 0; j < childNodes.length; j++) {
95757 var node = childNodes[j];
95758 var nodeName = node.nodeName;
95759 if (nodeName === '#text') continue;
95760 parsedComment[nodeName] = node.textContent;
95762 if (nodeName === 'uid') {
95763 var uid = node.textContent;
95765 if (uid && !_userCache.user[uid]) {
95766 _userCache.toLoad[uid] = true;
95771 if (parsedComment) {
95772 parsedComments.push(parsedComment);
95777 return parsedComments;
95780 function encodeNoteRtree(note) {
95790 var jsonparsers = {
95791 node: function nodeData(obj, uid) {
95792 return new osmNode({
95794 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95795 version: obj.version && obj.version.toString(),
95796 changeset: obj.changeset && obj.changeset.toString(),
95797 timestamp: obj.timestamp,
95799 uid: obj.uid && obj.uid.toString(),
95800 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
95804 way: function wayData(obj, uid) {
95805 return new osmWay({
95807 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95808 version: obj.version && obj.version.toString(),
95809 changeset: obj.changeset && obj.changeset.toString(),
95810 timestamp: obj.timestamp,
95812 uid: obj.uid && obj.uid.toString(),
95814 nodes: getNodesJSON(obj)
95817 relation: function relationData(obj, uid) {
95818 return new osmRelation({
95820 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
95821 version: obj.version && obj.version.toString(),
95822 changeset: obj.changeset && obj.changeset.toString(),
95823 timestamp: obj.timestamp,
95825 uid: obj.uid && obj.uid.toString(),
95827 members: getMembersJSON(obj)
95830 user: function parseUser(obj, uid) {
95833 display_name: obj.display_name,
95834 account_created: obj.account_created,
95835 image_url: obj.img && obj.img.href,
95836 changesets_count: obj.changesets && obj.changesets.count && obj.changesets.count.toString() || '0',
95837 active_blocks: obj.blocks && obj.blocks.received && obj.blocks.received.active && obj.blocks.received.active.toString() || '0'
95842 function parseJSON(payload, callback, options) {
95843 options = Object.assign({
95849 message: 'No JSON',
95854 var json = payload;
95855 if (_typeof(json) !== 'object') json = JSON.parse(payload);
95856 if (!json.elements) return callback({
95857 message: 'No JSON',
95860 var children = json.elements;
95861 var handle = window.requestIdleCallback(function () {
95862 _deferred["delete"](handle);
95867 for (var i = 0; i < children.length; i++) {
95868 result = parseChild(children[i]);
95869 if (result) results.push(result);
95872 callback(null, results);
95875 _deferred.add(handle);
95877 function parseChild(child) {
95878 var parser = jsonparsers[child.type];
95879 if (!parser) return null;
95881 uid = osmEntity.id.fromOSM(child.type, child.id);
95883 if (options.skipSeen) {
95884 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
95886 _tileCache.seen[uid] = true;
95889 return parser(child, uid);
95893 function parseUserJSON(payload, callback, options) {
95894 options = Object.assign({
95900 message: 'No JSON',
95905 var json = payload;
95906 if (_typeof(json) !== 'object') json = JSON.parse(payload);
95907 if (!json.users && !json.user) return callback({
95908 message: 'No JSON',
95911 var objs = json.users || [json];
95912 var handle = window.requestIdleCallback(function () {
95913 _deferred["delete"](handle);
95918 for (var i = 0; i < objs.length; i++) {
95919 result = parseObj(objs[i]);
95920 if (result) results.push(result);
95923 callback(null, results);
95926 _deferred.add(handle);
95928 function parseObj(obj) {
95929 var uid = obj.user.id && obj.user.id.toString();
95931 if (options.skipSeen && _userCache.user[uid]) {
95932 delete _userCache.toLoad[uid];
95936 var user = jsonparsers.user(obj.user, uid);
95937 _userCache.user[uid] = user;
95938 delete _userCache.toLoad[uid];
95944 node: function nodeData(obj, uid) {
95945 var attrs = obj.attributes;
95946 return new osmNode({
95948 visible: getVisible(attrs),
95949 version: attrs.version.value,
95950 changeset: attrs.changeset && attrs.changeset.value,
95951 timestamp: attrs.timestamp && attrs.timestamp.value,
95952 user: attrs.user && attrs.user.value,
95953 uid: attrs.uid && attrs.uid.value,
95954 loc: getLoc(attrs),
95958 way: function wayData(obj, uid) {
95959 var attrs = obj.attributes;
95960 return new osmWay({
95962 visible: getVisible(attrs),
95963 version: attrs.version.value,
95964 changeset: attrs.changeset && attrs.changeset.value,
95965 timestamp: attrs.timestamp && attrs.timestamp.value,
95966 user: attrs.user && attrs.user.value,
95967 uid: attrs.uid && attrs.uid.value,
95968 tags: getTags(obj),
95969 nodes: getNodes(obj)
95972 relation: function relationData(obj, uid) {
95973 var attrs = obj.attributes;
95974 return new osmRelation({
95976 visible: getVisible(attrs),
95977 version: attrs.version.value,
95978 changeset: attrs.changeset && attrs.changeset.value,
95979 timestamp: attrs.timestamp && attrs.timestamp.value,
95980 user: attrs.user && attrs.user.value,
95981 uid: attrs.uid && attrs.uid.value,
95982 tags: getTags(obj),
95983 members: getMembers(obj)
95986 note: function parseNote(obj, uid) {
95987 var attrs = obj.attributes;
95988 var childNodes = obj.childNodes;
95991 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
95993 var coincident = false;
95994 var epsilon = 0.00001;
95998 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
96001 var bbox = geoExtent(props.loc).bbox();
96002 coincident = _noteCache.rtree.search(bbox).length;
96003 } while (coincident); // parse note contents
96006 for (var i = 0; i < childNodes.length; i++) {
96007 var node = childNodes[i];
96008 var nodeName = node.nodeName;
96009 if (nodeName === '#text') continue; // if the element is comments, parse the comments
96011 if (nodeName === 'comments') {
96012 props[nodeName] = parseComments(node.childNodes);
96014 props[nodeName] = node.textContent;
96018 var note = new osmNote(props);
96019 var item = encodeNoteRtree(note);
96020 _noteCache.note[note.id] = note;
96022 _noteCache.rtree.insert(item);
96026 user: function parseUser(obj, uid) {
96027 var attrs = obj.attributes;
96030 display_name: attrs.display_name && attrs.display_name.value,
96031 account_created: attrs.account_created && attrs.account_created.value,
96032 changesets_count: '0',
96035 var img = obj.getElementsByTagName('img');
96037 if (img && img[0] && img[0].getAttribute('href')) {
96038 user.image_url = img[0].getAttribute('href');
96041 var changesets = obj.getElementsByTagName('changesets');
96043 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
96044 user.changesets_count = changesets[0].getAttribute('count');
96047 var blocks = obj.getElementsByTagName('blocks');
96049 if (blocks && blocks[0]) {
96050 var received = blocks[0].getElementsByTagName('received');
96052 if (received && received[0] && received[0].getAttribute('active')) {
96053 user.active_blocks = received[0].getAttribute('active');
96057 _userCache.user[uid] = user;
96058 delete _userCache.toLoad[uid];
96063 function parseXML(xml, callback, options) {
96064 options = Object.assign({
96068 if (!xml || !xml.childNodes) {
96075 var root = xml.childNodes[0];
96076 var children = root.childNodes;
96077 var handle = window.requestIdleCallback(function () {
96078 _deferred["delete"](handle);
96083 for (var i = 0; i < children.length; i++) {
96084 result = parseChild(children[i]);
96085 if (result) results.push(result);
96088 callback(null, results);
96091 _deferred.add(handle);
96093 function parseChild(child) {
96094 var parser = parsers[child.nodeName];
96095 if (!parser) return null;
96098 if (child.nodeName === 'user') {
96099 uid = child.attributes.id.value;
96101 if (options.skipSeen && _userCache.user[uid]) {
96102 delete _userCache.toLoad[uid];
96105 } else if (child.nodeName === 'note') {
96106 uid = child.getElementsByTagName('id')[0].textContent;
96108 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
96110 if (options.skipSeen) {
96111 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
96113 _tileCache.seen[uid] = true;
96117 return parser(child, uid);
96119 } // replace or remove note from rtree
96122 function updateRtree(item, replace) {
96123 _noteCache.rtree.remove(item, function isEql(a, b) {
96124 return a.data.id === b.data.id;
96128 _noteCache.rtree.insert(item);
96132 function wrapcb(thisArg, callback, cid) {
96133 return function (err, result) {
96135 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
96136 if (err.status === 400 || err.status === 401 || err.status === 403) {
96140 return callback.call(thisArg, err);
96141 } else if (thisArg.getConnectionId() !== cid) {
96142 return callback.call(thisArg, {
96143 message: 'Connection Switched',
96147 return callback.call(thisArg, err, result);
96153 init: function init() {
96154 utilRebind(this, dispatch$2, 'on');
96156 reset: function reset() {
96157 Array.from(_deferred).forEach(function (handle) {
96158 window.cancelIdleCallback(handle);
96160 _deferred["delete"](handle);
96163 _userChangesets = undefined;
96164 _userDetails = undefined;
96165 _rateLimitError = undefined;
96166 Object.values(_tileCache.inflight).forEach(abortRequest$2);
96167 Object.values(_noteCache.inflight).forEach(abortRequest$2);
96168 Object.values(_noteCache.inflightPost).forEach(abortRequest$2);
96169 if (_changeset.inflight) abortRequest$2(_changeset.inflight);
96190 _cachedApiStatus = undefined;
96194 getConnectionId: function getConnectionId() {
96195 return _connectionID;
96197 changesetURL: function changesetURL(changesetID) {
96198 return urlroot + '/changeset/' + changesetID;
96200 changesetsURL: function changesetsURL(center, zoom) {
96201 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
96202 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
96204 entityURL: function entityURL(entity) {
96205 return urlroot + '/' + entity.type + '/' + entity.osmId();
96207 historyURL: function historyURL(entity) {
96208 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
96210 userURL: function userURL(username) {
96211 return urlroot + '/user/' + username;
96213 noteURL: function noteURL(note) {
96214 return urlroot + '/note/' + note.id;
96216 noteReportURL: function noteReportURL(note) {
96217 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
96219 // Generic method to load data from the OSM API
96220 // Can handle either auth or unauth calls.
96221 loadFromAPI: function loadFromAPI(path, callback, options) {
96222 options = Object.assign({
96226 var cid = _connectionID;
96228 function done(err, payload) {
96229 if (that.getConnectionId() !== cid) {
96230 if (callback) callback({
96231 message: 'Connection Switched',
96237 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
96238 // Logout and retry the request..
96240 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
96242 that.loadFromAPI(path, callback, options); // else, no retry..
96244 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
96245 // Set the rateLimitError flag and trigger a warning..
96246 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
96247 _rateLimitError = err;
96248 dispatch$2.call('change');
96249 that.reloadApiStatus();
96250 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
96251 // If the response's error state doesn't match the status,
96252 // it's likely we lost or gained the connection so reload the status
96253 that.reloadApiStatus();
96258 return callback(err);
96260 if (path.indexOf('.json') !== -1) {
96261 return parseJSON(payload, callback, options);
96263 return parseXML(payload, callback, options);
96270 if (this.authenticated()) {
96276 var url = urlroot + path;
96277 var controller = new AbortController();
96280 if (path.indexOf('.json') !== -1) {
96287 signal: controller.signal
96288 }).then(function (data) {
96290 })["catch"](function (err) {
96291 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
96292 // but we can't access the response itself
96293 // https://github.com/d3/d3-fetch/issues/27
96295 var match = err.message.match(/^\d{3}/);
96300 statusText: err.message
96309 // Load a single entity by id (ways and relations use the `/full` call to include
96310 // nodes and members). Parent relations are not included, see `loadEntityRelations`.
96311 // GET /api/0.6/node/#id
96312 // GET /api/0.6/[way|relation]/#id/full
96313 loadEntity: function loadEntity(id, callback) {
96314 var type = osmEntity.id.type(id);
96315 var osmID = osmEntity.id.toOSM(id);
96319 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
96320 if (callback) callback(err, {
96325 // Load a single entity with a specific version
96326 // GET /api/0.6/[node|way|relation]/#id/#version
96327 loadEntityVersion: function loadEntityVersion(id, version, callback) {
96328 var type = osmEntity.id.type(id);
96329 var osmID = osmEntity.id.toOSM(id);
96333 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
96334 if (callback) callback(err, {
96339 // Load the relations of a single entity with the given.
96340 // GET /api/0.6/[node|way|relation]/#id/relations
96341 loadEntityRelations: function loadEntityRelations(id, callback) {
96342 var type = osmEntity.id.type(id);
96343 var osmID = osmEntity.id.toOSM(id);
96347 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/relations.json', function (err, entities) {
96348 if (callback) callback(err, {
96353 // Load multiple entities in chunks
96354 // (note: callback may be called multiple times)
96355 // Unlike `loadEntity`, child nodes and members are not fetched
96356 // GET /api/0.6/[nodes|ways|relations]?#parameters
96357 loadMultiple: function loadMultiple(ids, callback) {
96359 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
96360 Object.keys(groups).forEach(function (k) {
96361 var type = k + 's'; // nodes, ways, relations
96363 var osmIDs = groups[k].map(function (id) {
96364 return osmEntity.id.toOSM(id);
96369 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
96370 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
96371 if (callback) callback(err, {
96378 // Create, upload, and close a changeset
96379 // PUT /api/0.6/changeset/create
96380 // POST /api/0.6/changeset/#id/upload
96381 // PUT /api/0.6/changeset/#id/close
96382 putChangeset: function putChangeset(changeset, changes, callback) {
96383 var cid = _connectionID;
96385 if (_changeset.inflight) {
96387 message: 'Changeset already inflight',
96390 } else if (_changeset.open) {
96391 // reuse existing open changeset..
96392 return createdChangeset.call(this, null, _changeset.open);
96394 // Open a new changeset..
96397 path: '/api/0.6/changeset/create',
96400 'Content-Type': 'text/xml'
96403 content: JXON.stringify(changeset.asJXON())
96405 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
96408 function createdChangeset(err, changesetID) {
96409 _changeset.inflight = null;
96412 return callback(err, changeset);
96415 _changeset.open = changesetID;
96416 changeset = changeset.update({
96418 }); // Upload the changeset..
96422 path: '/api/0.6/changeset/' + changesetID + '/upload',
96425 'Content-Type': 'text/xml'
96428 content: JXON.stringify(changeset.osmChangeJXON(changes))
96430 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
96433 function uploadedChangeset(err) {
96434 _changeset.inflight = null;
96435 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
96436 // Add delay to allow for postgres replication #1646 #2678
96438 window.setTimeout(function () {
96439 callback(null, changeset);
96441 _changeset.open = null; // At this point, we don't really care if the connection was switched..
96442 // Only try to close the changeset if we're still talking to the same server.
96444 if (this.getConnectionId() === cid) {
96445 // Still attempt to close changeset, but ignore response because #2667
96448 path: '/api/0.6/changeset/' + changeset.id + '/close',
96451 'Content-Type': 'text/xml'
96460 // Load multiple users in chunks
96461 // (note: callback may be called multiple times)
96462 // GET /api/0.6/users?users=#id1,#id2,...,#idn
96463 loadUsers: function loadUsers(uids, callback) {
96466 utilArrayUniq(uids).forEach(function (uid) {
96467 if (_userCache.user[uid]) {
96468 delete _userCache.toLoad[uid];
96469 cached.push(_userCache.user[uid]);
96475 if (cached.length || !this.authenticated()) {
96476 callback(undefined, cached);
96477 if (!this.authenticated()) return; // require auth
96480 utilArrayChunk(toLoad, 150).forEach(function (arr) {
96483 path: '/api/0.6/users.json?users=' + arr.join()
96484 }, wrapcb(this, done, _connectionID));
96487 function done(err, payload) {
96488 if (err) return callback(err);
96492 return parseUserJSON(payload, function (err, results) {
96493 if (err) return callback(err);
96494 return callback(undefined, results);
96498 // Load a given user by id
96499 // GET /api/0.6/user/#id
96500 loadUser: function loadUser(uid, callback) {
96501 if (_userCache.user[uid] || !this.authenticated()) {
96503 delete _userCache.toLoad[uid];
96504 return callback(undefined, _userCache.user[uid]);
96509 path: '/api/0.6/user/' + uid + '.json'
96510 }, wrapcb(this, done, _connectionID));
96512 function done(err, payload) {
96513 if (err) return callback(err);
96517 return parseUserJSON(payload, function (err, results) {
96518 if (err) return callback(err);
96519 return callback(undefined, results[0]);
96523 // Load the details of the logged-in user
96524 // GET /api/0.6/user/details
96525 userDetails: function userDetails(callback) {
96526 if (_userDetails) {
96528 return callback(undefined, _userDetails);
96533 path: '/api/0.6/user/details.json'
96534 }, wrapcb(this, done, _connectionID));
96536 function done(err, payload) {
96537 if (err) return callback(err);
96541 return parseUserJSON(payload, function (err, results) {
96542 if (err) return callback(err);
96543 _userDetails = results[0];
96544 return callback(undefined, _userDetails);
96548 // Load previous changesets for the logged in user
96549 // GET /api/0.6/changesets?user=#id
96550 userChangesets: function userChangesets(callback) {
96551 if (_userChangesets) {
96553 return callback(undefined, _userChangesets);
96556 this.userDetails(wrapcb(this, gotDetails, _connectionID));
96558 function gotDetails(err, user) {
96560 return callback(err);
96565 path: '/api/0.6/changesets?user=' + user.id
96566 }, wrapcb(this, done, _connectionID));
96569 function done(err, xml) {
96571 return callback(err);
96574 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
96576 tags: getTags(changeset)
96578 }).filter(function (changeset) {
96579 var comment = changeset.tags.comment;
96580 return comment && comment !== '';
96582 return callback(undefined, _userChangesets);
96585 // Fetch the status of the OSM API
96586 // GET /api/capabilities
96587 status: function status(callback) {
96588 var url = urlroot + '/api/capabilities';
96589 var errback = wrapcb(this, done, _connectionID);
96590 d3_xml(url).then(function (data) {
96591 errback(null, data);
96592 })["catch"](function (err) {
96593 errback(err.message);
96596 function done(err, xml) {
96598 // the status is null if no response could be retrieved
96599 return callback(err, null);
96600 } // update blocklists
96603 var elements = xml.getElementsByTagName('blacklist');
96606 for (var i = 0; i < elements.length; i++) {
96607 var regexString = elements[i].getAttribute('regex'); // needs unencode?
96611 var regex = new RegExp(regexString);
96612 regexes.push(regex);
96619 if (regexes.length) {
96620 _imageryBlocklists = regexes;
96623 if (_rateLimitError) {
96624 return callback(_rateLimitError, 'rateLimited');
96626 var waynodes = xml.getElementsByTagName('waynodes');
96627 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
96628 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
96629 var apiStatus = xml.getElementsByTagName('status');
96630 var val = apiStatus[0].getAttribute('api');
96631 return callback(undefined, val);
96635 // Calls `status` and dispatches an `apiStatusChange` event if the returned
96636 // status differs from the cached status.
96637 reloadApiStatus: function reloadApiStatus() {
96638 // throttle to avoid unnecessary API calls
96639 if (!this.throttledReloadApiStatus) {
96641 this.throttledReloadApiStatus = throttle(function () {
96642 that.status(function (err, status) {
96643 if (status !== _cachedApiStatus) {
96644 _cachedApiStatus = status;
96645 dispatch$2.call('apiStatusChange', that, err, status);
96651 this.throttledReloadApiStatus();
96653 // Returns the maximum number of nodes a single way can have
96654 maxWayNodes: function maxWayNodes() {
96655 return _maxWayNodes;
96657 // Load data (entities) from the API in tiles
96658 // GET /api/0.6/map?bbox=
96659 loadTiles: function loadTiles(projection, callback) {
96660 if (_off) return; // determine the needed tiles to cover the view
96662 var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
96664 var hadRequests = hasInflightRequests(_tileCache);
96665 abortUnwantedRequests(_tileCache, tiles);
96667 if (hadRequests && !hasInflightRequests(_tileCache)) {
96668 dispatch$2.call('loaded'); // stop the spinner
96669 } // issue new requests..
96672 tiles.forEach(function (tile) {
96673 this.loadTile(tile, callback);
96676 // Load a single data tile
96677 // GET /api/0.6/map?bbox=
96678 loadTile: function loadTile(tile, callback) {
96680 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
96682 if (!hasInflightRequests(_tileCache)) {
96683 dispatch$2.call('loading'); // start the spinner
96686 var path = '/api/0.6/map.json?bbox=';
96690 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
96692 function tileCallback(err, parsed) {
96693 delete _tileCache.inflight[tile.id];
96696 delete _tileCache.toLoad[tile.id];
96697 _tileCache.loaded[tile.id] = true;
96698 var bbox = tile.extent.bbox();
96701 _tileCache.rtree.insert(bbox);
96705 callback(err, Object.assign({
96710 if (!hasInflightRequests(_tileCache)) {
96711 dispatch$2.call('loaded'); // stop the spinner
96715 isDataLoaded: function isDataLoaded(loc) {
96722 return _tileCache.rtree.collides(bbox);
96724 // load the tile that covers the given `loc`
96725 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
96726 // Back off if the toLoad queue is filling up.. re #6417
96727 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
96728 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
96729 if (Object.keys(_tileCache.toLoad).length > 50) return;
96730 var k = geoZoomToScale(_tileZoom + 1);
96731 var offset = geoRawMercator().scale(k)(loc);
96732 var projection = geoRawMercator().transform({
96737 var tiles = tiler$2.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection);
96738 tiles.forEach(function (tile) {
96739 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
96740 _tileCache.toLoad[tile.id] = true;
96741 this.loadTile(tile, callback);
96744 // Load notes from the API in tiles
96745 // GET /api/0.6/notes?bbox=
96746 loadNotes: function loadNotes(projection, noteOptions) {
96747 noteOptions = Object.assign({
96753 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
96755 var throttleLoadUsers = throttle(function () {
96756 var uids = Object.keys(_userCache.toLoad);
96757 if (!uids.length) return;
96758 that.loadUsers(uids, function () {}); // eagerly load user details
96759 }, 750); // determine the needed tiles to cover the view
96762 var tiles = tiler$2.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
96764 abortUnwantedRequests(_noteCache, tiles); // issue new requests..
96766 tiles.forEach(function (tile) {
96767 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
96771 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
96772 delete _noteCache.inflight[tile.id];
96775 _noteCache.loaded[tile.id] = true;
96778 throttleLoadUsers();
96779 dispatch$2.call('loadedNotes');
96784 // POST /api/0.6/notes?params
96785 postNoteCreate: function postNoteCreate(note, callback) {
96786 if (!this.authenticated()) {
96788 message: 'Not Authenticated',
96793 if (_noteCache.inflightPost[note.id]) {
96795 message: 'Note update already inflight',
96800 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
96802 var comment = note.newComment;
96804 if (note.newCategory && note.newCategory !== 'None') {
96805 comment += ' #' + note.newCategory;
96808 var path = '/api/0.6/notes?' + utilQsString({
96813 _noteCache.inflightPost[note.id] = oauth.xhr({
96816 }, wrapcb(this, done, _connectionID));
96818 function done(err, xml) {
96819 delete _noteCache.inflightPost[note.id];
96822 return callback(err);
96823 } // we get the updated note back, remove from caches and reparse..
96826 this.removeNote(note);
96830 return parseXML(xml, function (err, results) {
96832 return callback(err);
96834 return callback(undefined, results[0]);
96840 // POST /api/0.6/notes/#id/comment?text=comment
96841 // POST /api/0.6/notes/#id/close?text=comment
96842 // POST /api/0.6/notes/#id/reopen?text=comment
96843 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
96844 if (!this.authenticated()) {
96846 message: 'Not Authenticated',
96851 if (_noteCache.inflightPost[note.id]) {
96853 message: 'Note update already inflight',
96860 if (note.status !== 'closed' && newStatus === 'closed') {
96862 } else if (note.status !== 'open' && newStatus === 'open') {
96865 action = 'comment';
96866 if (!note.newComment) return; // when commenting, comment required
96869 var path = '/api/0.6/notes/' + note.id + '/' + action;
96871 if (note.newComment) {
96872 path += '?' + utilQsString({
96873 text: note.newComment
96877 _noteCache.inflightPost[note.id] = oauth.xhr({
96880 }, wrapcb(this, done, _connectionID));
96882 function done(err, xml) {
96883 delete _noteCache.inflightPost[note.id];
96886 return callback(err);
96887 } // we get the updated note back, remove from caches and reparse..
96890 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
96892 if (action === 'close') {
96893 _noteCache.closed[note.id] = true;
96894 } else if (action === 'reopen') {
96895 delete _noteCache.closed[note.id];
96901 return parseXML(xml, function (err, results) {
96903 return callback(err);
96905 return callback(undefined, results[0]);
96910 "switch": function _switch(options) {
96911 urlroot = options.urlroot;
96912 oauth.options(Object.assign({
96914 loading: authLoading,
96918 this.userChangesets(function () {}); // eagerly load user details/changesets
96920 dispatch$2.call('change');
96923 toggle: function toggle(val) {
96927 isChangesetInflight: function isChangesetInflight() {
96928 return !!_changeset.inflight;
96930 // get/set cached data
96931 // This is used to save/restore the state when entering/exiting the walkthrough
96932 // Also used for testing purposes.
96933 caches: function caches(obj) {
96934 function cloneCache(source) {
96936 Object.keys(source).forEach(function (k) {
96937 if (k === 'rtree') {
96938 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
96939 } else if (k === 'note') {
96941 Object.keys(source.note).forEach(function (id) {
96942 target.note[id] = osmNote(source.note[id]); // copy notes
96945 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
96951 if (!arguments.length) {
96953 tile: cloneCache(_tileCache),
96954 note: cloneCache(_noteCache),
96955 user: cloneCache(_userCache)
96957 } // access caches directly for testing (e.g., loading notes rtree)
96960 if (obj === 'get') {
96969 _tileCache = obj.tile;
96970 _tileCache.inflight = {};
96974 _noteCache = obj.note;
96975 _noteCache.inflight = {};
96976 _noteCache.inflightPost = {};
96980 _userCache = obj.user;
96985 logout: function logout() {
96986 _userChangesets = undefined;
96987 _userDetails = undefined;
96989 dispatch$2.call('change');
96992 authenticated: function authenticated() {
96993 return oauth.authenticated();
96995 authenticate: function authenticate(callback) {
96997 var cid = _connectionID;
96998 _userChangesets = undefined;
96999 _userDetails = undefined;
97001 function done(err, res) {
97003 if (callback) callback(err);
97007 if (that.getConnectionId() !== cid) {
97008 if (callback) callback({
97009 message: 'Connection Switched',
97015 _rateLimitError = undefined;
97016 dispatch$2.call('change');
97017 if (callback) callback(err, res);
97018 that.userChangesets(function () {}); // eagerly load user details/changesets
97021 return oauth.authenticate(done);
97023 imageryBlocklists: function imageryBlocklists() {
97024 return _imageryBlocklists;
97026 tileZoom: function tileZoom(val) {
97027 if (!arguments.length) return _tileZoom;
97031 // get all cached notes covering the viewport
97032 notes: function notes(projection) {
97033 var viewport = projection.clipExtent();
97034 var min = [viewport[0][0], viewport[1][1]];
97035 var max = [viewport[1][0], viewport[0][1]];
97036 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
97037 return _noteCache.rtree.search(bbox).map(function (d) {
97041 // get a single note from the cache
97042 getNote: function getNote(id) {
97043 return _noteCache.note[id];
97045 // remove a single note from the cache
97046 removeNote: function removeNote(note) {
97047 if (!(note instanceof osmNote) || !note.id) return;
97048 delete _noteCache.note[note.id];
97049 updateRtree(encodeNoteRtree(note), false); // false = remove
97051 // replace a single note in the cache
97052 replaceNote: function replaceNote(note) {
97053 if (!(note instanceof osmNote) || !note.id) return;
97054 _noteCache.note[note.id] = note;
97055 updateRtree(encodeNoteRtree(note), true); // true = replace
97059 // Get an array of note IDs closed during this session.
97060 // Used to populate `closed:note` changeset tag
97061 getClosedIDs: function getClosedIDs() {
97062 return Object.keys(_noteCache.closed).sort();
97066 var _apibase$1 = 'https://wiki.openstreetmap.org/w/api.php';
97067 var _inflight$1 = {};
97068 var _wikibaseCache = {};
97073 var debouncedRequest$1 = debounce(request$1, 500, {
97077 function request$1(url, callback) {
97078 if (_inflight$1[url]) return;
97079 var controller = new AbortController();
97080 _inflight$1[url] = controller;
97082 signal: controller.signal
97083 }).then(function (result) {
97084 delete _inflight$1[url];
97085 if (callback) callback(null, result);
97086 })["catch"](function (err) {
97087 delete _inflight$1[url];
97088 if (err.name === 'AbortError') return;
97089 if (callback) callback(err.message);
97093 var serviceOsmWikibase = {
97094 init: function init() {
97096 _wikibaseCache = {};
97099 reset: function reset() {
97100 Object.values(_inflight$1).forEach(function (controller) {
97101 controller.abort();
97107 * Get the best value for the property, or undefined if not found
97108 * @param entity object from wikibase
97109 * @param property string e.g. 'P4' for image
97110 * @param langCode string e.g. 'fr' for French
97112 claimToValue: function claimToValue(entity, property, langCode) {
97113 if (!entity.claims[property]) return undefined;
97114 var locale = _localeIDs[langCode];
97115 var preferredPick, localePick;
97116 entity.claims[property].forEach(function (stmt) {
97117 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
97118 // Or if not found, use the first value with the "preferred" rank
97119 if (!preferredPick && stmt.rank === 'preferred') {
97120 preferredPick = stmt;
97123 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
97127 var result = localePick || preferredPick;
97130 var datavalue = result.mainsnak.datavalue;
97131 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
97138 * Convert monolingual property into a key-value object (language -> value)
97139 * @param entity object from wikibase
97140 * @param property string e.g. 'P31' for monolingual wiki page title
97142 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
97143 if (!entity || !entity.claims[property]) return undefined;
97144 return entity.claims[property].reduce(function (acc, obj) {
97145 var value = obj.mainsnak.datavalue.value;
97146 acc[value.language] = value.text;
97150 toSitelink: function toSitelink(key, value) {
97151 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
97152 return result.replace(/_/g, ' ').trim();
97155 // Pass params object of the form:
97158 // value: 'string',
97159 // langCode: 'string'
97162 getEntity: function getEntity(params, callback) {
97163 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
97167 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
97168 var keySitelink = params.key ? this.toSitelink(params.key) : false;
97169 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
97170 var localeSitelink;
97172 if (params.langCodes) {
97173 params.langCodes.forEach(function (langCode) {
97174 if (_localeIDs[langCode] === undefined) {
97175 // If this is the first time we are asking about this locale,
97176 // fetch corresponding entity (if it exists), and cache it.
97177 // If there is no such entry, cache `false` value to avoid re-requesting it.
97178 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
97179 titles.push(localeSitelink);
97184 if (rtypeSitelink) {
97185 if (_wikibaseCache[rtypeSitelink]) {
97186 result.rtype = _wikibaseCache[rtypeSitelink];
97188 titles.push(rtypeSitelink);
97193 if (_wikibaseCache[keySitelink]) {
97194 result.key = _wikibaseCache[keySitelink];
97196 titles.push(keySitelink);
97201 if (_wikibaseCache[tagSitelink]) {
97202 result.tag = _wikibaseCache[tagSitelink];
97204 titles.push(tagSitelink);
97208 if (!titles.length) {
97209 // Nothing to do, we already had everything in the cache
97210 return callback(null, result);
97211 } // Requesting just the user language code
97212 // If backend recognizes the code, it will perform proper fallbacks,
97213 // and the result will contain the requested code. If not, all values are returned:
97214 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
97215 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
97219 action: 'wbgetentities',
97221 titles: titles.join('|'),
97222 languages: params.langCodes.join('|'),
97223 languagefallback: 1,
97225 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
97226 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
97227 // formatversion: 2,
97230 var url = _apibase$1 + '?' + utilQsString(obj);
97231 doRequest(url, function (err, d) {
97234 } else if (!d.success || d.error) {
97235 callback(d.error.messages.map(function (v) {
97236 return v.html['*'];
97239 var localeID = false;
97240 Object.values(d.entities).forEach(function (res) {
97241 if (res.missing !== '') {
97242 var title = res.sitelinks.wiki.title;
97244 if (title === rtypeSitelink) {
97245 _wikibaseCache[rtypeSitelink] = res;
97246 result.rtype = res;
97247 } else if (title === keySitelink) {
97248 _wikibaseCache[keySitelink] = res;
97250 } else if (title === tagSitelink) {
97251 _wikibaseCache[tagSitelink] = res;
97253 } else if (title === localeSitelink) {
97256 console.log('Unexpected title ' + title); // eslint-disable-line no-console
97261 if (localeSitelink) {
97262 // If locale ID is not found, store false to prevent repeated queries
97263 that.addLocale(params.langCodes[0], localeID);
97266 callback(null, result);
97271 // Pass params object of the form:
97273 // key: 'string', // required
97274 // value: 'string' // optional
97277 // Get an result object used to display tag documentation
97279 // title: 'string',
97280 // description: 'string',
97281 // editURL: 'string',
97282 // imageURL: 'string',
97283 // wiki: { title: 'string', text: 'string', url: 'string' }
97286 getDocs: function getDocs(params, callback) {
97288 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
97289 return code.toLowerCase();
97291 params.langCodes = langCodes;
97292 this.getEntity(params, function (err, data) {
97298 var entity = data.rtype || data.tag || data.key;
97301 callback('No entity');
97308 for (i in langCodes) {
97309 var _code = langCodes[i];
97311 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
97312 description = entity.descriptions[_code];
97317 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
97320 title: entity.title,
97321 description: description ? description.value : '',
97322 descriptionLocaleCode: description ? description.language : '',
97323 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
97326 if (entity.claims) {
97328 var image = that.claimToValue(entity, 'P4', langCodes[0]);
97331 imageroot = 'https://commons.wikimedia.org/w/index.php';
97333 image = that.claimToValue(entity, 'P28', langCodes[0]);
97336 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
97340 if (imageroot && image) {
97341 result.imageURL = imageroot + '?' + utilQsString({
97342 title: 'Special:Redirect/file/' + image,
97346 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
97347 // If neither tag nor key data item contain a wiki page in the needed language nor English,
97348 // get the first found wiki page from either the tag or the key item.
97351 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
97352 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
97353 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
97354 var wikis = [rtypeWiki, tagWiki, keyWiki];
97357 var wiki = wikis[i];
97359 for (var j in langCodes) {
97360 var code = langCodes[j];
97361 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
97362 var info = getWikiInfo(wiki, code, referenceId);
97365 result.wiki = info;
97370 if (result.wiki) break;
97373 callback(null, result); // Helper method to get wiki info if a given language exists
97375 function getWikiInfo(wiki, langCode, tKey) {
97376 if (wiki && wiki[langCode]) {
97378 title: wiki[langCode],
97380 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
97386 addLocale: function addLocale(langCode, qid) {
97387 // Makes it easier to unit test
97388 _localeIDs[langCode] = qid;
97390 apibase: function apibase(val) {
97391 if (!arguments.length) return _apibase$1;
97397 var jsonpCache = {};
97398 window.jsonpCache = jsonpCache;
97399 function jsonpRequest(url, callback) {
97401 abort: function abort() {}
97404 if (window.JSONP_FIX) {
97405 if (window.JSONP_DELAY === 0) {
97406 callback(window.JSONP_FIX);
97408 var t = window.setTimeout(function () {
97409 callback(window.JSONP_FIX);
97410 }, window.JSONP_DELAY || 0);
97412 request.abort = function () {
97413 window.clearTimeout(t);
97421 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
97426 c += chars.charAt(Math.floor(Math.random() * 52));
97432 function create(url) {
97433 var e = url.match(/callback=(\w+)/);
97434 var c = e ? e[1] : rand();
97436 jsonpCache[c] = function (data) {
97437 if (jsonpCache[c]) {
97444 function finalize() {
97445 delete jsonpCache[c];
97449 request.abort = finalize;
97450 return 'jsonpCache.' + c;
97453 var cb = create(url);
97454 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
97458 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
97459 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
97460 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
97461 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
97462 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
97463 var maxResults = 2000;
97464 var tileZoom = 16.5;
97465 var tiler$1 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
97466 var dispatch$1 = dispatch$8('loadedImages', 'viewerChanged');
97467 var minHfov = 10; // zoom in degrees: 20, 10, 5
97469 var maxHfov = 90; // zoom out degrees
97471 var defaultHfov = 45;
97472 var _hires = false;
97473 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
97475 var _currScene = 0;
97479 var _pannellumViewer;
97481 var _sceneOptions = {
97482 showFullscreenCtrl: false,
97493 var _loadViewerPromise;
97499 function abortRequest$1(i) {
97503 * localeTimeStamp().
97507 function localeTimestamp(s) {
97508 if (!s) return null;
97514 var d = new Date(s);
97515 if (isNaN(d.getTime())) return null;
97516 return d.toLocaleString(_mainLocalizer.localeCode(), options);
97519 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
97523 function loadTiles(which, url, projection, margin) {
97524 var tiles = tiler$1.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
97526 var cache = _ssCache[which];
97527 Object.keys(cache.inflight).forEach(function (k) {
97528 var wanted = tiles.find(function (tile) {
97529 return k.indexOf(tile.id + ',') === 0;
97533 abortRequest$1(cache.inflight[k]);
97534 delete cache.inflight[k];
97537 tiles.forEach(function (tile) {
97538 return loadNextTilePage(which, url, tile);
97542 * loadNextTilePage() load data for the next tile page in line.
97546 function loadNextTilePage(which, url, tile) {
97547 var cache = _ssCache[which];
97548 var nextPage = cache.nextPage[tile.id] || 0;
97549 var id = tile.id + ',' + String(nextPage);
97550 if (cache.loaded[id] || cache.inflight[id]) return;
97551 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
97552 cache.loaded[id] = true;
97553 delete cache.inflight[id];
97554 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
97557 var features = bubbles.map(function (bubble) {
97558 if (cache.points[bubble.id]) return null; // skip duplicates
97560 var loc = [bubble.lo, bubble.la];
97565 captured_at: bubble.cd,
97566 captured_by: 'microsoft',
97567 // nbn: bubble.nbn,
97568 // pbn: bubble.pbn,
97578 cache.points[bubble.id] = d; // a sequence starts here
97580 if (bubble.pr === undefined) {
97581 cache.leaders.push(bubble.id);
97591 }).filter(Boolean);
97592 cache.rtree.load(features);
97593 connectSequences();
97595 if (which === 'bubbles') {
97596 dispatch$1.call('loadedImages');
97599 } // call this sometimes to connect the bubbles into sequences
97602 function connectSequences() {
97603 var cache = _ssCache.bubbles;
97604 var keepLeaders = [];
97606 for (var i = 0; i < cache.leaders.length; i++) {
97607 var bubble = cache.points[cache.leaders[i]];
97608 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
97614 var complete = false;
97617 sequence.bubbles.push(bubble);
97618 seen[bubble.key] = true;
97620 if (bubble.ne === undefined) {
97623 bubble = cache.points[bubble.ne]; // advance to next
97625 } while (bubble && !seen[bubble.key] && !complete);
97628 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
97630 for (var j = 0; j < sequence.bubbles.length; j++) {
97631 sequence.bubbles[j].sequenceKey = sequence.key;
97632 } // create a GeoJSON LineString
97635 sequence.geojson = {
97636 type: 'LineString',
97638 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
97639 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
97642 coordinates: sequence.bubbles.map(function (d) {
97647 keepLeaders.push(cache.leaders[i]);
97649 } // couldn't complete these, save for later
97652 cache.leaders = keepLeaders;
97655 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
97659 function getBubbles(url, tile, callback) {
97660 var rect = tile.extent.rectangle();
97661 var urlForRequest = url + utilQsString({
97667 appkey: bubbleAppKey,
97668 jsCallback: '{callback}'
97670 return jsonpRequest(urlForRequest, function (data) {
97671 if (!data || data.error) {
97677 } // partition viewport into higher zoom tiles
97680 function partitionViewport(projection) {
97681 var z = geoScaleToZoom(projection.scale());
97682 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
97684 var tiler = utilTiler().zoomExtent([z2, z2]);
97685 return tiler.getTiles(projection).map(function (tile) {
97686 return tile.extent;
97688 } // no more than `limit` results per partition.
97691 function searchLimited(limit, projection, rtree) {
97692 limit = limit || 5;
97693 return partitionViewport(projection).reduce(function (result, extent) {
97694 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
97697 return found.length ? result.concat(found) : result;
97705 function loadImage(imgInfo) {
97706 return new Promise(function (resolve) {
97707 var img = new Image();
97709 img.onload = function () {
97710 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
97711 var ctx = canvas.getContext('2d');
97712 ctx.drawImage(img, imgInfo.x, imgInfo.y);
97719 img.onerror = function () {
97726 img.setAttribute('crossorigin', '');
97727 img.src = imgInfo.url;
97735 function loadCanvas(imageGroup) {
97736 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
97737 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
97746 var face = data[0].imgInfo.face;
97747 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
97749 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
97758 function loadFaces(faceGroup) {
97759 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
97761 status: 'loadFaces done'
97766 function setupCanvas(selection, reset) {
97768 selection.selectAll('#ideditor-stitcher-canvases').remove();
97769 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
97770 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
97773 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', function (d) {
97774 return 'ideditor-' + d;
97775 }).attr('width', _resolution).attr('height', _resolution);
97778 function qkToXY(qk) {
97783 for (var i = qk.length; i > 0; i--) {
97784 var key = qk[i - 1];
97785 x += +(key === '1' || key === '3') * scale;
97786 y += +(key === '2' || key === '3') * scale;
97793 function getQuadKeys() {
97794 var dim = _resolution / 256;
97798 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'];
97799 } else if (dim === 8) {
97800 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'];
97801 } else if (dim === 4) {
97802 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
97805 quadKeys = ['0', '1', '2', '3'];
97811 var serviceStreetside = {
97813 * init() initialize streetside.
97815 init: function init() {
97820 this.event = utilRebind(this, dispatch$1, 'on');
97824 * reset() reset the cache.
97826 reset: function reset() {
97828 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$1);
97836 rtree: new RBush(),
97847 bubbles: function bubbles(projection) {
97849 return searchLimited(limit, projection, _ssCache.bubbles.rtree);
97851 cachedImage: function cachedImage(imageKey) {
97852 return _ssCache.bubbles.points[imageKey];
97854 sequences: function sequences(projection) {
97855 var viewport = projection.clipExtent();
97856 var min = [viewport[0][0], viewport[1][1]];
97857 var max = [viewport[1][0], viewport[0][1]];
97858 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
97860 var results = []; // all sequences for bubbles in viewport
97862 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
97863 var key = d.data.sequenceKey;
97865 if (key && !seen[key]) {
97867 results.push(_ssCache.sequences[key].geojson);
97877 loadBubbles: function loadBubbles(projection, margin) {
97878 // by default: request 2 nearby tiles so we can connect sequences.
97879 if (margin === undefined) margin = 2;
97880 loadTiles('bubbles', bubbleApi, projection, margin);
97882 viewer: function viewer() {
97883 return _pannellumViewer;
97885 initViewer: function initViewer() {
97886 if (!window.pannellum) return;
97887 if (_pannellumViewer) return;
97890 var sceneID = _currScene.toString();
97894 firstScene: sceneID
97898 options.scenes[sceneID] = _sceneOptions;
97899 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
97901 ensureViewerLoaded: function ensureViewerLoaded(context) {
97902 if (_loadViewerPromise) return _loadViewerPromise; // create ms-wrapper, a photo wrapper class
97904 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
97905 // (used by all to house each custom photo viewer)
97907 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
97909 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
97911 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
97912 select(window).on(pointerPrefix + 'move.streetside', function () {
97913 dispatch$1.call('viewerChanged');
97915 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
97916 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
97918 var t = timer(function (elapsed) {
97919 dispatch$1.call('viewerChanged');
97921 if (elapsed > 2000) {
97925 }).append('div').attr('class', 'photo-attribution fillD');
97926 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
97927 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
97928 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
97930 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
97932 context.ui().photoviewer.on('resize.streetside', function () {
97933 if (_pannellumViewer) {
97934 _pannellumViewer.resize();
97937 _loadViewerPromise = new Promise(function (resolve, reject) {
97938 var loadedCount = 0;
97940 function loaded() {
97941 loadedCount += 1; // wait until both files are loaded
97943 if (loadedCount === 2) resolve();
97946 var head = select('head'); // load streetside pannellum viewer css
97948 head.selectAll('#ideditor-streetside-viewercss').data([0]).enter().append('link').attr('id', 'ideditor-streetside-viewercss').attr('rel', 'stylesheet').attr('crossorigin', 'anonymous').attr('href', context.asset(pannellumViewerCSS)).on('load.serviceStreetside', loaded).on('error.serviceStreetside', function () {
97950 }); // load streetside pannellum viewer js
97952 head.selectAll('#ideditor-streetside-viewerjs').data([0]).enter().append('script').attr('id', 'ideditor-streetside-viewerjs').attr('crossorigin', 'anonymous').attr('src', context.asset(pannellumViewerJS)).on('load.serviceStreetside', loaded).on('error.serviceStreetside', function () {
97955 })["catch"](function () {
97956 _loadViewerPromise = null;
97958 return _loadViewerPromise;
97960 function step(stepBy) {
97961 return function () {
97962 var viewer = context.container().select('.photoviewer');
97963 var selected = viewer.empty() ? undefined : viewer.datum();
97964 if (!selected) return;
97965 var nextID = stepBy === 1 ? selected.ne : selected.pr;
97967 var yaw = _pannellumViewer.getYaw();
97969 var ca = selected.ca + yaw;
97970 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
97973 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
97974 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
97975 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
97976 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
97977 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
97979 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
97980 poly = geoRotate(poly, -angle, origin);
97981 var extent = poly.reduce(function (extent, point) {
97982 return extent.extend(geoExtent(point));
97983 }, geoExtent()); // find nearest other bubble in the search polygon
97985 var minDist = Infinity;
97987 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
97988 if (d.data.key === selected.key) return;
97989 if (!geoPointInPolygon(d.data.loc, poly)) return;
97990 var dist = geoVecLength(d.data.loc, selected.loc);
97991 var theta = selected.ca - d.data.ca;
97992 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
97994 if (minTheta > 20) {
97995 dist += 5; // penalize distance if camera angles don't match
97998 if (dist < minDist) {
97999 nextID = d.data.key;
98004 var nextBubble = nextID && that.cachedImage(nextID);
98005 if (!nextBubble) return;
98006 context.map().centerEase(nextBubble.loc);
98007 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
98011 yaw: function yaw(_yaw) {
98012 if (typeof _yaw !== 'number') return _yaw;
98013 _sceneOptions.yaw = _yaw;
98020 showViewer: function showViewer(context) {
98021 var wrap = context.container().select('.photoviewer').classed('hide', false);
98022 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
98025 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
98026 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
98035 hideViewer: function hideViewer(context) {
98036 var viewer = context.container().select('.photoviewer');
98037 if (!viewer.empty()) viewer.datum(null);
98038 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
98039 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
98040 this.updateUrlImage(null);
98041 return this.setStyles(context, null, true);
98047 selectImage: function selectImage(context, key) {
98049 var d = this.cachedImage(key);
98050 var viewer = context.container().select('.photoviewer');
98051 if (!viewer.empty()) viewer.datum(d);
98052 this.setStyles(context, null, true);
98053 var wrap = context.container().select('.photoviewer .ms-wrapper');
98054 var attribution = wrap.selectAll('.photo-attribution').html('');
98055 wrap.selectAll('.pnlm-load-box') // display "loading.."
98056 .style('display', 'block');
98057 if (!d) return this;
98058 this.updateUrlImage(key);
98059 _sceneOptions.northOffset = d.ca;
98060 var line1 = attribution.append('div').attr('class', 'attribution-row');
98061 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
98063 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
98064 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
98065 d3_event.stopPropagation();
98067 _resolution = _hires ? 1024 : 512;
98068 wrap.call(setupCanvas, true);
98070 yaw: _pannellumViewer.getYaw(),
98071 pitch: _pannellumViewer.getPitch(),
98072 hfov: _pannellumViewer.getHfov()
98074 _sceneOptions = Object.assign(_sceneOptions, viewstate);
98075 that.selectImage(context, d.key).showViewer(context);
98077 label.append('span').html(_t.html('streetside.hires'));
98078 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
98080 if (d.captured_by) {
98081 var yyyy = new Date().getFullYear();
98082 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
98083 captureInfo.append('span').html('|');
98086 if (d.captured_at) {
98087 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
98088 } // Add image links
98091 var line2 = attribution.append('div').attr('class', 'attribution-row');
98092 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').html(_t.html('streetside.view_on_bing'));
98093 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').html(_t.html('streetside.report'));
98094 var bubbleIdQuadKey = d.key.toString(4);
98095 var paddingNeeded = 16 - bubbleIdQuadKey.length;
98097 for (var i = 0; i < paddingNeeded; i++) {
98098 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
98101 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
98102 var imgUrlSuffix = '.jpg?g=6338&n=z'; // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
98104 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
98106 var quadKeys = getQuadKeys();
98107 var faces = faceKeys.map(function (faceKey) {
98108 return quadKeys.map(function (quadKey) {
98109 var xy = qkToXY(quadKey);
98112 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
98118 loadFaces(faces).then(function () {
98119 if (!_pannellumViewer) {
98122 // make a new scene
98125 var sceneID = _currScene.toString();
98127 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
98130 if (_currScene > 2) {
98131 sceneID = (_currScene - 1).toString();
98133 _pannellumViewer.removeScene(sceneID);
98139 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
98140 return d && d.sequenceKey;
98142 // Updates the currently highlighted sequence and selected bubble.
98143 // Reset is only necessary when interacting with the viewport because
98144 // this implicitly changes the currently selected bubble/sequence
98145 setStyles: function setStyles(context, hovered, reset) {
98147 // reset all layers
98148 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
98149 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
98152 var hoveredBubbleKey = hovered && hovered.key;
98153 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
98154 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
98155 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
98158 var viewer = context.container().select('.photoviewer');
98159 var selected = viewer.empty() ? undefined : viewer.datum();
98160 var selectedBubbleKey = selected && selected.key;
98161 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
98162 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
98163 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
98165 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
98167 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
98168 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
98169 return highlightedBubbleKeys.indexOf(d.key) !== -1;
98170 }).classed('hovered', function (d) {
98171 return d.key === hoveredBubbleKey;
98172 }).classed('currentView', function (d) {
98173 return d.key === selectedBubbleKey;
98175 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
98176 return d.properties.key === hoveredSequenceKey;
98177 }).classed('currentView', function (d) {
98178 return d.properties.key === selectedSequenceKey;
98179 }); // update viewfields if needed
98181 context.container().selectAll('.layer-streetside-images .viewfield-group .viewfield').attr('d', viewfieldPath);
98183 function viewfieldPath() {
98184 var d = this.parentNode.__data__;
98186 if (d.pano && d.key !== selectedBubbleKey) {
98187 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
98189 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
98195 updateUrlImage: function updateUrlImage(imageKey) {
98196 if (!window.mocha) {
98197 var hash = utilStringQs(window.location.hash);
98200 hash.photo = 'streetside/' + imageKey;
98205 window.location.replace('#' + utilQsString(hash, true));
98212 cache: function cache() {
98217 var _apibase = 'https://taginfo.openstreetmap.org/api/4/';
98218 var _inflight = {};
98219 var _popularKeys = {};
98220 var _taginfoCache = {};
98222 point: 'count_nodes',
98223 vertex: 'count_nodes',
98224 area: 'count_ways',
98227 var tag_sort_members = {
98228 point: 'count_node_members',
98229 vertex: 'count_node_members',
98230 area: 'count_way_members',
98231 line: 'count_way_members',
98232 relation: 'count_relation_members'
98234 var tag_filters = {
98240 var tag_members_fractions = {
98241 point: 'count_node_members_fraction',
98242 vertex: 'count_node_members_fraction',
98243 area: 'count_way_members_fraction',
98244 line: 'count_way_members_fraction',
98245 relation: 'count_relation_members_fraction'
98248 function sets(params, n, o) {
98249 if (params.geometry && o[params.geometry]) {
98250 params[n] = o[params.geometry];
98256 function setFilter(params) {
98257 return sets(params, 'filter', tag_filters);
98260 function setSort(params) {
98261 return sets(params, 'sortname', tag_sorts);
98264 function setSortMembers(params) {
98265 return sets(params, 'sortname', tag_sort_members);
98268 function clean(params) {
98269 return utilObjectOmit(params, ['geometry', 'debounce']);
98272 function filterKeys(type) {
98273 var count_type = type ? 'count_' + type : 'count_all';
98274 return function (d) {
98275 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
98279 function filterMultikeys(prefix) {
98280 return function (d) {
98281 // d.key begins with prefix, and d.key contains no additional ':'s
98282 var re = new RegExp('^' + prefix + '(.*)$');
98283 var matches = d.key.match(re) || [];
98284 return matches.length === 2 && matches[1].indexOf(':') === -1;
98288 function filterValues(allowUpperCase) {
98289 return function (d) {
98290 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
98292 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
98294 return parseFloat(d.fraction) > 0.0;
98298 function filterRoles(geometry) {
98299 return function (d) {
98300 if (d.role === '') return false; // exclude empty role
98302 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
98304 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
98308 function valKey(d) {
98315 function valKeyDescription(d) {
98318 title: d.description || d.value
98322 obj.count = d.count;
98328 function roleKey(d) {
98333 } // sort keys with ':' lower than keys without ':'
98336 function sortKeys(a, b) {
98337 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
98340 var debouncedRequest = debounce(request, 300, {
98344 function request(url, params, exactMatch, callback, loaded) {
98345 if (_inflight[url]) return;
98346 if (checkCache(url, params, exactMatch, callback)) return;
98347 var controller = new AbortController();
98348 _inflight[url] = controller;
98350 signal: controller.signal
98351 }).then(function (result) {
98352 delete _inflight[url];
98353 if (loaded) loaded(null, result);
98354 })["catch"](function (err) {
98355 delete _inflight[url];
98356 if (err.name === 'AbortError') return;
98357 if (loaded) loaded(err.message);
98361 function checkCache(url, params, exactMatch, callback) {
98362 var rp = params.rp || 25;
98363 var testQuery = params.query || '';
98367 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
98369 if (hit && (url === testUrl || hit.length < rp)) {
98370 callback(null, hit);
98372 } // don't try to shorten the query
98375 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
98376 // that has returned fewer than max results (rp)
98378 testQuery = testQuery.slice(0, -1);
98379 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
98380 } while (testQuery.length >= 0);
98385 var serviceTaginfo = {
98386 init: function init() {
98388 _taginfoCache = {};
98390 // manually exclude some keys – #5377, #7485
98396 sorting_name: true,
98400 'bridge:name': true
98401 }; // Fetch popular keys. We'll exclude these from `values`
98402 // lookups because they stress taginfo, and they aren't likely
98403 // to yield meaningful autocomplete results.. see #3955
98407 sortname: 'values_all',
98411 lang: _mainLocalizer.languageCode()
98413 this.keys(params, function (err, data) {
98415 data.forEach(function (d) {
98416 if (d.value === 'opening_hours') return; // exception
98418 _popularKeys[d.value] = true;
98422 reset: function reset() {
98423 Object.values(_inflight).forEach(function (controller) {
98424 controller.abort();
98428 keys: function keys(params, callback) {
98429 var doRequest = params.debounce ? debouncedRequest : request;
98430 params = clean(setSort(params));
98431 params = Object.assign({
98433 sortname: 'count_all',
98436 lang: _mainLocalizer.languageCode()
98438 var url = _apibase + 'keys/all?' + utilQsString(params);
98439 doRequest(url, params, false, callback, function (err, d) {
98443 var f = filterKeys(params.filter);
98444 var result = d.data.filter(f).sort(sortKeys).map(valKey);
98445 _taginfoCache[url] = result;
98446 callback(null, result);
98450 multikeys: function multikeys(params, callback) {
98451 var doRequest = params.debounce ? debouncedRequest : request;
98452 params = clean(setSort(params));
98453 params = Object.assign({
98455 sortname: 'count_all',
98458 lang: _mainLocalizer.languageCode()
98460 var prefix = params.query;
98461 var url = _apibase + 'keys/all?' + utilQsString(params);
98462 doRequest(url, params, true, callback, function (err, d) {
98466 var f = filterMultikeys(prefix);
98467 var result = d.data.filter(f).map(valKey);
98468 _taginfoCache[url] = result;
98469 callback(null, result);
98473 values: function values(params, callback) {
98474 // Exclude popular keys from values lookups.. see #3955
98475 var key = params.key;
98477 if (key && _popularKeys[key]) {
98478 callback(null, []);
98482 var doRequest = params.debounce ? debouncedRequest : request;
98483 params = clean(setSort(setFilter(params)));
98484 params = Object.assign({
98486 sortname: 'count_all',
98489 lang: _mainLocalizer.languageCode()
98491 var url = _apibase + 'key/values?' + utilQsString(params);
98492 doRequest(url, params, false, callback, function (err, d) {
98496 // In most cases we prefer taginfo value results with lowercase letters.
98497 // A few OSM keys expect values to contain uppercase values (see #3377).
98498 // This is not an exhaustive list (e.g. `name` also has uppercase values)
98499 // but these are the fields where taginfo value lookup is most useful.
98500 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
98501 var allowUpperCase = re.test(params.key);
98502 var f = filterValues(allowUpperCase);
98503 var result = d.data.filter(f).map(valKeyDescription);
98504 _taginfoCache[url] = result;
98505 callback(null, result);
98509 roles: function roles(params, callback) {
98510 var doRequest = params.debounce ? debouncedRequest : request;
98511 var geometry = params.geometry;
98512 params = clean(setSortMembers(params));
98513 params = Object.assign({
98515 sortname: 'count_all_members',
98518 lang: _mainLocalizer.languageCode()
98520 var url = _apibase + 'relation/roles?' + utilQsString(params);
98521 doRequest(url, params, true, callback, function (err, d) {
98525 var f = filterRoles(geometry);
98526 var result = d.data.filter(f).map(roleKey);
98527 _taginfoCache[url] = result;
98528 callback(null, result);
98532 docs: function docs(params, callback) {
98533 var doRequest = params.debounce ? debouncedRequest : request;
98534 params = clean(setSort(params));
98535 var path = 'key/wiki_pages?';
98537 if (params.value) {
98538 path = 'tag/wiki_pages?';
98539 } else if (params.rtype) {
98540 path = 'relation/wiki_pages?';
98543 var url = _apibase + path + utilQsString(params);
98544 doRequest(url, params, true, callback, function (err, d) {
98548 _taginfoCache[url] = d.data;
98549 callback(null, d.data);
98553 apibase: function apibase(_) {
98554 if (!arguments.length) return _apibase;
98561 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
98564 * @param {Geometry} geometry input geometry
98565 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98566 * @param {Object} [options={}] Optional Parameters
98567 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98568 * @param {string|number} [options.id] Identifier associated with the Feature
98569 * @returns {Feature} a GeoJSON Feature
98573 * "coordinates": [110, 50]
98576 * var feature = turf.feature(geometry);
98581 function feature(geom, properties, options) {
98582 if (options === void 0) {
98590 if (options.id === 0 || options.id) {
98591 feat.id = options.id;
98594 if (options.bbox) {
98595 feat.bbox = options.bbox;
98598 feat.properties = properties || {};
98599 feat.geometry = geom;
98603 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
98606 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
98607 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98608 * @param {Object} [options={}] Optional Parameters
98609 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98610 * @param {string|number} [options.id] Identifier associated with the Feature
98611 * @returns {Feature<Polygon>} Polygon Feature
98613 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
98618 function polygon(coordinates, properties, options) {
98619 if (options === void 0) {
98623 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
98624 var ring = coordinates_1[_i];
98626 if (ring.length < 4) {
98627 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
98630 for (var j = 0; j < ring[ring.length - 1].length; j++) {
98631 // Check if first point of Polygon contains two numbers
98632 if (ring[ring.length - 1][j] !== ring[0][j]) {
98633 throw new Error("First and last Position are not equivalent.");
98640 coordinates: coordinates
98642 return feature(geom, properties, options);
98645 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
98648 * @param {Array<Array<number>>} coordinates an array of Positions
98649 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98650 * @param {Object} [options={}] Optional Parameters
98651 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98652 * @param {string|number} [options.id] Identifier associated with the Feature
98653 * @returns {Feature<LineString>} LineString Feature
98655 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
98656 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
98662 function lineString(coordinates, properties, options) {
98663 if (options === void 0) {
98667 if (coordinates.length < 2) {
98668 throw new Error("coordinates must be an array of two or more positions");
98672 type: "LineString",
98673 coordinates: coordinates
98675 return feature(geom, properties, options);
98678 * Creates a {@link Feature<MultiLineString>} based on a
98679 * coordinate array. Properties can be added optionally.
98681 * @name multiLineString
98682 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
98683 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98684 * @param {Object} [options={}] Optional Parameters
98685 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98686 * @param {string|number} [options.id] Identifier associated with the Feature
98687 * @returns {Feature<MultiLineString>} a MultiLineString feature
98688 * @throws {Error} if no coordinates are passed
98690 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
98695 function multiLineString(coordinates, properties, options) {
98696 if (options === void 0) {
98701 type: "MultiLineString",
98702 coordinates: coordinates
98704 return feature(geom, properties, options);
98707 * Creates a {@link Feature<MultiPolygon>} based on a
98708 * coordinate array. Properties can be added optionally.
98710 * @name multiPolygon
98711 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
98712 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
98713 * @param {Object} [options={}] Optional Parameters
98714 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
98715 * @param {string|number} [options.id] Identifier associated with the Feature
98716 * @returns {Feature<MultiPolygon>} a multipolygon feature
98717 * @throws {Error} if no coordinates are passed
98719 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
98725 function multiPolygon(coordinates, properties, options) {
98726 if (options === void 0) {
98731 type: "MultiPolygon",
98732 coordinates: coordinates
98734 return feature(geom, properties, options);
98738 * Get Geometry from Feature or Geometry Object
98740 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
98741 * @returns {Geometry|null} GeoJSON Geometry Object
98742 * @throws {Error} if geojson is not a Feature or Geometry Object
98745 * "type": "Feature",
98746 * "properties": {},
98749 * "coordinates": [110, 40]
98752 * var geom = turf.getGeom(point)
98753 * //={"type": "Point", "coordinates": [110, 40]}
98756 function getGeom(geojson) {
98757 if (geojson.type === "Feature") {
98758 return geojson.geometry;
98764 // Cohen-Sutherland line clipping algorithm, adapted to efficiently
98765 // handle polylines rather than just segments
98766 function lineclip(points, bbox, result) {
98767 var len = points.length,
98768 codeA = bitCode(points[0], bbox),
98775 if (!result) result = [];
98777 for (i = 1; i < len; i++) {
98780 codeB = lastCode = bitCode(b, bbox);
98783 if (!(codeA | codeB)) {
98787 if (codeB !== lastCode) {
98788 // segment went outside
98792 // start a new line
98796 } else if (i === len - 1) {
98801 } else if (codeA & codeB) {
98804 } else if (codeA) {
98805 // a outside, intersect with clip edge
98806 a = intersect(a, b, codeA, bbox);
98807 codeA = bitCode(a, bbox);
98810 b = intersect(a, b, codeB, bbox);
98811 codeB = bitCode(b, bbox);
98818 if (part.length) result.push(part);
98820 } // Sutherland-Hodgeman polygon clipping algorithm
98822 function polygonclip(points, bbox) {
98823 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
98825 for (edge = 1; edge <= 8; edge *= 2) {
98827 prev = points[points.length - 1];
98828 prevInside = !(bitCode(prev, bbox) & edge);
98830 for (i = 0; i < points.length; i++) {
98832 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
98834 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
98835 if (inside) result.push(p); // add a point if it's inside
98838 prevInside = inside;
98842 if (!points.length) break;
98846 } // intersect a segment against one of the 4 lines that make up the bbox
98848 function intersect(a, b, edge, bbox) {
98849 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] // top
98850 : edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] // bottom
98851 : edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] // right
98852 : edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] // left
98854 } // bit code reflects the point position relative to the bbox:
98856 // top 1001 1000 1010
98857 // mid 0001 0000 0010
98858 // bottom 0101 0100 0110
98861 function bitCode(p, bbox) {
98863 if (p[0] < bbox[0]) code |= 1; // left
98864 else if (p[0] > bbox[2]) code |= 2; // right
98866 if (p[1] < bbox[1]) code |= 4; // bottom
98867 else if (p[1] > bbox[3]) code |= 8; // top
98873 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
98874 * [lineclip](https://github.com/mapbox/lineclip).
98875 * May result in degenerate edges when clipping Polygons.
98878 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
98879 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
98880 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
98882 * var bbox = [0, 0, 10, 10];
98883 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
98885 * var clipped = turf.bboxClip(poly, bbox);
98888 * var addToMap = [bbox, poly, clipped]
98891 function bboxClip(feature, bbox) {
98892 var geom = getGeom(feature);
98893 var type = geom.type;
98894 var properties = feature.type === "Feature" ? feature.properties : {};
98895 var coords = geom.coordinates;
98899 case "MultiLineString":
98902 if (type === "LineString") {
98906 coords.forEach(function (line) {
98907 lineclip(line, bbox, lines_1);
98910 if (lines_1.length === 1) {
98911 return lineString(lines_1[0], properties);
98914 return multiLineString(lines_1, properties);
98917 return polygon(clipPolygon(coords, bbox), properties);
98919 case "MultiPolygon":
98920 return multiPolygon(coords.map(function (poly) {
98921 return clipPolygon(poly, bbox);
98925 throw new Error("geometry " + type + " not supported");
98929 function clipPolygon(rings, bbox) {
98932 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
98933 var ring = rings_1[_i];
98934 var clipped = polygonclip(ring, bbox);
98936 if (clipped.length > 0) {
98937 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
98938 clipped.push(clipped[0]);
98941 if (clipped.length >= 4) {
98942 outRings.push(clipped);
98950 var tiler = utilTiler().tileSize(512).margin(1);
98951 var dispatch = dispatch$8('loadedData');
98955 function abortRequest(controller) {
98956 controller.abort();
98959 function vtToGeoJSON(data, tile, mergeCache) {
98960 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
98961 var layers = Object.keys(vectorTile$1.layers);
98963 if (!Array.isArray(layers)) {
98968 layers.forEach(function (layerID) {
98969 var layer = vectorTile$1.layers[layerID];
98972 for (var i = 0; i < layer.length; i++) {
98973 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
98974 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
98976 if (geometry.type === 'Polygon') {
98977 geometry.type = 'MultiPolygon';
98978 geometry.coordinates = [geometry.coordinates];
98981 var isClipped = false; // Clip to tile bounds
98983 if (geometry.type === 'MultiPolygon') {
98984 var featureClip = bboxClip(feature, tile.extent.rectangle());
98986 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
98987 // feature = featureClip;
98991 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
98993 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
98994 } // Generate some unique IDs and add some metadata
98997 var featurehash = utilHashcode(fastJsonStableStringify(feature));
98998 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
98999 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
99000 feature.__featurehash__ = featurehash;
99001 feature.__propertyhash__ = propertyhash;
99002 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
99004 if (isClipped && geometry.type === 'MultiPolygon') {
99005 var merged = mergeCache[propertyhash];
99007 if (merged && merged.length) {
99008 var other = merged[0];
99009 var coords = index.union(feature.geometry.coordinates, other.geometry.coordinates);
99011 if (!coords || !coords.length) {
99012 continue; // something failed in polygon union
99015 merged.push(feature);
99017 for (var j = 0; j < merged.length; j++) {
99018 // all these features get...
99019 merged[j].geometry.coordinates = coords; // same coords
99021 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
99024 mergeCache[propertyhash] = [feature];
99033 function loadTile(source, tile) {
99034 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
99035 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
99036 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
99037 var subdomains = r.split(',');
99038 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
99040 var controller = new AbortController();
99041 source.inflight[tile.id] = controller;
99043 signal: controller.signal
99044 }).then(function (response) {
99045 if (!response.ok) {
99046 throw new Error(response.status + ' ' + response.statusText);
99049 source.loaded[tile.id] = [];
99050 delete source.inflight[tile.id];
99051 return response.arrayBuffer();
99052 }).then(function (data) {
99054 throw new Error('No Data');
99057 var z = tile.xyz[2];
99059 if (!source.canMerge[z]) {
99060 source.canMerge[z] = {}; // initialize mergeCache
99063 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
99064 dispatch.call('loadedData');
99065 })["catch"](function () {
99066 source.loaded[tile.id] = [];
99067 delete source.inflight[tile.id];
99071 var serviceVectorTile = {
99072 init: function init() {
99077 this.event = utilRebind(this, dispatch, 'on');
99079 reset: function reset() {
99080 for (var sourceID in _vtCache) {
99081 var source = _vtCache[sourceID];
99083 if (source && source.inflight) {
99084 Object.values(source.inflight).forEach(abortRequest);
99090 addSource: function addSource(sourceID, template) {
99091 _vtCache[sourceID] = {
99092 template: template,
99097 return _vtCache[sourceID];
99099 data: function data(sourceID, projection) {
99100 var source = _vtCache[sourceID];
99101 if (!source) return [];
99102 var tiles = tiler.getTiles(projection);
99106 for (var i = 0; i < tiles.length; i++) {
99107 var features = source.loaded[tiles[i].id];
99108 if (!features || !features.length) continue;
99110 for (var j = 0; j < features.length; j++) {
99111 var feature = features[j];
99112 var hash = feature.__featurehash__;
99113 if (seen[hash]) continue;
99114 seen[hash] = true; // return a shallow copy, because the hash may change
99115 // later if this feature gets merged with another
99117 results.push(Object.assign({}, feature)); // shallow copy
99123 loadTiles: function loadTiles(sourceID, template, projection) {
99124 var source = _vtCache[sourceID];
99127 source = this.addSource(sourceID, template);
99130 var tiles = tiler.getTiles(projection); // abort inflight requests that are no longer needed
99132 Object.keys(source.inflight).forEach(function (k) {
99133 var wanted = tiles.find(function (tile) {
99134 return k === tile.id;
99138 abortRequest(source.inflight[k]);
99139 delete source.inflight[k];
99142 tiles.forEach(function (tile) {
99143 loadTile(source, tile);
99146 cache: function cache() {
99151 var apibase = 'https://www.wikidata.org/w/api.php?';
99152 var _wikidataCache = {};
99153 var serviceWikidata = {
99154 init: function init() {},
99155 reset: function reset() {
99156 _wikidataCache = {};
99158 // Search for Wikidata items matching the query
99159 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
99161 if (callback) callback('No query', {});
99165 var lang = this.languagesToQuery()[0];
99166 var url = apibase + utilQsString({
99167 action: 'wbsearchentities',
99172 // the language to search
99174 // the language for the label and description in the result
99179 d3_json(url).then(function (result) {
99180 if (result && result.error) {
99181 throw new Error(result.error);
99184 if (callback) callback(null, result.search || {});
99185 })["catch"](function (err) {
99186 if (callback) callback(err.message, {});
99189 // Given a Wikipedia language and article title,
99190 // return an array of corresponding Wikidata entities.
99191 itemsByTitle: function itemsByTitle(lang, title, callback) {
99193 if (callback) callback('No title', {});
99197 lang = lang || 'en';
99198 var url = apibase + utilQsString({
99199 action: 'wbgetentities',
99202 sites: lang.replace(/-/g, '_') + 'wiki',
99205 // shrink response by filtering to one language
99208 d3_json(url).then(function (result) {
99209 if (result && result.error) {
99210 throw new Error(result.error);
99213 if (callback) callback(null, result.entities || {});
99214 })["catch"](function (err) {
99215 if (callback) callback(err.message, {});
99218 languagesToQuery: function languagesToQuery() {
99219 return _mainLocalizer.localeCodes().map(function (code) {
99220 return code.toLowerCase();
99221 }).filter(function (code) {
99222 // HACK: en-us isn't a wikidata language. We should really be filtering by
99223 // the languages known to be supported by wikidata.
99224 return code !== 'en-us';
99227 entityByQID: function entityByQID(qid, callback) {
99229 callback('No qid', {});
99233 if (_wikidataCache[qid]) {
99234 if (callback) callback(null, _wikidataCache[qid]);
99238 var langs = this.languagesToQuery();
99239 var url = apibase + utilQsString({
99240 action: 'wbgetentities',
99244 props: 'labels|descriptions|claims|sitelinks',
99245 sitefilter: langs.map(function (d) {
99248 languages: langs.join('|'),
99249 languagefallback: 1,
99252 d3_json(url).then(function (result) {
99253 if (result && result.error) {
99254 throw new Error(result.error);
99257 if (callback) callback(null, result.entities[qid] || {});
99258 })["catch"](function (err) {
99259 if (callback) callback(err.message, {});
99262 // Pass `params` object of the form:
99264 // qid: 'string' // brand wikidata (e.g. 'Q37158')
99267 // Get an result object used to display tag documentation
99269 // title: 'string',
99270 // description: 'string',
99271 // editURL: 'string',
99272 // imageURL: 'string',
99273 // wiki: { title: 'string', text: 'string', url: 'string' }
99276 getDocs: function getDocs(params, callback) {
99277 var langs = this.languagesToQuery();
99278 this.entityByQID(params.qid, function (err, entity) {
99279 if (err || !entity) {
99280 callback(err || 'No entity');
99288 var code = langs[i];
99290 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
99291 description = entity.descriptions[code];
99296 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
99300 description: description ? description.value : '',
99301 descriptionLocaleCode: description ? description.language : '',
99302 editURL: 'https://www.wikidata.org/wiki/' + entity.id
99305 if (entity.claims) {
99306 var imageroot = 'https://commons.wikimedia.org/w/index.php';
99307 var props = ['P154', 'P18']; // logo image, image
99311 for (i = 0; i < props.length; i++) {
99312 prop = entity.claims[props[i]];
99314 if (prop && Object.keys(prop).length > 0) {
99315 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
99318 result.imageURL = imageroot + '?' + utilQsString({
99319 title: 'Special:Redirect/file/' + image,
99328 if (entity.sitelinks) {
99329 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
99331 for (i = 0; i < langs.length; i++) {
99332 // check each, in order of preference
99333 var w = langs[i] + 'wiki';
99335 if (entity.sitelinks[w]) {
99336 var title = entity.sitelinks[w].title;
99337 var tKey = 'inspector.wiki_reference';
99339 if (!englishLocale && langs[i] === 'en') {
99340 // user's locale isn't English but
99341 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
99347 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
99354 callback(null, result);
99359 var endpoint = 'https://en.wikipedia.org/w/api.php?';
99360 var serviceWikipedia = {
99361 init: function init() {},
99362 reset: function reset() {},
99363 search: function search(lang, query, callback) {
99365 if (callback) callback('No Query', []);
99369 lang = lang || 'en';
99370 var url = endpoint.replace('en', lang) + utilQsString({
99374 srinfo: 'suggestion',
99379 d3_json(url).then(function (result) {
99380 if (result && result.error) {
99381 throw new Error(result.error);
99382 } else if (!result || !result.query || !result.query.search) {
99383 throw new Error('No Results');
99387 var titles = result.query.search.map(function (d) {
99390 callback(null, titles);
99392 })["catch"](function (err) {
99393 if (callback) callback(err, []);
99396 suggestions: function suggestions(lang, query, callback) {
99398 if (callback) callback('', []);
99402 lang = lang || 'en';
99403 var url = endpoint.replace('en', lang) + utilQsString({
99404 action: 'opensearch',
99411 d3_json(url).then(function (result) {
99412 if (result && result.error) {
99413 throw new Error(result.error);
99414 } else if (!result || result.length < 2) {
99415 throw new Error('No Results');
99418 if (callback) callback(null, result[1] || []);
99419 })["catch"](function (err) {
99420 if (callback) callback(err.message, []);
99423 translations: function translations(lang, title, callback) {
99425 if (callback) callback('No Title');
99429 var url = endpoint.replace('en', lang) + utilQsString({
99437 d3_json(url).then(function (result) {
99438 if (result && result.error) {
99439 throw new Error(result.error);
99440 } else if (!result || !result.query || !result.query.pages) {
99441 throw new Error('No Results');
99445 var list = result.query.pages[Object.keys(result.query.pages)[0]];
99446 var translations = {};
99448 if (list && list.langlinks) {
99449 list.langlinks.forEach(function (d) {
99450 translations[d.lang] = d['*'];
99454 callback(null, translations);
99456 })["catch"](function (err) {
99457 if (callback) callback(err.message);
99463 geocoder: serviceNominatim,
99464 keepRight: serviceKeepRight,
99465 improveOSM: serviceImproveOSM,
99466 osmose: serviceOsmose,
99467 mapillary: serviceMapillary,
99469 openstreetcam: serviceOpenstreetcam,
99471 osmWikibase: serviceOsmWikibase,
99472 maprules: serviceMapRules,
99473 streetside: serviceStreetside,
99474 taginfo: serviceTaginfo,
99475 vectorTile: serviceVectorTile,
99476 wikidata: serviceWikidata,
99477 wikipedia: serviceWikipedia
99480 function modeDragNote(context) {
99485 var edit = behaviorEdit(context);
99487 var _nudgeInterval;
99491 var _note; // most current note.. dragged note may have stale datum.
99494 function startNudge(d3_event, nudge) {
99495 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
99496 _nudgeInterval = window.setInterval(function () {
99497 context.map().pan(nudge);
99498 doMove(d3_event, nudge);
99502 function stopNudge() {
99503 if (_nudgeInterval) {
99504 window.clearInterval(_nudgeInterval);
99505 _nudgeInterval = null;
99509 function origin(note) {
99510 return context.projection(note.loc);
99513 function start(d3_event, note) {
99515 var osm = services.osm;
99518 // Get latest note from cache.. The marker may have a stale datum bound to it
99519 // and dragging it around can sometimes delete the users note comment.
99520 _note = osm.getNote(_note.id);
99523 context.surface().selectAll('.note-' + _note.id).classed('active', true);
99524 context.perform(actionNoop());
99525 context.enter(mode);
99526 context.selectedNoteID(_note.id);
99529 function move(d3_event, entity, point) {
99530 d3_event.stopPropagation();
99531 _lastLoc = context.projection.invert(point);
99533 var nudge = geoViewportEdge(point, context.map().dimensions());
99536 startNudge(d3_event, nudge);
99542 function doMove(d3_event, nudge) {
99543 nudge = nudge || [0, 0];
99544 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
99545 var currMouse = geoVecSubtract(currPoint, nudge);
99546 var loc = context.projection.invert(currMouse);
99547 _note = _note.move(loc);
99548 var osm = services.osm;
99551 osm.replaceNote(_note); // update note cache
99554 context.replace(actionNoop()); // trigger redraw
99558 context.replace(actionNoop()); // trigger redraw
99560 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
99563 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);
99565 mode.enter = function () {
99566 context.install(edit);
99569 mode.exit = function () {
99570 context.ui().sidebar.hover.cancel();
99571 context.uninstall(edit);
99572 context.surface().selectAll('.active').classed('active', false);
99576 mode.behavior = drag;
99580 function modeSelectData(context, selectedDatum) {
99585 var keybinding = utilKeybinding('select-data');
99586 var dataEditor = uiDataEditor(context);
99587 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
99589 function selectData(d3_event, drawn) {
99590 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
99592 if (selection.empty()) {
99593 // Return to browse mode if selected DOM elements have
99594 // disappeared because the user moved them out of view..
99595 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
99597 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
99598 context.enter(modeBrowse(context));
99601 selection.classed('selected', true);
99606 if (context.container().select('.combobox').size()) return;
99607 context.enter(modeBrowse(context));
99610 mode.zoomToSelected = function () {
99611 var extent = geoExtent(d3_geoBounds(selectedDatum));
99612 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
99615 mode.enter = function () {
99616 behaviors.forEach(context.install);
99617 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
99618 select(document).call(keybinding);
99620 var sidebar = context.ui().sidebar;
99621 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
99623 var extent = geoExtent(d3_geoBounds(selectedDatum));
99624 sidebar.expand(sidebar.intersects(extent));
99625 context.map().on('drawn.select-data', selectData);
99628 mode.exit = function () {
99629 behaviors.forEach(context.uninstall);
99630 select(document).call(keybinding.unbind);
99631 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
99632 context.map().on('drawn.select-data', null);
99633 context.ui().sidebar.hide();
99639 function behaviorSelect(context) {
99640 var _tolerancePx = 4; // see also behaviorDrag
99642 var _lastMouseEvent = null;
99643 var _showMenu = false;
99644 var _downPointers = {};
99645 var _longPressTimeout = null;
99646 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
99648 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
99650 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
99652 function keydown(d3_event) {
99653 if (d3_event.keyCode === 32) {
99654 // don't react to spacebar events during text input
99655 var activeNode = document.activeElement;
99656 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
99659 if (d3_event.keyCode === 93 || // context menu key
99660 d3_event.keyCode === 32) {
99662 d3_event.preventDefault();
99665 if (d3_event.repeat) return; // ignore repeated events for held keys
99666 // if any key is pressed the user is probably doing something other than long-pressing
99670 if (d3_event.shiftKey) {
99671 context.surface().classed('behavior-multiselect', true);
99674 if (d3_event.keyCode === 32) {
99676 if (!_downPointers.spacebar && _lastMouseEvent) {
99678 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
99679 _downPointers.spacebar = {
99680 firstEvent: _lastMouseEvent,
99681 lastEvent: _lastMouseEvent
99687 function keyup(d3_event) {
99690 if (!d3_event.shiftKey) {
99691 context.surface().classed('behavior-multiselect', false);
99694 if (d3_event.keyCode === 93) {
99695 // context menu key
99696 d3_event.preventDefault();
99697 _lastInteractionType = 'menukey';
99698 contextmenu(d3_event);
99699 } else if (d3_event.keyCode === 32) {
99701 var pointer = _downPointers.spacebar;
99704 delete _downPointers.spacebar;
99705 if (pointer.done) return;
99706 d3_event.preventDefault();
99707 _lastInteractionType = 'spacebar';
99708 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
99713 function pointerdown(d3_event) {
99714 var id = (d3_event.pointerId || 'mouse').toString();
99716 if (d3_event.buttons && d3_event.buttons !== 1) return;
99717 context.ui().closeEditMenu();
99718 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
99719 _downPointers[id] = {
99720 firstEvent: d3_event,
99721 lastEvent: d3_event
99725 function didLongPress(id, interactionType) {
99726 var pointer = _downPointers[id];
99727 if (!pointer) return;
99729 for (var i in _downPointers) {
99730 // don't allow this or any currently down pointer to trigger another click
99731 _downPointers[i].done = true;
99732 } // treat long presses like right-clicks
99735 _longPressTimeout = null;
99736 _lastInteractionType = interactionType;
99738 click(pointer.firstEvent, pointer.lastEvent, id);
99741 function pointermove(d3_event) {
99742 var id = (d3_event.pointerId || 'mouse').toString();
99744 if (_downPointers[id]) {
99745 _downPointers[id].lastEvent = d3_event;
99748 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
99749 _lastMouseEvent = d3_event;
99751 if (_downPointers.spacebar) {
99752 _downPointers.spacebar.lastEvent = d3_event;
99757 function pointerup(d3_event) {
99758 var id = (d3_event.pointerId || 'mouse').toString();
99759 var pointer = _downPointers[id];
99760 if (!pointer) return;
99761 delete _downPointers[id];
99763 if (_multiselectionPointerId === id) {
99764 _multiselectionPointerId = null;
99767 if (pointer.done) return;
99768 click(pointer.firstEvent, d3_event, id);
99771 function pointercancel(d3_event) {
99772 var id = (d3_event.pointerId || 'mouse').toString();
99773 if (!_downPointers[id]) return;
99774 delete _downPointers[id];
99776 if (_multiselectionPointerId === id) {
99777 _multiselectionPointerId = null;
99781 function contextmenu(d3_event) {
99782 d3_event.preventDefault();
99784 if (!+d3_event.clientX && !+d3_event.clientY) {
99785 if (_lastMouseEvent) {
99786 d3_event.sourceEvent = _lastMouseEvent;
99791 _lastMouseEvent = d3_event;
99792 _lastInteractionType = 'rightclick';
99796 click(d3_event, d3_event);
99799 function click(firstEvent, lastEvent, pointerId) {
99801 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
99802 // are transformed when drag-panning.
99804 var pointGetter = utilFastMouse(mapNode);
99805 var p1 = pointGetter(firstEvent);
99806 var p2 = pointGetter(lastEvent);
99807 var dist = geoVecLength(p1, p2);
99809 if (dist > _tolerancePx || !mapContains(lastEvent)) {
99814 var targetDatum = lastEvent.target.__data__;
99815 var multiselectEntityId;
99817 if (!_multiselectionPointerId) {
99818 // If a different pointer than the one triggering this click is down on a
99819 // feature, treat this and all future clicks as multiselection until that
99820 // pointer is raised.
99821 var selectPointerInfo = pointerDownOnSelection(pointerId);
99823 if (selectPointerInfo) {
99824 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
99826 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
99827 _downPointers[selectPointerInfo.pointerId].done = true;
99829 } // support multiselect if data is already selected
99832 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
99833 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
99834 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
99835 _multiselectionPointerId && !multiselectEntityId);
99837 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
99839 function mapContains(event) {
99840 var rect = mapNode.getBoundingClientRect();
99841 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
99844 function pointerDownOnSelection(skipPointerId) {
99845 var mode = context.mode();
99846 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
99848 for (var pointerId in _downPointers) {
99849 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
99850 var pointerInfo = _downPointers[pointerId];
99851 var p1 = pointGetter(pointerInfo.firstEvent);
99852 var p2 = pointGetter(pointerInfo.lastEvent);
99853 if (geoVecLength(p1, p2) > _tolerancePx) continue;
99854 var datum = pointerInfo.firstEvent.target.__data__;
99855 var entity = datum && datum.properties && datum.properties.entity || datum;
99857 if (context.graph().hasEntity(entity.id)) {
99859 pointerId: pointerId,
99860 entityId: entity.id,
99861 selected: selectedIDs.indexOf(entity.id) !== -1
99870 function processClick(datum, isMultiselect, point, alsoSelectId) {
99871 var mode = context.mode();
99872 var showMenu = _showMenu;
99873 var interactionType = _lastInteractionType;
99874 var entity = datum && datum.properties && datum.properties.entity;
99875 if (entity) datum = entity;
99877 if (datum && datum.type === 'midpoint') {
99878 // treat targeting midpoints as if targeting the parent way
99879 datum = datum.parents[0];
99884 if (datum instanceof osmEntity) {
99885 // targeting an entity
99886 var selectedIDs = context.selectedIDs();
99887 context.selectedNoteID(null);
99888 context.selectedErrorID(null);
99890 if (!isMultiselect) {
99891 // don't change the selection if we're toggling the menu atop a multiselection
99892 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
99893 if (alsoSelectId === datum.id) alsoSelectId = null;
99894 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
99895 // selected since listeners may expect `context.enter` events,
99896 // e.g. in the walkthrough
99898 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
99899 context.enter(newMode);
99902 if (selectedIDs.indexOf(datum.id) !== -1) {
99903 // clicked entity is already in the selectedIDs list..
99905 // deselect clicked entity, then reenter select mode or return to browse mode..
99906 selectedIDs = selectedIDs.filter(function (id) {
99907 return id !== datum.id;
99909 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
99910 context.enter(newMode);
99913 // clicked entity is not in the selected list, add it..
99914 selectedIDs = selectedIDs.concat([datum.id]);
99915 newMode = mode.selectedIDs(selectedIDs);
99916 context.enter(newMode);
99919 } else if (datum && datum.__featurehash__ && !isMultiselect) {
99920 // targeting custom data
99921 context.selectedNoteID(null).enter(modeSelectData(context, datum));
99922 } else if (datum instanceof osmNote && !isMultiselect) {
99923 // targeting a note
99924 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
99925 } else if (datum instanceof QAItem & !isMultiselect) {
99926 // targeting an external QA issue
99927 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
99929 // targeting nothing
99930 context.selectedNoteID(null);
99931 context.selectedErrorID(null);
99933 if (!isMultiselect && mode.id !== 'browse') {
99934 context.enter(modeBrowse(context));
99938 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
99940 if (showMenu) context.ui().showEditMenu(point, interactionType);
99944 function cancelLongPress() {
99945 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
99946 _longPressTimeout = null;
99949 function resetProperties() {
99952 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
99955 function behavior(selection) {
99957 _lastMouseEvent = context.map().lastPointerEvent();
99958 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 (d3_event) {
99959 // Edge and IE really like to show the contextmenu on the
99960 // menubar when user presses a keyboard menu button
99961 // even after we've already preventdefaulted the key event.
99964 if (+e.clientX === 0 && +e.clientY === 0) {
99965 d3_event.preventDefault();
99968 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
99969 /*if (d3_event && d3_event.shiftKey) {
99971 .classed('behavior-multiselect', true);
99975 behavior.off = function (selection) {
99977 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);
99978 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
99979 context.surface().classed('behavior-multiselect', false);
99985 function operationContinue(context, selectedIDs) {
99986 var _entities = selectedIDs.map(function (id) {
99987 return context.graph().entity(id);
99990 var _geometries = Object.assign({
99993 }, utilArrayGroupBy(_entities, function (entity) {
99994 return entity.geometry(context.graph());
99997 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
99999 function candidateWays() {
100000 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
100001 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
100005 var _candidates = candidateWays();
100007 var operation = function operation() {
100008 var candidate = _candidates[0];
100009 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
100012 operation.relatedEntityIds = function () {
100013 return _candidates.length ? [_candidates[0].id] : [];
100016 operation.available = function () {
100017 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
100020 operation.disabled = function () {
100021 if (_candidates.length === 0) {
100023 } else if (_candidates.length > 1) {
100030 operation.tooltip = function () {
100031 var disable = operation.disabled();
100032 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
100035 operation.annotation = function () {
100036 return _t('operations.continue.annotation.line');
100039 operation.id = 'continue';
100040 operation.keys = [_t('operations.continue.key')];
100041 operation.title = _t('operations.continue.title');
100042 operation.behavior = behaviorOperation(context).which(operation);
100046 function operationCopy(context, selectedIDs) {
100047 function getFilteredIdsToCopy() {
100048 return selectedIDs.filter(function (selectedID) {
100049 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
100051 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
100055 var operation = function operation() {
100056 var graph = context.graph();
100057 var selected = groupEntities(getFilteredIdsToCopy(), graph);
100063 for (i = 0; i < selected.relation.length; i++) {
100064 entity = selected.relation[i];
100066 if (!skip[entity.id] && entity.isComplete(graph)) {
100067 canCopy.push(entity.id);
100068 skip = getDescendants(entity.id, graph, skip);
100072 for (i = 0; i < selected.way.length; i++) {
100073 entity = selected.way[i];
100075 if (!skip[entity.id]) {
100076 canCopy.push(entity.id);
100077 skip = getDescendants(entity.id, graph, skip);
100081 for (i = 0; i < selected.node.length; i++) {
100082 entity = selected.node[i];
100084 if (!skip[entity.id]) {
100085 canCopy.push(entity.id);
100089 context.copyIDs(canCopy);
100091 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
100092 // store the anchor coordinates if copying more than a single node
100093 context.copyLonLat(context.projection.invert(_point));
100095 context.copyLonLat(null);
100099 function groupEntities(ids, graph) {
100100 var entities = ids.map(function (id) {
100101 return graph.entity(id);
100107 }, utilArrayGroupBy(entities, 'type'));
100110 function getDescendants(id, graph, descendants) {
100111 var entity = graph.entity(id);
100113 descendants = descendants || {};
100115 if (entity.type === 'relation') {
100116 children = entity.members.map(function (m) {
100119 } else if (entity.type === 'way') {
100120 children = entity.nodes;
100125 for (var i = 0; i < children.length; i++) {
100126 if (!descendants[children[i]]) {
100127 descendants[children[i]] = true;
100128 descendants = getDescendants(children[i], graph, descendants);
100135 operation.available = function () {
100136 return getFilteredIdsToCopy().length > 0;
100139 operation.disabled = function () {
100140 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
100142 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
100149 operation.availableForKeypress = function () {
100150 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
100152 return !selection || !selection.toString();
100155 operation.tooltip = function () {
100156 var disable = operation.disabled();
100157 return disable ? _t('operations.copy.' + disable, {
100159 }) : _t('operations.copy.description', {
100164 operation.annotation = function () {
100165 return _t('operations.copy.annotation', {
100172 operation.point = function (val) {
100178 operation.keys = [uiCmd('⌘C')];
100179 operation.title = _t('operations.copy.title');
100180 operation.behavior = behaviorOperation(context).which(operation);
100184 function operationDisconnect(context, selectedIDs) {
100189 selectedIDs.forEach(function (id) {
100190 var entity = context.entity(id);
100192 if (entity.type === 'way') {
100194 } else if (entity.geometry(context.graph()) === 'vertex') {
100203 _annotationID = 'features';
100205 var _disconnectingVertexIds = [];
100206 var _disconnectingWayIds = [];
100208 if (_vertexIDs.length > 0) {
100209 // At the selected vertices, disconnect the selected ways, if any, else
100210 // disconnect all connected ways
100211 _disconnectingVertexIds = _vertexIDs;
100213 _vertexIDs.forEach(function (vertexID) {
100214 var action = actionDisconnect(vertexID);
100216 if (_wayIDs.length > 0) {
100217 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
100218 var way = context.entity(wayID);
100219 return way.nodes.indexOf(vertexID) !== -1;
100222 action.limitWays(waysIDsForVertex);
100227 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
100232 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
100233 return _wayIDs.indexOf(id) === -1;
100235 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
100237 if (_wayIDs.length === 1) {
100238 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
100240 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
100242 } else if (_wayIDs.length > 0) {
100243 // Disconnect the selected ways from each other, if they're connected,
100244 // else disconnect them from all connected ways
100245 var ways = _wayIDs.map(function (id) {
100246 return context.entity(id);
100249 var nodes = utilGetAllNodes(_wayIDs, context.graph());
100250 _coords = nodes.map(function (n) {
100252 }); // actions for connected nodes shared by at least two selected ways
100254 var sharedActions = [];
100255 var sharedNodes = []; // actions for connected nodes
100257 var unsharedActions = [];
100258 var unsharedNodes = [];
100259 nodes.forEach(function (node) {
100260 var action = actionDisconnect(node.id).limitWays(_wayIDs);
100262 if (action.disabled(context.graph()) !== 'not_connected') {
100268 if (way.nodes.indexOf(node.id) !== -1) {
100276 sharedActions.push(action);
100277 sharedNodes.push(node);
100279 unsharedActions.push(action);
100280 unsharedNodes.push(node);
100284 _descriptionID += 'no_points.';
100285 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
100287 if (sharedActions.length) {
100288 // if any nodes are shared, only disconnect the selected ways from each other
100289 _actions = sharedActions;
100290 _disconnectingVertexIds = sharedNodes.map(function (node) {
100293 _descriptionID += 'conjoined';
100294 _annotationID = 'from_each_other';
100296 // if no nodes are shared, disconnect the selected ways from all connected ways
100297 _actions = unsharedActions;
100298 _disconnectingVertexIds = unsharedNodes.map(function (node) {
100302 if (_wayIDs.length === 1) {
100303 _descriptionID += context.graph().geometry(_wayIDs[0]);
100305 _descriptionID += 'separate';
100310 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
100312 var operation = function operation() {
100313 context.perform(function (graph) {
100314 return _actions.reduce(function (graph, action) {
100317 }, operation.annotation());
100318 context.validator().validate();
100321 operation.relatedEntityIds = function () {
100322 if (_vertexIDs.length) {
100323 return _disconnectingWayIds;
100326 return _disconnectingVertexIds;
100329 operation.available = function () {
100330 if (_actions.length === 0) return false;
100331 if (_otherIDs.length !== 0) return false;
100332 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
100333 return _vertexIDs.some(function (vertexID) {
100334 var way = context.entity(wayID);
100335 return way.nodes.indexOf(vertexID) !== -1;
100341 operation.disabled = function () {
100344 for (var actionIndex in _actions) {
100345 reason = _actions[actionIndex].disabled(context.graph());
100346 if (reason) return reason;
100349 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
100350 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
100351 } else if (_coords && someMissing()) {
100352 return 'not_downloaded';
100353 } else if (selectedIDs.some(context.hasHiddenConnections)) {
100354 return 'connected_to_hidden';
100359 function someMissing() {
100360 if (context.inIntro()) return false;
100361 var osm = context.connection();
100364 var missing = _coords.filter(function (loc) {
100365 return !osm.isDataLoaded(loc);
100369 missing.forEach(function (loc) {
100370 context.loadTileAtLoc(loc);
100380 operation.tooltip = function () {
100381 var disable = operation.disabled();
100384 return _t('operations.disconnect.' + disable);
100387 return _t('operations.disconnect.description.' + _descriptionID);
100390 operation.annotation = function () {
100391 return _t('operations.disconnect.annotation.' + _annotationID);
100394 operation.id = 'disconnect';
100395 operation.keys = [_t('operations.disconnect.key')];
100396 operation.title = _t('operations.disconnect.title');
100397 operation.behavior = behaviorOperation(context).which(operation);
100401 function operationDowngrade(context, selectedIDs) {
100402 var _affectedFeatureCount = 0;
100404 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
100406 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
100408 function downgradeTypeForEntityIDs(entityIds) {
100410 _affectedFeatureCount = 0;
100412 for (var i in entityIds) {
100413 var entityID = entityIds[i];
100414 var type = downgradeTypeForEntityID(entityID);
100417 _affectedFeatureCount += 1;
100419 if (downgradeType && type !== downgradeType) {
100420 if (downgradeType !== 'generic' && type !== 'generic') {
100421 downgradeType = 'building_address';
100423 downgradeType = 'generic';
100434 function downgradeTypeForEntityID(entityID) {
100435 var graph = context.graph();
100436 var entity = graph.entity(entityID);
100437 var preset = _mainPresetIndex.match(entity, graph);
100438 if (!preset || preset.isFallback()) return null;
100440 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
100441 return key.match(/^addr:.{1,}/);
100446 var geometry = entity.geometry(graph);
100448 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
100452 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
100459 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
100460 var addressKeysToKeep = ['source'];
100462 var operation = function operation() {
100463 context.perform(function (graph) {
100464 for (var i in selectedIDs) {
100465 var entityID = selectedIDs[i];
100466 var type = downgradeTypeForEntityID(entityID);
100468 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
100470 for (var key in tags) {
100471 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
100473 if (type === 'building') {
100474 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
100477 if (type !== 'generic') {
100478 if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
100484 graph = actionChangeTags(entityID, tags)(graph);
100488 }, operation.annotation());
100489 context.validator().validate(); // refresh the select mode to enable the delete operation
100491 context.enter(modeSelect(context, selectedIDs));
100494 operation.available = function () {
100498 operation.disabled = function () {
100499 if (selectedIDs.some(hasWikidataTag)) {
100500 return 'has_wikidata_tag';
100505 function hasWikidataTag(id) {
100506 var entity = context.entity(id);
100507 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
100511 operation.tooltip = function () {
100512 var disable = operation.disabled();
100513 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
100516 operation.annotation = function () {
100519 if (_downgradeType === 'building_address') {
100522 suffix = _downgradeType;
100525 return _t('operations.downgrade.annotation.' + suffix, {
100526 n: _affectedFeatureCount
100530 operation.id = 'downgrade';
100531 operation.keys = [uiCmd('⌫')];
100532 operation.title = _t('operations.downgrade.title');
100533 operation.behavior = behaviorOperation(context).which(operation);
100537 function operationExtract(context, selectedIDs) {
100538 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
100540 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
100541 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
100544 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
100548 var _actions = selectedIDs.map(function (entityID) {
100549 var graph = context.graph();
100550 var entity = graph.hasEntity(entityID);
100551 if (!entity || !entity.hasInterestingTags()) return null;
100552 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
100554 if (entity.type !== 'node') {
100555 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
100557 if (preset.geometry.indexOf('point') === -1) return null;
100560 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
100561 return actionExtract(entityID, context.projection);
100564 var operation = function operation() {
100565 var combinedAction = function combinedAction(graph) {
100566 _actions.forEach(function (action) {
100573 context.perform(combinedAction, operation.annotation()); // do the extract
100575 var extractedNodeIDs = _actions.map(function (action) {
100576 return action.getExtractedNodeID();
100579 context.enter(modeSelect(context, extractedNodeIDs));
100582 operation.available = function () {
100583 return _actions.length && selectedIDs.length === _actions.length;
100586 operation.disabled = function () {
100587 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
100589 } else if (selectedIDs.some(function (entityID) {
100590 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
100592 return 'connected_to_hidden';
100598 operation.tooltip = function () {
100599 var disableReason = operation.disabled();
100602 return _t('operations.extract.' + disableReason + '.' + _amount);
100604 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
100608 operation.annotation = function () {
100609 return _t('operations.extract.annotation', {
100614 operation.id = 'extract';
100615 operation.keys = [_t('operations.extract.key')];
100616 operation.title = _t('operations.extract.title');
100617 operation.behavior = behaviorOperation(context).which(operation);
100621 function operationMerge(context, selectedIDs) {
100622 var _action = getAction();
100625 // prefer a non-disabled action first
100626 var join = actionJoin(selectedIDs);
100627 if (!join.disabled(context.graph())) return join;
100628 var merge = actionMerge(selectedIDs);
100629 if (!merge.disabled(context.graph())) return merge;
100630 var mergePolygon = actionMergePolygon(selectedIDs);
100631 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
100632 var mergeNodes = actionMergeNodes(selectedIDs);
100633 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
100635 if (join.disabled(context.graph()) !== 'not_eligible') return join;
100636 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
100637 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
100641 var operation = function operation() {
100642 if (operation.disabled()) return;
100643 context.perform(_action, operation.annotation());
100644 context.validator().validate();
100645 var resultIDs = selectedIDs.filter(context.hasEntity);
100647 if (resultIDs.length > 1) {
100648 var interestingIDs = resultIDs.filter(function (id) {
100649 return context.entity(id).hasInterestingTags();
100651 if (interestingIDs.length) resultIDs = interestingIDs;
100654 context.enter(modeSelect(context, resultIDs));
100657 operation.available = function () {
100658 return selectedIDs.length >= 2;
100661 operation.disabled = function () {
100662 var actionDisabled = _action.disabled(context.graph());
100664 if (actionDisabled) return actionDisabled;
100665 var osm = context.connection();
100667 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
100668 return 'too_many_vertices';
100674 operation.tooltip = function () {
100675 var disabled = operation.disabled();
100678 if (disabled === 'restriction') {
100679 return _t('operations.merge.restriction', {
100680 relation: _mainPresetIndex.item('type/restriction').name()
100684 return _t('operations.merge.' + disabled);
100687 return _t('operations.merge.description');
100690 operation.annotation = function () {
100691 return _t('operations.merge.annotation', {
100696 operation.id = 'merge';
100697 operation.keys = [_t('operations.merge.key')];
100698 operation.title = _t('operations.merge.title');
100699 operation.behavior = behaviorOperation(context).which(operation);
100703 function operationPaste(context) {
100706 var operation = function operation() {
100707 if (!_pastePoint) return;
100708 var oldIDs = context.copyIDs();
100709 if (!oldIDs.length) return;
100710 var projection = context.projection;
100711 var extent = geoExtent();
100712 var oldGraph = context.copyGraph();
100714 var action = actionCopyEntities(oldIDs, oldGraph);
100715 context.perform(action);
100716 var copies = action.copies();
100717 var originals = new Set();
100718 Object.values(copies).forEach(function (entity) {
100719 originals.add(entity.id);
100722 for (var id in copies) {
100723 var oldEntity = oldGraph.entity(id);
100724 var newEntity = copies[id];
100726 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
100729 var parents = context.graph().parentWays(newEntity);
100730 var parentCopied = parents.some(function (parent) {
100731 return originals.has(parent.id);
100735 newIDs.push(newEntity.id);
100737 } // Use the location of the copy operation to offset the paste location,
100738 // or else use the center of the pasted extent
100741 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
100742 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
100744 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
100745 context.enter(modeSelect(context, newIDs));
100748 operation.point = function (val) {
100753 operation.available = function () {
100754 return context.mode().id === 'browse';
100757 operation.disabled = function () {
100758 return !context.copyIDs().length;
100761 operation.tooltip = function () {
100762 var oldGraph = context.copyGraph();
100763 var ids = context.copyIDs();
100766 return _t('operations.paste.nothing_copied');
100769 return _t('operations.paste.description', {
100770 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
100775 operation.annotation = function () {
100776 var ids = context.copyIDs();
100777 return _t('operations.paste.annotation', {
100782 operation.id = 'paste';
100783 operation.keys = [uiCmd('⌘V')];
100784 operation.title = _t('operations.paste.title');
100788 function operationReverse(context, selectedIDs) {
100789 var operation = function operation() {
100790 context.perform(function combinedReverseAction(graph) {
100791 actions().forEach(function (action) {
100795 }, operation.annotation());
100796 context.validator().validate();
100799 function actions(situation) {
100800 return selectedIDs.map(function (entityID) {
100801 var entity = context.hasEntity(entityID);
100802 if (!entity) return null;
100804 if (situation === 'toolbar') {
100805 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
100808 var geometry = entity.geometry(context.graph());
100809 if (entity.type !== 'node' && geometry !== 'line') return null;
100810 var action = actionReverse(entityID);
100811 if (action.disabled(context.graph())) return null;
100816 function reverseTypeID() {
100818 var nodeActionCount = acts.filter(function (act) {
100819 var entity = context.hasEntity(act.entityID());
100820 return entity && entity.type === 'node';
100822 if (nodeActionCount === 0) return 'line';
100823 if (nodeActionCount === acts.length) return 'point';
100827 operation.available = function (situation) {
100828 return actions(situation).length > 0;
100831 operation.disabled = function () {
100835 operation.tooltip = function () {
100836 return _t('operations.reverse.description.' + reverseTypeID());
100839 operation.annotation = function () {
100841 return _t('operations.reverse.annotation.' + reverseTypeID(), {
100846 operation.id = 'reverse';
100847 operation.keys = [_t('operations.reverse.key')];
100848 operation.title = _t('operations.reverse.title');
100849 operation.behavior = behaviorOperation(context).which(operation);
100853 function operationSplit(context, selectedIDs) {
100854 var _vertexIds = selectedIDs.filter(function (id) {
100855 return context.graph().geometry(id) === 'vertex';
100858 var _selectedWayIds = selectedIDs.filter(function (id) {
100859 var entity = context.graph().hasEntity(id);
100860 return entity && entity.type === 'way';
100863 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
100865 var _action = actionSplit(_vertexIds);
100868 var _geometry = 'feature';
100869 var _waysAmount = 'single';
100871 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
100874 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
100875 _ways = _action.ways(context.graph());
100878 _ways.forEach(function (way) {
100879 geometries[way.geometry(context.graph())] = true;
100882 if (Object.keys(geometries).length === 1) {
100883 _geometry = Object.keys(geometries)[0];
100886 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
100889 var operation = function operation() {
100890 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
100892 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
100893 // filter out relations that may have had member additions
100894 return context.entity(id).type === 'way';
100897 context.enter(modeSelect(context, idsToSelect));
100900 operation.relatedEntityIds = function () {
100901 return _selectedWayIds.length ? [] : _ways.map(function (way) {
100906 operation.available = function () {
100910 operation.disabled = function () {
100911 var reason = _action.disabled(context.graph());
100915 } else if (selectedIDs.some(context.hasHiddenConnections)) {
100916 return 'connected_to_hidden';
100922 operation.tooltip = function () {
100923 var disable = operation.disabled();
100924 if (disable) return _t('operations.split.' + disable);
100925 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
100928 operation.annotation = function () {
100929 return _t('operations.split.annotation.' + _geometry, {
100934 operation.id = 'split';
100935 operation.keys = [_t('operations.split.key')];
100936 operation.title = _t('operations.split.title');
100937 operation.behavior = behaviorOperation(context).which(operation);
100941 function operationStraighten(context, selectedIDs) {
100942 var _wayIDs = selectedIDs.filter(function (id) {
100943 return id.charAt(0) === 'w';
100946 var _nodeIDs = selectedIDs.filter(function (id) {
100947 return id.charAt(0) === 'n';
100950 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
100952 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
100954 var _coords = _nodes.map(function (n) {
100958 var _extent = utilTotalExtent(selectedIDs, context.graph());
100960 var _action = chooseAction();
100964 function chooseAction() {
100965 // straighten selected nodes
100966 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
100968 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
100969 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
100973 for (var i = 0; i < selectedIDs.length; i++) {
100974 var entity = context.entity(selectedIDs[i]);
100976 if (entity.type === 'node') {
100978 } else if (entity.type !== 'way' || entity.isClosed()) {
100979 return null; // exit early, can't straighten these
100982 startNodeIDs.push(entity.first());
100983 endNodeIDs.push(entity.last());
100984 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
100987 startNodeIDs = startNodeIDs.filter(function (n) {
100988 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
100990 endNodeIDs = endNodeIDs.filter(function (n) {
100991 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
100992 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
100994 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
100996 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
100999 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
101001 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
101004 // If we're only straightenting between two points, we only need that extent visible
101005 _extent = utilTotalExtent(_nodeIDs, context.graph());
101009 return actionStraightenWay(selectedIDs, context.projection);
101017 context.perform(_action, operation.annotation());
101018 window.setTimeout(function () {
101019 context.validator().validate();
101020 }, 300); // after any transition
101023 operation.available = function () {
101024 return Boolean(_action);
101027 operation.disabled = function () {
101028 var reason = _action.disabled(context.graph());
101032 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
101034 } else if (someMissing()) {
101035 return 'not_downloaded';
101036 } else if (selectedIDs.some(context.hasHiddenConnections)) {
101037 return 'connected_to_hidden';
101042 function someMissing() {
101043 if (context.inIntro()) return false;
101044 var osm = context.connection();
101047 var missing = _coords.filter(function (loc) {
101048 return !osm.isDataLoaded(loc);
101052 missing.forEach(function (loc) {
101053 context.loadTileAtLoc(loc);
101063 operation.tooltip = function () {
101064 var disable = operation.disabled();
101065 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
101068 operation.annotation = function () {
101069 return _t('operations.straighten.annotation.' + _geometry, {
101070 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
101074 operation.id = 'straighten';
101075 operation.keys = [_t('operations.straighten.key')];
101076 operation.title = _t('operations.straighten.title');
101077 operation.behavior = behaviorOperation(context).which(operation);
101081 var Operations = /*#__PURE__*/Object.freeze({
101083 operationCircularize: operationCircularize,
101084 operationContinue: operationContinue,
101085 operationCopy: operationCopy,
101086 operationDelete: operationDelete,
101087 operationDisconnect: operationDisconnect,
101088 operationDowngrade: operationDowngrade,
101089 operationExtract: operationExtract,
101090 operationMerge: operationMerge,
101091 operationMove: operationMove,
101092 operationOrthogonalize: operationOrthogonalize,
101093 operationPaste: operationPaste,
101094 operationReflectShort: operationReflectShort,
101095 operationReflectLong: operationReflectLong,
101096 operationReverse: operationReverse,
101097 operationRotate: operationRotate,
101098 operationSplit: operationSplit,
101099 operationStraighten: operationStraighten
101102 function modeSelect(context, selectedIDs) {
101107 var keybinding = utilKeybinding('select');
101109 var _breatheBehavior = behaviorBreathe();
101111 var _modeDragNode = modeDragNode(context);
101117 var _newFeature = false;
101118 var _follow = false; // `_focusedParentWayId` is used when we visit a vertex with multiple
101119 // parents, and we want to remember which parent line we started on.
101121 var _focusedParentWayId;
101126 if (selectedIDs && selectedIDs.length === 1) {
101127 return context.hasEntity(selectedIDs[0]);
101131 function selectedEntities() {
101132 return selectedIDs.map(function (id) {
101133 return context.hasEntity(id);
101137 function checkSelectedIDs() {
101140 if (Array.isArray(selectedIDs)) {
101141 ids = selectedIDs.filter(function (id) {
101142 return context.hasEntity(id);
101147 context.enter(modeBrowse(context));
101149 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
101150 // switch between single- and multi-select UI
101151 context.enter(modeSelect(context, ids));
101157 } // find the parent ways for nextVertex, previousVertex, and selectParent
101160 function parentWaysIdsOfSelection(onlyCommonParents) {
101161 var graph = context.graph();
101164 for (var i = 0; i < selectedIDs.length; i++) {
101165 var entity = context.hasEntity(selectedIDs[i]);
101167 if (!entity || entity.geometry(graph) !== 'vertex') {
101168 return []; // selection includes some non-vertices
101171 var currParents = graph.parentWays(entity).map(function (w) {
101180 parents = (onlyCommonParents ? utilArrayIntersection : utilArrayUnion)(parents, currParents);
101188 } // find the child nodes for selected ways
101191 function childNodeIdsOfSelection(onlyCommon) {
101192 var graph = context.graph();
101195 for (var i = 0; i < selectedIDs.length; i++) {
101196 var entity = context.hasEntity(selectedIDs[i]);
101198 if (!entity || !['area', 'line'].includes(entity.geometry(graph))) {
101199 return []; // selection includes non-area/non-line
101202 var currChilds = graph.childNodes(entity).map(function (node) {
101211 childs = (onlyCommon ? utilArrayIntersection : utilArrayUnion)(childs, currChilds);
101221 function checkFocusedParent() {
101222 if (_focusedParentWayId) {
101223 var parents = parentWaysIdsOfSelection(true);
101224 if (parents.indexOf(_focusedParentWayId) === -1) _focusedParentWayId = null;
101228 function parentWayIdForVertexNavigation() {
101229 var parentIds = parentWaysIdsOfSelection(true);
101231 if (_focusedParentWayId && parentIds.indexOf(_focusedParentWayId) !== -1) {
101232 // prefer the previously seen parent
101233 return _focusedParentWayId;
101236 return parentIds.length ? parentIds[0] : null;
101239 mode.selectedIDs = function (val) {
101240 if (!arguments.length) return selectedIDs;
101245 mode.zoomToSelected = function () {
101246 context.map().zoomToEase(selectedEntities());
101249 mode.newFeature = function (val) {
101250 if (!arguments.length) return _newFeature;
101255 mode.selectBehavior = function (val) {
101256 if (!arguments.length) return _selectBehavior;
101261 mode.follow = function (val) {
101262 if (!arguments.length) return _follow;
101267 function loadOperations() {
101268 _operations.forEach(function (operation) {
101269 if (operation.behavior) {
101270 context.uninstall(operation.behavior);
101274 _operations = Object.values(Operations).map(function (o) {
101275 return o(context, selectedIDs);
101276 }).filter(function (o) {
101277 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
101278 }).concat([// group copy/downgrade/delete operation together at the end of the list
101279 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
101280 return operation.available();
101283 _operations.forEach(function (operation) {
101284 if (operation.behavior) {
101285 context.install(operation.behavior);
101287 }); // remove any displayed menu
101290 context.ui().closeEditMenu();
101293 mode.operations = function () {
101297 mode.enter = function () {
101298 if (!checkSelectedIDs()) return;
101299 context.features().forceVisible(selectedIDs);
101301 _modeDragNode.restoreSelectedIDs(selectedIDs);
101305 if (!_behaviors.length) {
101306 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
101307 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
101310 _behaviors.forEach(context.install);
101312 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(utilKeybinding.plusKeys.map(function (key) {
101313 return uiCmd('⇧' + key);
101314 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
101315 return uiCmd('⇧⌥' + key);
101316 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
101317 return uiCmd('⇧' + key);
101318 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
101319 return uiCmd('⇧⌥' + key);
101320 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], focusNextParent).on(uiCmd('⌘↑'), selectParent).on(uiCmd('⌘↓'), selectChild).on('⎋', esc, true);
101321 select(document).call(keybinding);
101322 context.ui().sidebar.select(selectedIDs, _newFeature);
101323 context.history().on('change.select', function () {
101324 loadOperations(); // reselect after change in case relation members were removed or added
101327 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
101328 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
101331 _breatheBehavior.restartIfNeeded(context.surface());
101333 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
101337 var extent = geoExtent();
101338 var graph = context.graph();
101339 selectedIDs.forEach(function (id) {
101340 var entity = context.entity(id);
101342 extent._extend(entity.extent(graph));
101344 var loc = extent.center();
101345 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
101350 function nudgeSelection(delta) {
101352 // prevent nudging during low zoom selection
101353 if (!context.map().withinEditableZoom()) return;
101354 var moveOp = operationMove(context, selectedIDs);
101356 if (moveOp.disabled()) {
101357 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
101359 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
101360 context.validator().validate();
101365 function scaleSelection(factor) {
101367 // prevent scaling during low zoom selection
101368 if (!context.map().withinEditableZoom()) return;
101369 var nodes = utilGetAllNodes(selectedIDs, context.graph());
101370 var isUp = factor > 1; // can only scale if multiple nodes are selected
101372 if (nodes.length <= 1) return;
101373 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
101374 // object, but we don't want an actual scale operation at this point.
101376 function scalingDisabled() {
101379 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
101381 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
101382 return 'not_downloaded';
101383 } else if (selectedIDs.some(context.hasHiddenConnections)) {
101384 return 'connected_to_hidden';
101390 if (isUp) return false;
101391 var dLon = Math.abs(extent[1][0] - extent[0][0]);
101392 var dLat = Math.abs(extent[1][1] - extent[0][1]);
101393 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
101396 function someMissing() {
101397 if (context.inIntro()) return false;
101398 var osm = context.connection();
101401 var missing = nodes.filter(function (n) {
101402 return !osm.isDataLoaded(n.loc);
101406 missing.forEach(function (loc) {
101407 context.loadTileAtLoc(loc);
101416 function incompleteRelation(id) {
101417 var entity = context.entity(id);
101418 return entity.type === 'relation' && !entity.isComplete(context.graph());
101422 var disabled = scalingDisabled();
101425 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
101426 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
101428 var pivot = context.projection(extent.center());
101429 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
101432 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
101433 context.validator().validate();
101438 function didDoubleUp(d3_event, loc) {
101439 if (!context.map().withinEditableZoom()) return;
101440 var target = select(d3_event.target);
101441 var datum = target.datum();
101442 var entity = datum && datum.properties && datum.properties.entity;
101445 if (entity instanceof osmWay && target.classed('target')) {
101446 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
101447 var prev = entity.nodes[choice.index - 1];
101448 var next = entity.nodes[choice.index];
101449 context.perform(actionAddMidpoint({
101452 }, osmNode()), _t('operations.add.annotation.vertex'));
101453 } else if (entity.type === 'midpoint') {
101454 context.perform(actionAddMidpoint({
101457 }, osmNode()), _t('operations.add.annotation.vertex'));
101461 function selectElements() {
101462 if (!checkSelectedIDs()) return;
101463 var surface = context.surface();
101464 surface.selectAll('.selected-member').classed('selected-member', false);
101465 surface.selectAll('.selected').classed('selected', false);
101466 surface.selectAll('.related').classed('related', false); // reload `_focusedParentWayId` based on the current selection
101470 if (_focusedParentWayId) {
101471 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
101474 if (context.map().withinEditableZoom()) {
101475 // Apply selection styling if not in wide selection
101476 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
101477 /* skipMultipolgonMembers */
101478 )).classed('selected-member', true);
101479 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
101484 if (context.container().select('.combobox').size()) return;
101485 context.enter(modeBrowse(context));
101488 function firstVertex(d3_event) {
101489 d3_event.preventDefault();
101490 var entity = singular();
101491 var parentId = parentWayIdForVertexNavigation();
101494 if (entity && entity.type === 'way') {
101497 way = context.entity(parentId);
101500 _focusedParentWayId = way && way.id;
101503 context.enter(mode.selectedIDs([way.first()]).follow(true));
101507 function lastVertex(d3_event) {
101508 d3_event.preventDefault();
101509 var entity = singular();
101510 var parentId = parentWayIdForVertexNavigation();
101513 if (entity && entity.type === 'way') {
101516 way = context.entity(parentId);
101519 _focusedParentWayId = way && way.id;
101522 context.enter(mode.selectedIDs([way.last()]).follow(true));
101526 function previousVertex(d3_event) {
101527 d3_event.preventDefault();
101528 var parentId = parentWayIdForVertexNavigation();
101529 _focusedParentWayId = parentId;
101531 var way = context.entity(parentId);
101532 var length = way.nodes.length;
101533 var curr = way.nodes.indexOf(selectedIDs[0]);
101538 } else if (way.isClosed()) {
101543 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
101547 function nextVertex(d3_event) {
101548 d3_event.preventDefault();
101549 var parentId = parentWayIdForVertexNavigation();
101550 _focusedParentWayId = parentId;
101552 var way = context.entity(parentId);
101553 var length = way.nodes.length;
101554 var curr = way.nodes.indexOf(selectedIDs[0]);
101557 if (curr < length - 1) {
101559 } else if (way.isClosed()) {
101564 context.enter(mode.selectedIDs([way.nodes[index]]).follow(true));
101568 function focusNextParent(d3_event) {
101569 d3_event.preventDefault();
101570 var parents = parentWaysIdsOfSelection(true);
101571 if (!parents || parents.length < 2) return;
101572 var index = parents.indexOf(_focusedParentWayId);
101574 if (index < 0 || index > parents.length - 2) {
101575 _focusedParentWayId = parents[0];
101577 _focusedParentWayId = parents[index + 1];
101580 var surface = context.surface();
101581 surface.selectAll('.related').classed('related', false);
101583 if (_focusedParentWayId) {
101584 surface.selectAll(utilEntitySelector([_focusedParentWayId])).classed('related', true);
101588 function selectParent(d3_event) {
101589 d3_event.preventDefault();
101590 var currentSelectedIds = mode.selectedIDs();
101591 var parentIds = _focusedParentWayId ? [_focusedParentWayId] : parentWaysIdsOfSelection(false);
101592 if (!parentIds.length) return;
101593 context.enter(mode.selectedIDs(parentIds)); // set this after re-entering the selection since we normally want it cleared on exit
101595 _focusedVertexIds = currentSelectedIds;
101598 function selectChild(d3_event) {
101599 d3_event.preventDefault();
101600 var currentSelectedIds = mode.selectedIDs();
101601 var childIds = _focusedVertexIds ? _focusedVertexIds.filter(function (id) {
101602 return context.hasEntity(id);
101603 }) : childNodeIdsOfSelection(true);
101604 if (!childIds || !childIds.length) return;
101605 if (currentSelectedIds.length === 1) _focusedParentWayId = currentSelectedIds[0];
101606 context.enter(mode.selectedIDs(childIds));
101610 mode.exit = function () {
101611 // we could enter the mode multiple times but it's only new the first time
101613 _focusedVertexIds = null;
101615 _operations.forEach(function (operation) {
101616 if (operation.behavior) {
101617 context.uninstall(operation.behavior);
101623 _behaviors.forEach(context.uninstall);
101625 select(document).call(keybinding.unbind);
101626 context.ui().closeEditMenu();
101627 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
101628 var surface = context.surface();
101629 surface.selectAll('.selected-member').classed('selected-member', false);
101630 surface.selectAll('.selected').classed('selected', false);
101631 surface.selectAll('.highlighted').classed('highlighted', false);
101632 surface.selectAll('.related').classed('related', false);
101633 context.map().on('drawn.select', null);
101634 context.ui().sidebar.hide();
101635 context.features().forceVisible([]);
101636 var entity = singular();
101638 if (_newFeature && entity && entity.type === 'relation' && // no tags
101639 Object.keys(entity.tags).length === 0 && // no parent relations
101640 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
101641 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
101642 // the user added this relation but didn't edit it at all, so just delete it
101643 var deleteAction = actionDeleteRelation(entity.id, true
101644 /* don't delete untagged members */
101646 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
101653 function uiLasso(context) {
101655 lasso.coordinates = [];
101657 function lasso(selection) {
101658 context.container().classed('lasso', true);
101659 group = selection.append('g').attr('class', 'lasso hide');
101660 polygon = group.append('path').attr('class', 'lasso-path');
101661 group.call(uiToggle(true));
101666 polygon.data([lasso.coordinates]).attr('d', function (d) {
101667 return 'M' + d.join(' L') + ' Z';
101672 lasso.extent = function () {
101673 return lasso.coordinates.reduce(function (extent, point) {
101674 return extent.extend(geoExtent(point));
101678 lasso.p = function (_) {
101679 if (!arguments.length) return lasso;
101680 lasso.coordinates.push(_);
101685 lasso.close = function () {
101687 group.call(uiToggle(false, function () {
101692 context.container().classed('lasso', false);
101698 function behaviorLasso(context) {
101699 // use pointer events on supported platforms; fallback to mouse events
101700 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
101702 var behavior = function behavior(selection) {
101705 function pointerdown(d3_event) {
101706 var button = 0; // left
101708 if (d3_event.button === button && d3_event.shiftKey === true) {
101710 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
101711 d3_event.stopPropagation();
101715 function pointermove() {
101717 lasso = uiLasso(context);
101718 context.surface().call(lasso);
101721 lasso.p(context.map().mouse());
101724 function normalize(a, b) {
101725 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])]];
101730 var graph = context.graph();
101733 if (context.map().editableDataEnabled(true
101735 ) && context.map().isInWideSelection()) {
101736 // only select from the visible nodes
101737 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
101738 } else if (!context.map().editableDataEnabled()) {
101742 var bounds = lasso.extent().map(context.projection.invert);
101743 var extent = geoExtent(normalize(bounds[0], bounds[1]));
101744 var intersects = context.history().intersects(extent).filter(function (entity) {
101745 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
101746 }); // sort the lassoed nodes as best we can
101748 intersects.sort(function (node1, node2) {
101749 var parents1 = graph.parentWays(node1);
101750 var parents2 = graph.parentWays(node2);
101752 if (parents1.length && parents2.length) {
101753 // both nodes are vertices
101754 var sharedParents = utilArrayIntersection(parents1, parents2);
101756 if (sharedParents.length) {
101757 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
101759 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
101761 // vertices do not share a way; group them by their respective parent ways
101762 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
101764 } else if (parents1.length || parents2.length) {
101765 // only one node is a vertex; sort standalone points before vertices
101766 return parents1.length - parents2.length;
101767 } // both nodes are standalone points; sort left to right
101770 return node1.loc[0] - node2.loc[0];
101772 return intersects.map(function (entity) {
101778 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
101784 context.enter(modeSelect(context, ids));
101788 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
101791 behavior.off = function (selection) {
101792 selection.on(_pointerPrefix + 'down.lasso', null);
101798 function modeBrowse(context) {
101802 title: _t('modes.browse.title'),
101803 description: _t('modes.browse.description')
101811 mode.selectBehavior = function (val) {
101812 if (!arguments.length) return _selectBehavior;
101817 mode.enter = function () {
101818 if (!_behaviors.length) {
101819 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
101820 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
101823 _behaviors.forEach(context.install); // Get focus on the body.
101826 if (document.activeElement && document.activeElement.blur) {
101827 document.activeElement.blur();
101831 context.ui().sidebar.show(sidebar);
101833 context.ui().sidebar.select(null);
101837 mode.exit = function () {
101838 context.ui().sidebar.hover.cancel();
101840 _behaviors.forEach(context.uninstall);
101843 context.ui().sidebar.hide();
101847 mode.sidebar = function (_) {
101848 if (!arguments.length) return sidebar;
101853 mode.operations = function () {
101854 return [operationPaste(context)];
101860 function behaviorAddWay(context) {
101861 var dispatch = dispatch$8('start', 'startFromWay', 'startFromNode');
101862 var draw = behaviorDraw(context);
101864 function behavior(surface) {
101865 draw.on('click', function () {
101866 dispatch.apply('start', this, arguments);
101867 }).on('clickWay', function () {
101868 dispatch.apply('startFromWay', this, arguments);
101869 }).on('clickNode', function () {
101870 dispatch.apply('startFromNode', this, arguments);
101871 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
101872 context.map().dblclickZoomEnable(false);
101876 behavior.off = function (surface) {
101877 surface.call(draw.off);
101880 behavior.cancel = function () {
101881 window.setTimeout(function () {
101882 context.map().dblclickZoomEnable(true);
101884 context.enter(modeBrowse(context));
101887 return utilRebind(behavior, dispatch, 'on');
101890 function behaviorHash(context) {
101891 // cached window.location.hash
101892 var _cachedHash = null; // allowable latitude range
101894 var _latitudeLimit = 90 - 1e-8;
101896 function computedHashParameters() {
101897 var map = context.map();
101898 var center = map.center();
101900 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
101901 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
101904 var selected = context.selectedIDs().filter(function (id) {
101905 return context.hasEntity(id);
101909 newParams.id = selected.join(',');
101912 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
101913 return Object.assign(oldParams, newParams);
101916 function computedHash() {
101917 return '#' + utilQsString(computedHashParameters(), true);
101920 function computedTitle(includeChangeCount) {
101921 var baseTitle = context.documentTitleBase() || 'iD';
101925 var selected = context.selectedIDs().filter(function (id) {
101926 return context.hasEntity(id);
101930 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
101932 if (selected.length > 1) {
101933 contextual = _t('title.labeled_and_more', {
101935 count: selected.length - 1
101938 contextual = firstLabel;
101944 if (includeChangeCount) {
101945 changeCount = context.history().difference().summary().length;
101948 titleID = contextual ? 'changes_context' : 'changes';
101953 return _t('title.format.' + titleID, {
101963 function updateTitle(includeChangeCount) {
101964 if (!context.setsDocumentTitle()) return;
101965 var newTitle = computedTitle(includeChangeCount);
101967 if (document.title !== newTitle) {
101968 document.title = newTitle;
101972 function updateHashIfNeeded() {
101973 if (context.inIntro()) return;
101974 var latestHash = computedHash();
101976 if (_cachedHash !== latestHash) {
101977 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
101978 // though unavoidably creating a browser history entry
101980 window.history.replaceState(null, computedTitle(false
101981 /* includeChangeCount */
101982 ), latestHash); // set the title we want displayed for the browser tab/window
101985 /* includeChangeCount */
101990 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
101992 var _throttledUpdateTitle = throttle(function () {
101994 /* includeChangeCount */
101998 function hashchange() {
101999 // ignore spurious hashchange events
102000 if (window.location.hash === _cachedHash) return;
102001 _cachedHash = window.location.hash;
102002 var q = utilStringQs(_cachedHash);
102003 var mapArgs = (q.map || '').split('/').map(Number);
102005 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
102009 // don't update if the new hash already reflects the state of iD
102010 if (_cachedHash === computedHash()) return;
102011 var mode = context.mode();
102012 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
102015 var ids = q.id.split(',').filter(function (id) {
102016 return context.hasEntity(id);
102019 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
102020 context.enter(modeSelect(context, ids));
102025 var center = context.map().center();
102026 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
102027 var maxdist = 500; // Don't allow the hash location to change too much while drawing
102028 // This can happen if the user accidentally hit the back button. #3996
102030 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
102031 context.enter(modeBrowse(context));
102038 context.map().on('move.behaviorHash', _throttledUpdate);
102039 context.history().on('change.behaviorHash', _throttledUpdateTitle);
102040 context.on('enter.behaviorHash', _throttledUpdate);
102041 select(window).on('hashchange.behaviorHash', hashchange);
102043 if (window.location.hash) {
102044 var q = utilStringQs(window.location.hash);
102047 //if (!context.history().hasRestorableChanges()) {
102048 // targeting specific features: download, select, and zoom to them
102049 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
102052 if (q.walkthrough === 'true') {
102053 behavior.startWalkthrough = true;
102057 behavior.hadHash = true;
102065 behavior.off = function () {
102066 _throttledUpdate.cancel();
102068 _throttledUpdateTitle.cancel();
102070 context.map().on('move.behaviorHash', null);
102071 context.on('enter.behaviorHash', null);
102072 select(window).on('hashchange.behaviorHash', null);
102073 window.location.hash = '';
102079 // This is only done in testing because of the performance penalty.
102081 var debug = false; // Reexport just what our tests use, see #4379
102085 geoProjection: projection,
102086 polygonArea: d3_polygonArea,
102087 polygonCentroid: d3_polygonCentroid,
102093 var iD = /*#__PURE__*/Object.freeze({
102097 actionAddEntity: actionAddEntity,
102098 actionAddMember: actionAddMember,
102099 actionAddMidpoint: actionAddMidpoint,
102100 actionAddVertex: actionAddVertex,
102101 actionChangeMember: actionChangeMember,
102102 actionChangePreset: actionChangePreset,
102103 actionChangeTags: actionChangeTags,
102104 actionCircularize: actionCircularize,
102105 actionConnect: actionConnect,
102106 actionCopyEntities: actionCopyEntities,
102107 actionDeleteMember: actionDeleteMember,
102108 actionDeleteMultiple: actionDeleteMultiple,
102109 actionDeleteNode: actionDeleteNode,
102110 actionDeleteRelation: actionDeleteRelation,
102111 actionDeleteWay: actionDeleteWay,
102112 actionDiscardTags: actionDiscardTags,
102113 actionDisconnect: actionDisconnect,
102114 actionExtract: actionExtract,
102115 actionJoin: actionJoin,
102116 actionMerge: actionMerge,
102117 actionMergeNodes: actionMergeNodes,
102118 actionMergePolygon: actionMergePolygon,
102119 actionMergeRemoteChanges: actionMergeRemoteChanges,
102120 actionMove: actionMove,
102121 actionMoveMember: actionMoveMember,
102122 actionMoveNode: actionMoveNode,
102123 actionNoop: actionNoop,
102124 actionOrthogonalize: actionOrthogonalize,
102125 actionRestrictTurn: actionRestrictTurn,
102126 actionReverse: actionReverse,
102127 actionRevert: actionRevert,
102128 actionRotate: actionRotate,
102129 actionScale: actionScale,
102130 actionSplit: actionSplit,
102131 actionStraightenNodes: actionStraightenNodes,
102132 actionStraightenWay: actionStraightenWay,
102133 actionUnrestrictTurn: actionUnrestrictTurn,
102134 actionReflect: actionReflect,
102135 actionUpgradeTags: actionUpgradeTags,
102136 behaviorAddWay: behaviorAddWay,
102137 behaviorBreathe: behaviorBreathe,
102138 behaviorDrag: behaviorDrag,
102139 behaviorDrawWay: behaviorDrawWay,
102140 behaviorDraw: behaviorDraw,
102141 behaviorEdit: behaviorEdit,
102142 behaviorHash: behaviorHash,
102143 behaviorHover: behaviorHover,
102144 behaviorLasso: behaviorLasso,
102145 behaviorOperation: behaviorOperation,
102146 behaviorPaste: behaviorPaste,
102147 behaviorSelect: behaviorSelect,
102148 coreContext: coreContext,
102149 coreFileFetcher: coreFileFetcher,
102150 fileFetcher: _mainFileFetcher,
102151 coreDifference: coreDifference,
102153 coreHistory: coreHistory,
102154 coreLocalizer: coreLocalizer,
102156 localizer: _mainLocalizer,
102157 coreLocations: coreLocations,
102158 locationManager: _mainLocations,
102159 prefs: corePreferences,
102161 coreUploader: coreUploader,
102162 coreValidator: coreValidator,
102164 geoLatToMeters: geoLatToMeters,
102165 geoLonToMeters: geoLonToMeters,
102166 geoMetersToLat: geoMetersToLat,
102167 geoMetersToLon: geoMetersToLon,
102168 geoMetersToOffset: geoMetersToOffset,
102169 geoOffsetToMeters: geoOffsetToMeters,
102170 geoScaleToZoom: geoScaleToZoom,
102171 geoSphericalClosestNode: geoSphericalClosestNode,
102172 geoSphericalDistance: geoSphericalDistance,
102173 geoZoomToScale: geoZoomToScale,
102175 geoChooseEdge: geoChooseEdge,
102176 geoEdgeEqual: geoEdgeEqual,
102177 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
102178 geoHasLineIntersections: geoHasLineIntersections,
102179 geoHasSelfIntersections: geoHasSelfIntersections,
102181 geoLineIntersection: geoLineIntersection,
102182 geoPathHasIntersections: geoPathHasIntersections,
102183 geoPathIntersections: geoPathIntersections,
102184 geoPathLength: geoPathLength,
102185 geoPointInPolygon: geoPointInPolygon,
102186 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
102187 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
102188 geoViewportEdge: geoViewportEdge,
102189 geoRawMercator: geoRawMercator,
102191 geoVecAngle: geoVecAngle,
102192 geoVecCross: geoVecCross,
102194 geoVecEqual: geoVecEqual,
102195 geoVecFloor: geoVecFloor,
102196 geoVecInterp: geoVecInterp,
102197 geoVecLength: geoVecLength,
102198 geoVecLengthSquare: geoVecLengthSquare,
102199 geoVecNormalize: geoVecNormalize,
102200 geoVecNormalizedDot: geoVecNormalizedDot,
102201 geoVecProject: geoVecProject,
102202 geoVecSubtract: geoVecSubtract,
102203 geoVecScale: geoVecScale,
102204 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
102205 geoOrthoCalcScore: geoOrthoCalcScore,
102206 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
102207 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
102208 modeAddArea: modeAddArea,
102209 modeAddLine: modeAddLine,
102210 modeAddPoint: modeAddPoint,
102211 modeAddNote: modeAddNote,
102212 modeBrowse: modeBrowse,
102213 modeDragNode: modeDragNode,
102214 modeDragNote: modeDragNote,
102215 modeDrawArea: modeDrawArea,
102216 modeDrawLine: modeDrawLine,
102218 modeRotate: modeRotate,
102220 modeSelect: modeSelect,
102221 modeSelectData: modeSelectData,
102222 modeSelectError: modeSelectError,
102223 modeSelectNote: modeSelectNote,
102224 operationCircularize: operationCircularize,
102225 operationContinue: operationContinue,
102226 operationCopy: operationCopy,
102227 operationDelete: operationDelete,
102228 operationDisconnect: operationDisconnect,
102229 operationDowngrade: operationDowngrade,
102230 operationExtract: operationExtract,
102231 operationMerge: operationMerge,
102232 operationMove: operationMove,
102233 operationOrthogonalize: operationOrthogonalize,
102234 operationPaste: operationPaste,
102235 operationReflectShort: operationReflectShort,
102236 operationReflectLong: operationReflectLong,
102237 operationReverse: operationReverse,
102238 operationRotate: operationRotate,
102239 operationSplit: operationSplit,
102240 operationStraighten: operationStraighten,
102241 osmChangeset: osmChangeset,
102245 osmRelation: osmRelation,
102248 osmIntersection: osmIntersection,
102250 osmInferRestriction: osmInferRestriction,
102252 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
102253 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
102254 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
102255 osmJoinWays: osmJoinWays,
102256 get osmAreaKeys () { return osmAreaKeys; },
102257 osmSetAreaKeys: osmSetAreaKeys,
102258 osmTagSuggestingArea: osmTagSuggestingArea,
102259 get osmPointTags () { return osmPointTags; },
102260 osmSetPointTags: osmSetPointTags,
102261 get osmVertexTags () { return osmVertexTags; },
102262 osmSetVertexTags: osmSetVertexTags,
102263 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
102264 osmOneWayTags: osmOneWayTags,
102265 osmPavedTags: osmPavedTags,
102266 osmIsInterestingTag: osmIsInterestingTag,
102267 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
102268 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
102269 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
102270 presetCategory: presetCategory,
102271 presetCollection: presetCollection,
102272 presetField: presetField,
102273 presetPreset: presetPreset,
102274 presetManager: _mainPresetIndex,
102275 presetIndex: presetIndex,
102276 rendererBackgroundSource: rendererBackgroundSource,
102277 rendererBackground: rendererBackground,
102278 rendererFeatures: rendererFeatures,
102279 rendererMap: rendererMap,
102280 rendererPhotos: rendererPhotos,
102281 rendererTileLayer: rendererTileLayer,
102283 serviceKeepRight: serviceKeepRight,
102284 serviceImproveOSM: serviceImproveOSM,
102285 serviceOsmose: serviceOsmose,
102286 serviceMapillary: serviceMapillary,
102287 serviceMapRules: serviceMapRules,
102288 serviceNominatim: serviceNominatim,
102289 serviceNsi: serviceNsi,
102290 serviceOpenstreetcam: serviceOpenstreetcam,
102291 serviceOsm: serviceOsm,
102292 serviceOsmWikibase: serviceOsmWikibase,
102293 serviceStreetside: serviceStreetside,
102294 serviceTaginfo: serviceTaginfo,
102295 serviceVectorTile: serviceVectorTile,
102296 serviceWikidata: serviceWikidata,
102297 serviceWikipedia: serviceWikipedia,
102302 svgKeepRight: svgKeepRight,
102304 svgGeolocate: svgGeolocate,
102308 svgMapillaryImages: svgMapillaryImages,
102309 svgMapillarySigns: svgMapillarySigns,
102310 svgMidpoints: svgMidpoints,
102312 svgMarkerSegments: svgMarkerSegments,
102313 svgOpenstreetcamImages: svgOpenstreetcamImages,
102315 svgPassiveVertex: svgPassiveVertex,
102317 svgPointTransform: svgPointTransform,
102319 svgRelationMemberTags: svgRelationMemberTags,
102320 svgSegmentWay: svgSegmentWay,
102321 svgStreetside: svgStreetside,
102322 svgTagClasses: svgTagClasses,
102323 svgTagPattern: svgTagPattern,
102326 svgVertices: svgVertices,
102327 uiFieldDefaultCheck: uiFieldCheck,
102328 uiFieldOnewayCheck: uiFieldCheck,
102329 uiFieldCheck: uiFieldCheck,
102330 uiFieldManyCombo: uiFieldCombo,
102331 uiFieldMultiCombo: uiFieldCombo,
102332 uiFieldNetworkCombo: uiFieldCombo,
102333 uiFieldSemiCombo: uiFieldCombo,
102334 uiFieldTypeCombo: uiFieldCombo,
102335 uiFieldCombo: uiFieldCombo,
102336 uiFieldUrl: uiFieldText,
102337 uiFieldIdentifier: uiFieldText,
102338 uiFieldNumber: uiFieldText,
102339 uiFieldTel: uiFieldText,
102340 uiFieldEmail: uiFieldText,
102341 uiFieldText: uiFieldText,
102342 uiFieldAccess: uiFieldAccess,
102343 uiFieldAddress: uiFieldAddress,
102344 uiFieldCycleway: uiFieldCycleway,
102345 uiFieldLanes: uiFieldLanes,
102346 uiFieldLocalized: uiFieldLocalized,
102347 uiFieldRoadspeed: uiFieldRoadspeed,
102348 uiFieldStructureRadio: uiFieldRadio,
102349 uiFieldRadio: uiFieldRadio,
102350 uiFieldRestrictions: uiFieldRestrictions,
102351 uiFieldTextarea: uiFieldTextarea,
102352 uiFieldWikidata: uiFieldWikidata,
102353 uiFieldWikipedia: uiFieldWikipedia,
102356 uiPanelBackground: uiPanelBackground,
102357 uiPanelHistory: uiPanelHistory,
102358 uiPanelLocation: uiPanelLocation,
102359 uiPanelMeasurement: uiPanelMeasurement,
102360 uiInfoPanels: uiInfoPanels,
102361 uiPaneBackground: uiPaneBackground,
102362 uiPaneHelp: uiPaneHelp,
102363 uiPaneIssues: uiPaneIssues,
102364 uiPaneMapData: uiPaneMapData,
102365 uiPanePreferences: uiPanePreferences,
102366 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
102367 uiSectionBackgroundList: uiSectionBackgroundList,
102368 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
102369 uiSectionChanges: uiSectionChanges,
102370 uiSectionDataLayers: uiSectionDataLayers,
102371 uiSectionEntityIssues: uiSectionEntityIssues,
102372 uiSectionFeatureType: uiSectionFeatureType,
102373 uiSectionMapFeatures: uiSectionMapFeatures,
102374 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
102375 uiSectionOverlayList: uiSectionOverlayList,
102376 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
102377 uiSectionPresetFields: uiSectionPresetFields,
102378 uiSectionPrivacy: uiSectionPrivacy,
102379 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
102380 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
102381 uiSectionRawTagEditor: uiSectionRawTagEditor,
102382 uiSectionSelectionList: uiSectionSelectionList,
102383 uiSectionValidationIssues: uiSectionValidationIssues,
102384 uiSectionValidationOptions: uiSectionValidationOptions,
102385 uiSectionValidationRules: uiSectionValidationRules,
102386 uiSectionValidationStatus: uiSectionValidationStatus,
102387 uiSettingsCustomBackground: uiSettingsCustomBackground,
102388 uiSettingsCustomData: uiSettingsCustomData,
102391 uiAttribution: uiAttribution,
102392 uiChangesetEditor: uiChangesetEditor,
102394 uiCombobox: uiCombobox,
102396 uiCommitWarnings: uiCommitWarnings,
102398 uiConflicts: uiConflicts,
102399 uiContributors: uiContributors,
102401 uiDataEditor: uiDataEditor,
102402 uiDataHeader: uiDataHeader,
102403 uiDisclosure: uiDisclosure,
102404 uiEditMenu: uiEditMenu,
102405 uiEntityEditor: uiEntityEditor,
102406 uiFeatureInfo: uiFeatureInfo,
102407 uiFeatureList: uiFeatureList,
102409 uiFieldHelp: uiFieldHelp,
102411 uiFormFields: uiFormFields,
102412 uiFullScreen: uiFullScreen,
102413 uiGeolocate: uiGeolocate,
102414 uiImproveOsmComments: uiImproveOsmComments,
102415 uiImproveOsmDetails: uiImproveOsmDetails,
102416 uiImproveOsmEditor: uiImproveOsmEditor,
102417 uiImproveOsmHeader: uiImproveOsmHeader,
102419 uiInspector: uiInspector,
102420 uiIssuesInfo: uiIssuesInfo,
102421 uiKeepRightDetails: uiKeepRightDetails,
102422 uiKeepRightEditor: uiKeepRightEditor,
102423 uiKeepRightHeader: uiKeepRightHeader,
102426 uiMapInMap: uiMapInMap,
102429 uiNoteComments: uiNoteComments,
102430 uiNoteEditor: uiNoteEditor,
102431 uiNoteHeader: uiNoteHeader,
102432 uiNoteReport: uiNoteReport,
102434 uiPresetIcon: uiPresetIcon,
102435 uiPresetList: uiPresetList,
102439 uiSourceSwitch: uiSourceSwitch,
102444 uiTagReference: uiTagReference,
102448 uiViewOnOSM: uiViewOnOSM,
102449 uiViewOnKeepRight: uiViewOnKeepRight,
102451 utilAesEncrypt: utilAesEncrypt,
102452 utilAesDecrypt: utilAesDecrypt,
102453 utilArrayChunk: utilArrayChunk,
102454 utilArrayDifference: utilArrayDifference,
102455 utilArrayFlatten: utilArrayFlatten,
102456 utilArrayGroupBy: utilArrayGroupBy,
102457 utilArrayIdentical: utilArrayIdentical,
102458 utilArrayIntersection: utilArrayIntersection,
102459 utilArrayUnion: utilArrayUnion,
102460 utilArrayUniq: utilArrayUniq,
102461 utilArrayUniqBy: utilArrayUniqBy,
102462 utilAsyncMap: utilAsyncMap,
102463 utilCleanTags: utilCleanTags,
102464 utilCombinedTags: utilCombinedTags,
102465 utilDeepMemberSelector: utilDeepMemberSelector,
102466 utilDetect: utilDetect,
102467 utilDisplayName: utilDisplayName,
102468 utilDisplayNameForPath: utilDisplayNameForPath,
102469 utilDisplayType: utilDisplayType,
102470 utilDisplayLabel: utilDisplayLabel,
102471 utilEntityRoot: utilEntityRoot,
102472 utilEditDistance: utilEditDistance,
102473 utilEntityAndDeepMemberIDs: utilEntityAndDeepMemberIDs,
102474 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
102475 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
102476 utilEntitySelector: utilEntitySelector,
102477 utilFastMouse: utilFastMouse,
102478 utilFetchJson: utilFetchJson,
102479 utilFunctor: utilFunctor,
102480 utilGetAllNodes: utilGetAllNodes,
102481 utilGetSetValue: utilGetSetValue,
102482 utilHashcode: utilHashcode,
102483 utilHighlightEntities: utilHighlightEntities,
102484 utilKeybinding: utilKeybinding,
102485 utilNoAuto: utilNoAuto,
102486 utilObjectOmit: utilObjectOmit,
102487 utilPrefixCSSProperty: utilPrefixCSSProperty,
102488 utilPrefixDOMProperty: utilPrefixDOMProperty,
102489 utilQsString: utilQsString,
102490 utilRebind: utilRebind,
102491 utilSafeClassName: utilSafeClassName,
102492 utilSetTransform: utilSetTransform,
102493 utilSessionMutex: utilSessionMutex,
102494 utilStringQs: utilStringQs,
102495 utilTagDiff: utilTagDiff,
102496 utilTagText: utilTagText,
102498 utilTotalExtent: utilTotalExtent,
102499 utilTriggerEvent: utilTriggerEvent,
102500 utilUnicodeCharsCount: utilUnicodeCharsCount,
102501 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
102502 utilUniqueDomId: utilUniqueDomId,
102504 validationAlmostJunction: validationAlmostJunction,
102505 validationCloseNodes: validationCloseNodes,
102506 validationCrossingWays: validationCrossingWays,
102507 validationDisconnectedWay: validationDisconnectedWay,
102508 validationFormatting: validationFormatting,
102509 validationHelpRequest: validationHelpRequest,
102510 validationImpossibleOneway: validationImpossibleOneway,
102511 validationIncompatibleSource: validationIncompatibleSource,
102512 validationMaprules: validationMaprules,
102513 validationMismatchedGeometry: validationMismatchedGeometry,
102514 validationMissingRole: validationMissingRole,
102515 validationMissingTag: validationMissingTag,
102516 validationOutdatedTags: validationOutdatedTags,
102517 validationPrivateData: validationPrivateData,
102518 validationSuspiciousName: validationSuspiciousName,
102519 validationUnsquareWay: validationUnsquareWay
102522 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
102523 var start = Date.now();
102524 return window.requestAnimationFrame(function () {
102527 timeRemaining: function timeRemaining() {
102528 return Math.max(0, 50 - (Date.now() - start));
102534 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
102535 window.cancelAnimationFrame(id);